diff --git a/Common/Context.h b/Common/Context.h
index 017f4f8f37661ad9f667ff33b3edb1b90b7074d0..1449d1c042d3690abdd2f78f25d1e848a7ab2676 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -105,6 +105,9 @@ class CTX {
   double zoomFactor;
   // draw background gradient?
   int bgGradient;
+  // draw background image?
+  std::string bgImageFileName;
+  double bgImagePosition[2];
   // fltk font size (and delta for palette windows)
   int fontSize, deltaFontSize;
   // font name, FLTK enum and size for opengl graphics
@@ -201,6 +204,7 @@ class CTX {
     int jpegQuality, jpegSmoothing, geoLabels, text, texAsEquation;
     int gifDither, gifSort, gifInterlace, gifTransparent;
     int posElementary, posElement, posGamma, posEta, posRho, posDisto;
+    int compositeWindows;
   } print;
   // color options
   struct{
diff --git a/Common/CreateFile.cpp b/Common/CreateFile.cpp
index 293cab2a6348696f6542fa92bd311cbd852ce79a..c4321ec5ead11f038957e75095903a5f96b96970 100644
--- a/Common/CreateFile.cpp
+++ b/Common/CreateFile.cpp
@@ -14,6 +14,8 @@
 
 #if defined(HAVE_FLTK)
 #include <FL/gl.h>
+#include "GUI.h"
+#include "graphicWindow.h"
 #include "gl2ps.h"
 #include "gl2gif.h"
 #include "gl2jpeg.h"
@@ -101,6 +103,48 @@ std::string GetDefaultFileName(int format)
   return name;
 }
 
+#if defined(HAVE_FLTK)
+static PixelBuffer *GetCompositePixelBuffer(GLenum format, GLenum type)
+{
+  PixelBuffer *buffer;
+  if(!CTX::instance()->print.compositeWindows){
+    GLint width = GUI::instance()->getCurrentOpenglWindow()->w();
+    GLint height = GUI::instance()->getCurrentOpenglWindow()->h();
+    buffer = new PixelBuffer(width, height, format, type);
+    buffer->fill(CTX::instance()->batch);
+  }
+  else{
+    graphicWindow *g = GUI::instance()->graph[0];
+    for(unsigned int i = 1; i < GUI::instance()->graph.size(); i++){
+      for(unsigned int j = 0; j < GUI::instance()->graph[i]->gl.size(); j++){
+        if(GUI::instance()->graph[i]->gl[j] == 
+           GUI::instance()->getCurrentOpenglWindow()){
+          g = GUI::instance()->graph[i];
+          break;
+        }
+      }
+    }
+    int ww = 0, hh = 0;
+    std::vector<PixelBuffer*> buffers;
+    for(unsigned int i = 0; i < g->gl.size(); i++){
+      openglWindow::setLastHandled(g->gl[i]);
+      buffer = new PixelBuffer(g->gl[i]->w(), g->gl[i]->h(), format, type);
+      buffer->fill(CTX::instance()->batch);
+      buffers.push_back(buffer);
+      ww = std::max(ww, g->gl[i]->x() + g->gl[i]->w());
+      hh = std::max(hh, g->gl[i]->y() + g->gl[i]->h());
+    }
+    buffer = new PixelBuffer(ww, hh, format, type);
+    for(unsigned int i = 0; i < g->gl.size(); i++){
+      buffer->copyPixels(g->gl[i]->x(), hh - g->gl[i]->h() - g->gl[i]->y(), 
+                         buffers[i]);
+      delete buffers[i];
+    }
+  }
+  return buffer;
+}
+#endif
+
 void CreateOutputFile(std::string fileName, int format)
 {
   if(fileName.empty())
@@ -113,15 +157,6 @@ void CreateOutputFile(std::string fileName, int format)
   CTX::instance()->print.format = format;
   CTX::instance()->printing = 1;
 
-#if defined(HAVE_FLTK)
-  int vp[4];
-  GetCurrentOpenglWindowViewport(vp);
-  GLint viewport[4];
-  for(int i = 0; i < 4; i++) viewport[i] = vp[i];
-  GLint width = viewport[2] - viewport[0];
-  GLint height = viewport[3] - viewport[1];
-#endif
-
   bool printEndMessage = true;
   if(format != FORMAT_AUTO) 
     Msg::StatusBar(2, true, "Writing '%s'", fileName.c_str());
@@ -221,28 +256,28 @@ void CreateOutputFile(std::string fileName, int format)
   case FORMAT_JPEG:
   case FORMAT_PNG:
     {
+      if(!GUI::available()) break;
+
       FILE *fp = fopen(fileName.c_str(), "wb");
       if(!fp){
         Msg::Error("Unable to open file '%s'", fileName.c_str());
         break;
       }
 
-      PixelBuffer buffer(width, height, GL_RGB, GL_UNSIGNED_BYTE);
-
       int old_gradient = CTX::instance()->bgGradient;
       if(format == FORMAT_GIF && CTX::instance()->print.gifTransparent)
         CTX::instance()->bgGradient = 0;
-      buffer.Fill(CTX::instance()->batch);
+
+      PixelBuffer *buffer = GetCompositePixelBuffer(GL_RGB, GL_UNSIGNED_BYTE);
+
       CTX::instance()->bgGradient = old_gradient;
 
-      if(format == FORMAT_PPM){
-        create_ppm(fp, &buffer);
-      }
-      else if(format == FORMAT_YUV){
-        create_yuv(fp, &buffer);
-      }
-      else if(format == FORMAT_GIF){
-        create_gif(fp, &buffer,
+      if(format == FORMAT_PPM)
+        create_ppm(fp, buffer);
+      else if(format == FORMAT_YUV)
+        create_yuv(fp, buffer);
+      else if(format == FORMAT_GIF)
+        create_gif(fp, buffer, 
                    CTX::instance()->print.gifDither,
                    CTX::instance()->print.gifSort,
                    CTX::instance()->print.gifInterlace,
@@ -250,14 +285,13 @@ void CreateOutputFile(std::string fileName, int format)
                    CTX::instance()->unpackRed(CTX::instance()->color.bg),
                    CTX::instance()->unpackGreen(CTX::instance()->color.bg), 
                    CTX::instance()->unpackBlue(CTX::instance()->color.bg));
-      }
-      else if(format == FORMAT_JPEG){
-        create_jpeg(fp, &buffer, CTX::instance()->print.jpegQuality, 
+      else if(format == FORMAT_JPEG)
+        create_jpeg(fp, buffer, CTX::instance()->print.jpegQuality, 
                     CTX::instance()->print.jpegSmoothing);
-      }
-      else{
-        create_png(fp, &buffer, 100);
-      }
+      else
+        create_png(fp, buffer, 100);
+
+      delete buffer;
       fclose(fp);
     }
     break;
@@ -267,27 +301,17 @@ void CreateOutputFile(std::string fileName, int format)
   case FORMAT_PDF:
   case FORMAT_SVG:
     {
+      if(!GUI::available()) break;
+
       FILE *fp = fopen(fileName.c_str(), "wb");
       if(!fp){
         Msg::Error("Unable to open file '%s'", fileName.c_str());
         break;
       }
-      
-      int psformat;
-      switch(format){
-      case FORMAT_PDF:
-        psformat = GL2PS_PDF;
-        break;
-      case FORMAT_PS:
-        psformat = GL2PS_PS;
-        break;
-      case FORMAT_SVG:
-        psformat = GL2PS_SVG;
-        break;
-      default:
-        psformat = GL2PS_EPS;
-        break;
-      }
+
+      GLint width = GUI::instance()->getCurrentOpenglWindow()->w();
+      GLint height = GUI::instance()->getCurrentOpenglWindow()->h();
+      GLint viewport[4] = {0, 0, width, height};
 
       int old_gradient = CTX::instance()->bgGradient;
       if(!CTX::instance()->print.epsBackground) CTX::instance()->bgGradient = 0;
@@ -295,8 +319,13 @@ void CreateOutputFile(std::string fileName, int format)
       PixelBuffer buffer(width, height, GL_RGB, GL_FLOAT);
       
       if(CTX::instance()->print.epsQuality == 0)
-        buffer.Fill(CTX::instance()->batch);
+        buffer.fill(CTX::instance()->batch);
       
+      int psformat = 
+        (format == FORMAT_PDF) ? GL2PS_PDF :
+        (format == FORMAT_PS) ? GL2PS_PS :
+        (format == FORMAT_SVG) ? GL2PS_SVG :
+        GL2PS_EPS;
       int pssort = 
         (CTX::instance()->print.epsQuality == 3) ? GL2PS_NO_SORT :
         (CTX::instance()->print.epsQuality == 2) ? GL2PS_BSP_SORT : 
@@ -327,14 +356,14 @@ void CreateOutputFile(std::string fileName, int format)
           glMatrixMode(GL_MODELVIEW);
           glLoadIdentity();
           glRasterPos2d(0, 0);
-          gl2psDrawPixels(width, height, 0, 0, GL_RGB, GL_FLOAT, buffer.GetPixels());
+          gl2psDrawPixels(width, height, 0, 0, GL_RGB, GL_FLOAT, buffer.getPixels());
           glMatrixMode(GL_PROJECTION);
           glLoadMatrixd(projection);
           glMatrixMode(GL_MODELVIEW);
           glLoadMatrixd(modelview);
         }
         else{
-          buffer.Fill(CTX::instance()->batch);
+          buffer.fill(CTX::instance()->batch);
         }
         res = gl2psEndPage();
       }
@@ -346,11 +375,16 @@ void CreateOutputFile(std::string fileName, int format)
 
   case FORMAT_TEX:
     {
+      if(!GUI::available()) break;
+
       FILE *fp = fopen(fileName.c_str(), "w");
       if(!fp){
         Msg::Error("Unable to open file '%s'", fileName.c_str());
         break;
       }
+      GLint width = GUI::instance()->getCurrentOpenglWindow()->w();
+      GLint height = GUI::instance()->getCurrentOpenglWindow()->h();
+      GLint viewport[4] = {0, 0, width, height};
       GLint buffsize = 0;
       int res = GL2PS_OVERFLOW;
       while(res == GL2PS_OVERFLOW) {
@@ -361,7 +395,7 @@ void CreateOutputFile(std::string fileName, int format)
         PixelBuffer buffer(width, height, GL_RGB, GL_UNSIGNED_BYTE);
         int oldtext = CTX::instance()->print.text;
         CTX::instance()->print.text = 1;
-        buffer.Fill(CTX::instance()->batch);
+        buffer.fill(CTX::instance()->batch);
         CTX::instance()->print.text = oldtext;
         res = gl2psEndPage();
       }
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index c8d6785bb75c7f6ed503163cf596b58d1cdee869..50b4e6ea01ccee479bd1aafbc0da1dae4d11557a 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -38,6 +38,9 @@ StringXString GeneralOptions_String[] = {
   { F|O, "AxesLabelZ" , opt_general_axes_label2 , "" , 
     "Z-axis label" },
 
+  { F|O, "BackgroundImageFileName" , opt_general_background_image_filename , "" ,
+    "Background image file in JPEG or PNG format" },
+
   { F|O, "DefaultFileName" , opt_general_default_filename , "untitled.geo" ,
     "Default project file name" },
   { F,   "Display" , opt_general_display , "" ,
@@ -459,6 +462,10 @@ StringXNumber GeneralOptions_Number[] = {
 
   { F|O, "BackgroundGradient" , opt_general_background_gradient , 1. ,
     "Draw background gradient (0=none, 1=vertical, 2=horizontal, 3=radial)" },
+  { F|O, "BackgroundImagePositionX" , opt_general_background_image_position0 , 1.e5 ,
+    "X position (in pixels) of background image (< 0: right alignment; > 1e5: centered)" },
+  { F|O, "BackgroundImagePositionY" , opt_general_background_image_position1 , 1.e5 ,
+    "Y position (in pixels) of background image (< 0: right alignment; > 1e5: centered)" },
 
   { F,   "Clip0A" , opt_general_clip0a , 1.0 ,
     "First coefficient in equation for clipping plane 0 (`A' in `AX+BY+CZ+D=0')" },
@@ -468,23 +475,23 @@ StringXNumber GeneralOptions_Number[] = {
     "Third coefficient in equation for clipping plane 0 (`C' in `AX+BY+CZ+D=0')" },
   { F,   "Clip0D" , opt_general_clip0d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 0 (`D' in `AX+BY+CZ+D=0')" },
-  { F,   "Clip1A" , opt_general_clip1a , 1.0 , 
+  { F,   "Clip1A" , opt_general_clip1a , 0.0 , 
     "First coefficient in equation for clipping plane 1" },
-  { F,   "Clip1B" , opt_general_clip1b , 0.0 , 
+  { F,   "Clip1B" , opt_general_clip1b , 1.0 , 
     "Second coefficient in equation for clipping plane 1" },
   { F,   "Clip1C" , opt_general_clip1c , 0.0 , 
     "Third coefficient in equation for clipping plane 1" },
   { F,   "Clip1D" , opt_general_clip1d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 1" },
-  { F,   "Clip2A" , opt_general_clip2a , 1.0 , 
+  { F,   "Clip2A" , opt_general_clip2a , 0.0 , 
     "First coefficient in equation for clipping plane 2" },
   { F,   "Clip2B" , opt_general_clip2b , 0.0 , 
     "Second coefficient in equation for clipping plane 2" },
-  { F,   "Clip2C" , opt_general_clip2c , 0.0 , 
+  { F,   "Clip2C" , opt_general_clip2c , 1.0 , 
     "Third coefficient in equation for clipping plane 2" },
   { F,   "Clip2D" , opt_general_clip2d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 2" },
-  { F,   "Clip3A" , opt_general_clip3a , 1.0 , 
+  { F,   "Clip3A" , opt_general_clip3a , -1.0 , 
     "First coefficient in equation for clipping plane 3" },
   { F,   "Clip3B" , opt_general_clip3b , 0.0 , 
     "Second coefficient in equation for clipping plane 3" },
@@ -492,19 +499,19 @@ StringXNumber GeneralOptions_Number[] = {
     "Third coefficient in equation for clipping plane 3" },
   { F,   "Clip3D" , opt_general_clip3d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 3" },
-  { F,   "Clip4A" , opt_general_clip4a , 1.0 , 
+  { F,   "Clip4A" , opt_general_clip4a , 0.0 , 
     "First coefficient in equation for clipping plane 4" },
-  { F,   "Clip4B" , opt_general_clip4b , 0.0 , 
+  { F,   "Clip4B" , opt_general_clip4b , -1.0 , 
     "Second coefficient in equation for clipping plane 4" },
   { F,   "Clip4C" , opt_general_clip4c , 0.0 , 
     "Third coefficient in equation for clipping plane 4" },
   { F,   "Clip4D" , opt_general_clip4d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 4" },
-  { F,   "Clip5A" , opt_general_clip5a , 1.0 , 
+  { F,   "Clip5A" , opt_general_clip5a , 0.0 , 
     "First coefficient in equation for clipping plane 5" },
   { F,   "Clip5B" , opt_general_clip5b , 0.0 , 
     "Second coefficient in equation for clipping plane 5" },
-  { F,   "Clip5C" , opt_general_clip5c , 0.0 , 
+  { F,   "Clip5C" , opt_general_clip5c , -1.0 , 
     "Third coefficient in equation for clipping plane 5" },
   { F,   "Clip5D" , opt_general_clip5d , 0.0 , 
     "Fourth coefficient in equation for clipping plane 5" },
@@ -736,9 +743,9 @@ StringXNumber GeneralOptions_Number[] = {
   { F|O, "SmallAxes" , opt_general_small_axes , 1. ,
     "Display the small axes" },
   { F|O, "SmallAxesPositionX" , opt_general_small_axes_position0 , -60. ,
-    "X position of small axes (use negative values for right alignment)" },
+    "X position (in pixels) of small axes (< 0: right alignment; > 1e5: centered)" },
   { F|O, "SmallAxesPositionY" , opt_general_small_axes_position1 , -40. ,
-    "Y position of small axes (use negative values for bottom alignment)" },
+    "Y position (in pixels) of small axes (< 0: right alignment; > 1e5: centered)" },
   { F|O, "SmallAxesSize" , opt_general_small_axes_size , 30. ,
     "Size (in pixels) of small axes" },
   { F|S, "SolverPositionX" , opt_general_solver_position0 , 650. , 
@@ -1378,9 +1385,9 @@ StringXNumber ViewOptions_Number[] = {
   { F|O, "PointType" , opt_view_point_type , 0. , 
     "Display points as solid color dots (0), 3D spheres (1), scaled dots (2) or scaled spheres (3)" },
   { F|O, "PositionX" , opt_view_position0 , 100. , 
-    "Horizontal position (in pixels) of the upper left corner of the scale or 2D plot" }, 
+    "X position (in pixels) of the scale or 2D plot (< 0: right alignment; > 1e5: centered)" }, 
   { F|O, "PositionY" , opt_view_position1 , 50. , 
-    "Vertical position (in pixels) of the upper left corner of the scale or 2D plot" }, 
+    "Y position (in pixels) of the scale or 2D plot (< 0: right alignment; > 1e5: centered)" }, 
 
   { F,   "RaiseX" , opt_view_raise0 , 0. , 
     "Elevation of the view along X-axis (in model coordinates)" },
@@ -1450,6 +1457,9 @@ StringXNumber ViewOptions_Number[] = {
 } ;
 
 StringXNumber PrintOptions_Number[] = {
+  { F|O, "CompositeWindows" , opt_print_composite_windows , 0. ,
+    "Composite all window tiles in the same output image (for bitmap output only)" },
+
   { F|O, "EpsBackground" , opt_print_eps_background , 1. ,
     "Save image background in PostScript/PDF output" },
   { F|O, "EpsBestRoot" , opt_print_eps_best_root , 1. ,
diff --git a/Common/Makefile b/Common/Makefile
index 76d0e6f3126471de32bef99d078d2519a15bcdb2..407bc019d1943e36f291895c477ac6454be22041 100644
--- a/Common/Makefile
+++ b/Common/Makefile
@@ -141,9 +141,10 @@ CreateFile${OBJEXT}: CreateFile.cpp GmshConfig.h GmshMessage.h ../Geo/GModel.h \
   ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h GmshDefines.h StringUtils.h Context.h \
   ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h Options.h \
-  ../Post/ColorTable.h ../Graphics/gl2ps.h ../Common/GmshConfig.h \
-  ../Graphics/gl2gif.h ../Graphics/PixelBuffer.h ../Common/GmshMessage.h \
-  ../Fltk/Draw.h ../Common/MallocUtils.h ../Graphics/gl2jpeg.h \
+  ../Post/ColorTable.h ../Fltk/GUI.h ../Fltk/graphicWindow.h \
+  ../Fltk/openglWindow.h ../Graphics/drawContext.h ../Graphics/gl2ps.h \
+  ../Common/GmshConfig.h ../Graphics/gl2gif.h ../Graphics/PixelBuffer.h \
+  ../Common/GmshMessage.h ../Fltk/Draw.h ../Graphics/gl2jpeg.h \
   ../Graphics/PixelBuffer.h ../Graphics/gl2png.h \
   ../Graphics/PixelBuffer.h ../Graphics/gl2ppm.h \
   ../Graphics/PixelBuffer.h ../Graphics/gl2yuv.h \
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 3b96d68f4f827735b4039147151bcecaa47b243b..5d0582fd052f6e1c48330725081c262433177103 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -931,6 +931,13 @@ std::string opt_general_display(OPT_ARGS_STR)
   return CTX::instance()->display;
 }
 
+std::string opt_general_background_image_filename(OPT_ARGS_STR)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImageFileName = val;
+  return CTX::instance()->bgImageFileName;
+}
+
 std::string opt_general_filename(OPT_ARGS_STR)
 {
   return GModel::current()->getFileName();
@@ -3530,6 +3537,20 @@ double opt_general_background_gradient(OPT_ARGS_NUM)
   return CTX::instance()->bgGradient;
 }
 
+double opt_general_background_image_position0(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImagePosition[0] = val;
+  return CTX::instance()->bgImagePosition[0];
+}
+
+double opt_general_background_image_position1(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImagePosition[1] = val;
+  return CTX::instance()->bgImagePosition[1];
+}
+
 double opt_general_trackball(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
@@ -8280,6 +8301,13 @@ double opt_print_tex_as_equation(OPT_ARGS_NUM)
   return CTX::instance()->print.texAsEquation;
 }
 
+double opt_print_composite_windows(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->print.compositeWindows = (int)val;
+  return CTX::instance()->print.compositeWindows;
+}
+
 // Color option routines
 
 #if defined(HAVE_FLTK)
diff --git a/Common/Options.h b/Common/Options.h
index bbe99a21e1cffb0e004872d5ef3ade965dcf7020..d164e4ffb5f1e41258a5708ddd522c7417980ad7 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -31,6 +31,7 @@ std::string opt_general_axes_label2(OPT_ARGS_STR);
 std::string opt_general_axes_format0(OPT_ARGS_STR);
 std::string opt_general_axes_format1(OPT_ARGS_STR);
 std::string opt_general_axes_format2(OPT_ARGS_STR);
+std::string opt_general_background_image_filename(OPT_ARGS_STR);
 std::string opt_general_display(OPT_ARGS_STR);
 std::string opt_general_filename(OPT_ARGS_STR);
 std::string opt_general_default_filename(OPT_ARGS_STR);
@@ -279,6 +280,8 @@ double opt_general_shine(OPT_ARGS_NUM);
 double opt_general_shine_exponent(OPT_ARGS_NUM);
 double opt_general_color_scheme(OPT_ARGS_NUM);
 double opt_general_background_gradient(OPT_ARGS_NUM);
+double opt_general_background_image_position0(OPT_ARGS_NUM);
+double opt_general_background_image_position1(OPT_ARGS_NUM);
 double opt_general_verbosity(OPT_ARGS_NUM);
 double opt_general_nopopup(OPT_ARGS_NUM);
 double opt_general_non_modal_windows(OPT_ARGS_NUM);
@@ -693,6 +696,7 @@ double opt_print_gif_interlace(OPT_ARGS_NUM);
 double opt_print_gif_transparent(OPT_ARGS_NUM);
 double opt_print_text(OPT_ARGS_NUM);
 double opt_print_tex_as_equation(OPT_ARGS_NUM);
+double opt_print_composite_windows(OPT_ARGS_NUM);
 
 // COLORS
 
diff --git a/Fltk/Draw.cpp b/Fltk/Draw.cpp
index 6107cb186d46fc6aab9e462f111ceae278dc469a..ba21c826a2ac99588bc4e1f9f0cb4b31c5114c49 100644
--- a/Fltk/Draw.cpp
+++ b/Fltk/Draw.cpp
@@ -56,14 +56,6 @@ void DrawCurrentOpenglWindow(bool make_current)
   gl->getDrawContext()->draw2d();
 }
 
-void GetCurrentOpenglWindowViewport(int viewport[4])
-{
-  if(!GUI::available()) return;
-  openglWindow *gl = GUI::instance()->getCurrentOpenglWindow();
-  for(int i = 0; i < 4; i++)
-    viewport[i] = gl->getDrawContext()->viewport[i];
-}
-
 int GetFontIndex(const char *fontname)
 {
   if(fontname){
diff --git a/Fltk/Draw.h b/Fltk/Draw.h
index defebfd35d16052d5c467970a0b5d0e4c40ebbaf..6206230767da154fd1e0ea1736eb186a2cacd826 100644
--- a/Fltk/Draw.h
+++ b/Fltk/Draw.h
@@ -11,7 +11,6 @@
 void Draw();
 void DrawPlugin(void (*draw)(void *context));
 void DrawCurrentOpenglWindow(bool make_current);
-void GetCurrentOpenglWindowViewport(int viewport[4]);
 int GetFontIndex(const char *fontname);
 int GetFontEnum(int index);
 const char *GetFontName(int index);
diff --git a/Fltk/fileDialogs.cpp b/Fltk/fileDialogs.cpp
index 1ecbb9f25bb0fcecb56da12fa74a87a7ce36f0e3..5d4aaeff6784ea4663d6ff215223f11368d87cb8 100644
--- a/Fltk/fileDialogs.cpp
+++ b/Fltk/fileDialogs.cpp
@@ -133,20 +133,23 @@ int generic_bitmap_dialog(const char *name, const char *title, int format)
 {
   struct _generic_bitmap_dialog{
     Fl_Window *window;
-    Fl_Check_Button *b;
+    Fl_Check_Button *b[2];
     Fl_Button *ok, *cancel;
   };
   static _generic_bitmap_dialog *dialog = NULL;
 
   if(!dialog){
     dialog = new _generic_bitmap_dialog;
-    int h = 3 * WB + 2 * BH, w = 2 * BB + 3 * WB, y = WB;
+    int h = 3 * WB + 3 * BH, w = 2 * BB + 3 * WB, y = WB;
     dialog->window = new Fl_Double_Window(w, h);
     dialog->window->box(GMSH_WINDOW_BOX);
     dialog->window->set_modal();
-    dialog->b = new Fl_Check_Button
+    dialog->b[0] = new Fl_Check_Button
       (WB, y, 2 * BB + WB, BH, "Print text strings"); y += BH;
-    dialog->b->type(FL_TOGGLE_BUTTON);
+    dialog->b[0]->type(FL_TOGGLE_BUTTON);
+    dialog->b[1] = new Fl_Check_Button
+      (WB, y, 2 * BB + WB, BH, "Composite all window tiles"); y += BH;
+    dialog->b[1]->type(FL_TOGGLE_BUTTON);
     dialog->ok = new Fl_Return_Button(WB, y + WB, BB, BH, "OK");
     dialog->cancel = new Fl_Button(2 * WB + BB, y + WB, BB, BH, "Cancel");
     dialog->window->end();
@@ -154,7 +157,8 @@ int generic_bitmap_dialog(const char *name, const char *title, int format)
   }
   
   dialog->window->label(title);
-  dialog->b->value(CTX::instance()->print.text);
+  dialog->b[0]->value(CTX::instance()->print.text);
+  dialog->b[1]->value(CTX::instance()->print.compositeWindows);
   dialog->window->show();
 
   while(dialog->window->shown()){
@@ -163,7 +167,8 @@ int generic_bitmap_dialog(const char *name, const char *title, int format)
       Fl_Widget* o = Fl::readqueue();
       if (!o) break;
       if (o == dialog->ok) {
-        opt_print_text(0, GMSH_SET | GMSH_GUI, (int)dialog->b->value());
+        opt_print_text(0, GMSH_SET | GMSH_GUI, (int)dialog->b[0]->value());
+        opt_print_composite_windows(0, GMSH_SET | GMSH_GUI, (int)dialog->b[1]->value());
         CreateOutputFile(name, format);
         dialog->window->hide();
         return 1;
@@ -233,14 +238,14 @@ int jpeg_dialog(const char *name)
   struct _jpeg_dialog{
     Fl_Window *window;
     Fl_Value_Slider *s[2];
-    Fl_Check_Button *b;
+    Fl_Check_Button *b[2];
     Fl_Button *ok, *cancel;
   };
   static _jpeg_dialog *dialog = NULL;
 
   if(!dialog){
     dialog = new _jpeg_dialog;
-    int h = 3 * WB + 4 * BH, w = 2 * BB + 3 * WB, y = WB;
+    int h = 3 * WB + 5 * BH, w = 2 * BB + 3 * WB, y = WB;
     dialog->window = new Fl_Double_Window(w, h, "JPEG Options");
     dialog->window->box(GMSH_WINDOW_BOX);
     dialog->window->set_modal();
@@ -256,9 +261,12 @@ int jpeg_dialog(const char *name)
     dialog->s[1]->minimum(0);
     dialog->s[1]->maximum(100);
     dialog->s[1]->step(1);
-    dialog->b = new Fl_Check_Button
+    dialog->b[0] = new Fl_Check_Button
       (WB, y, 2 * BB + WB, BH, "Print text strings"); y += BH;
-    dialog->b->type(FL_TOGGLE_BUTTON);
+    dialog->b[0]->type(FL_TOGGLE_BUTTON);
+    dialog->b[1] = new Fl_Check_Button
+      (WB, y, 2 * BB + WB, BH, "Composite all window tiles"); y += BH;
+    dialog->b[1]->type(FL_TOGGLE_BUTTON);
     dialog->ok = new Fl_Return_Button(WB, y + WB, BB, BH, "OK");
     dialog->cancel = new Fl_Button(2 * WB + BB, y + WB, BB, BH, "Cancel");
     dialog->window->end();
@@ -267,7 +275,8 @@ int jpeg_dialog(const char *name)
   
   dialog->s[0]->value(CTX::instance()->print.jpegQuality);
   dialog->s[1]->value(CTX::instance()->print.jpegSmoothing);
-  dialog->b->value(CTX::instance()->print.text);
+  dialog->b[0]->value(CTX::instance()->print.text);
+  dialog->b[1]->value(CTX::instance()->print.compositeWindows);
   dialog->window->show();
 
   while(dialog->window->shown()){
@@ -278,7 +287,8 @@ int jpeg_dialog(const char *name)
       if (o == dialog->ok) {
         opt_print_jpeg_quality(0, GMSH_SET | GMSH_GUI, (int)dialog->s[0]->value());
         opt_print_jpeg_smoothing(0, GMSH_SET | GMSH_GUI, (int)dialog->s[1]->value());
-        opt_print_text(0, GMSH_SET | GMSH_GUI, (int)dialog->b->value());
+        opt_print_text(0, GMSH_SET | GMSH_GUI, (int)dialog->b[0]->value());
+        opt_print_composite_windows(0, GMSH_SET | GMSH_GUI, (int)dialog->b[1]->value());
         CreateOutputFile(name, FORMAT_JPEG);
         dialog->window->hide();
         return 1;
@@ -298,14 +308,14 @@ int gif_dialog(const char *name)
 {
   struct _gif_dialog{
     Fl_Window *window;
-    Fl_Check_Button *b[5];
+    Fl_Check_Button *b[6];
     Fl_Button *ok, *cancel;
   };
   static _gif_dialog *dialog = NULL;
 
   if(!dialog){
     dialog = new _gif_dialog;
-    int h = 3 * WB + 6 * BH, w = 2 * BB + 3 * WB, y = WB;
+    int h = 3 * WB + 7 * BH, w = 2 * BB + 3 * WB, y = WB;
     dialog->window = new Fl_Double_Window(w, h, "GIF Options");
     dialog->window->box(GMSH_WINDOW_BOX);
     dialog->window->set_modal();
@@ -319,7 +329,9 @@ int gif_dialog(const char *name)
       (WB, y, 2 * BB + WB, BH, "Transparent background"); y += BH;
     dialog->b[4] = new Fl_Check_Button
       (WB, y, 2 * BB + WB, BH, "Print text strings"); y += BH;
-    for(int i = 0; i < 5; i++){
+    dialog->b[5] = new Fl_Check_Button
+      (WB, y, 2 * BB + WB, BH, "Composite all window tiles"); y += BH;
+    for(int i = 0; i < 6; i++){
       dialog->b[i]->type(FL_TOGGLE_BUTTON);
     }
     dialog->ok = new Fl_Return_Button(WB, y + WB, BB, BH, "OK");
@@ -333,6 +345,7 @@ int gif_dialog(const char *name)
   dialog->b[2]->value(CTX::instance()->print.gifSort);
   dialog->b[3]->value(CTX::instance()->print.gifTransparent);
   dialog->b[4]->value(CTX::instance()->print.text);
+  dialog->b[5]->value(CTX::instance()->print.compositeWindows);
   dialog->window->show();
 
   while(dialog->window->shown()){
@@ -346,6 +359,7 @@ int gif_dialog(const char *name)
         opt_print_gif_sort(0, GMSH_SET | GMSH_GUI, dialog->b[2]->value());
         opt_print_gif_transparent(0, GMSH_SET | GMSH_GUI, dialog->b[3]->value());
         opt_print_text(0, GMSH_SET | GMSH_GUI, dialog->b[4]->value());
+        opt_print_composite_windows(0, GMSH_SET | GMSH_GUI, dialog->b[5]->value());
         CreateOutputFile(name, FORMAT_GIF);
         dialog->window->hide();
         return 1;
diff --git a/Fltk/partitionDialog.cpp b/Fltk/partitionDialog.cpp
index 988b9715bc24867e05c0bdacbdaa1e001ce74d67..58734d7c409d01b2611435933b9b2c4ab2732131 100644
--- a/Fltk/partitionDialog.cpp
+++ b/Fltk/partitionDialog.cpp
@@ -776,7 +776,7 @@ void partition_dialog()
 
 void partition_dialog()
 {
-  Msg::Error("Gmsh has to be compiled with METIS or CHACO support to partition meshes");
+  Msg::Error("Gmsh must be compiled with METIS or CHACO support to partition meshes");
 }
 
 #endif
diff --git a/Geo/GModelIO_Fourier.cpp b/Geo/GModelIO_Fourier.cpp
index 83f9fa43617f44736219e606cb1878d53fbb81d1..db25e3f2807728501e26d94988527bf0ed99d70b 100644
--- a/Geo/GModelIO_Fourier.cpp
+++ b/Geo/GModelIO_Fourier.cpp
@@ -144,14 +144,14 @@ void GModel::_deleteFMInternals()
 
 int GModel::readFourier(const std::string &fn)
 {
-  Msg::Error("Gmsh has to be compiled with Fourier Model support to load '%s'",
+  Msg::Error("Gmsh must be compiled with Fourier Model support to load '%s'",
       fn.c_str());
   return 0;
 }
 
 int GModel::writeFourier(const std::string &fn)
 {
-  Msg::Error("Gmsh has to be compiled with Fourier Model support to load '%s'",
+  Msg::Error("Gmsh must be compiled with Fourier Model support to load '%s'",
 	     fn.c_str());
   return 0;
 }
diff --git a/Geo/GModelIO_MED.cpp b/Geo/GModelIO_MED.cpp
index a64173cf779209062776dad2ea726221328226ac..3a75713fe2e9d21a7035e5f979bbebbc2cd22073 100644
--- a/Geo/GModelIO_MED.cpp
+++ b/Geo/GModelIO_MED.cpp
@@ -526,21 +526,21 @@ int GModel::writeMED(const std::string &name, bool saveAll, double scalingFactor
 
 int GModel::readMED(const std::string &name)
 {
-  Msg::Error("Gmsh has to be compiled with MED support to read '%s'",
+  Msg::Error("Gmsh must be compiled with MED support to read '%s'",
 	     name.c_str());
   return 0;
 }
 
 int GModel::readMED(const std::string &name, int meshIndex)
 {
-  Msg::Error("Gmsh has to be compiled with MED support to read '%s'",
+  Msg::Error("Gmsh must be compiled with MED support to read '%s'",
 	     name.c_str());
   return 0;
 }
 
 int GModel::writeMED(const std::string &name, bool saveAll, double scalingFactor)
 {
-  Msg::Error("Gmsh has to be compiled with MED support to write '%s'",
+  Msg::Error("Gmsh must be compiled with MED support to write '%s'",
 	     name.c_str());
   return 0;
 }
diff --git a/Geo/GModelIO_OCC.cpp b/Geo/GModelIO_OCC.cpp
index 71133a89f2951ef5d11c68a9f845a5c860446872..76ce32ffa4b4d2930f0044b523fadd734ba8804a 100644
--- a/Geo/GModelIO_OCC.cpp
+++ b/Geo/GModelIO_OCC.cpp
@@ -699,28 +699,28 @@ void GModel::_deleteOCCInternals()
 
 int GModel::readOCCSTEP(const std::string &fn)
 {
-  Msg::Error("Gmsh has to be compiled with OpenCascade support to load '%s'",
+  Msg::Error("Gmsh must be compiled with OpenCascade support to load '%s'",
 	     fn.c_str());
   return 0;
 }
 
 int GModel::readOCCIGES(const std::string &fn)
 {
-  Msg::Error("Gmsh has to be compiled with OpenCascade support to load '%s'",
+  Msg::Error("Gmsh must be compiled with OpenCascade support to load '%s'",
 	     fn.c_str());
   return 0;
 }
 
 int GModel::readOCCBREP(const std::string &fn)
 {
-  Msg::Error("Gmsh has to be compiled with OpenCascade support to load '%s'",
+  Msg::Error("Gmsh must be compiled with OpenCascade support to load '%s'",
 	     fn.c_str());
   return 0;
 }
 
 int GModel::importOCCShape(const void *shape, const void *options)
 {
-  Msg::Error("Gmsh has to be compiled with OpenCascade support to import "
+  Msg::Error("Gmsh must be compiled with OpenCascade support to import "
 	     "a TopoDS_Shape");
   return 0;
 }
diff --git a/Graphics/Makefile b/Graphics/Makefile
index b9ba433e4f1ef4a00f41ff6e15fd6e8e18bff139..3e5c659c2315c148d9a456518c19512849fba54b 100644
--- a/Graphics/Makefile
+++ b/Graphics/Makefile
@@ -145,10 +145,10 @@ gl2ps${OBJEXT}: gl2ps.cpp gl2ps.h ../Common/GmshConfig.h
 gl2gif${OBJEXT}: gl2gif.cpp ../Common/MallocUtils.h gl2gif.h PixelBuffer.h \
   ../Common/GmshConfig.h ../Common/GmshMessage.h ../Fltk/Draw.h
 gl2jpeg${OBJEXT}: gl2jpeg.cpp ../Common/GmshConfig.h gl2jpeg.h PixelBuffer.h \
-  ../Common/GmshMessage.h ../Fltk/Draw.h ../Common/MallocUtils.h
+  ../Common/GmshMessage.h ../Fltk/Draw.h
 gl2png${OBJEXT}: gl2png.cpp ../Common/GmshConfig.h gl2png.h PixelBuffer.h \
-  ../Common/GmshMessage.h ../Fltk/Draw.h ../Common/MallocUtils.h
+  ../Common/GmshMessage.h ../Fltk/Draw.h
 gl2ppm${OBJEXT}: gl2ppm.cpp gl2ppm.h PixelBuffer.h ../Common/GmshConfig.h \
-  ../Common/GmshMessage.h ../Fltk/Draw.h ../Common/MallocUtils.h
+  ../Common/GmshMessage.h ../Fltk/Draw.h
 gl2yuv${OBJEXT}: gl2yuv.cpp ../Common/MallocUtils.h gl2yuv.h PixelBuffer.h \
   ../Common/GmshConfig.h ../Common/GmshMessage.h ../Fltk/Draw.h
diff --git a/Graphics/PixelBuffer.h b/Graphics/PixelBuffer.h
index a00e2e26217ed4fa1c303798c39df8461a90b0bd..1b69d882214334184a56553e8345f4a86958a164 100644
--- a/Graphics/PixelBuffer.h
+++ b/Graphics/PixelBuffer.h
@@ -6,11 +6,11 @@
 #ifndef _PIXEL_BUFFER_H_
 #define _PIXEL_BUFFER_H_
 
+#include <string.h>
 #include <FL/gl.h>
 #include "GmshConfig.h"
 #include "GmshMessage.h"
 #include "Draw.h"
-#include "MallocUtils.h"
 
 #if defined(HAVE_OSMESA)
 #include <GL/osmesa.h>
@@ -20,7 +20,7 @@ class PixelBuffer{
  private:
   int _width, _height, _numComp, _dataSize;
   GLenum _format, _type;
-  void *_pixels;
+  unsigned char *_pixels;
  public:
   PixelBuffer(int width, int height, GLenum format, GLenum type)
     : _width(width), _height(height), _format(format), _type(type)
@@ -48,27 +48,46 @@ class PixelBuffer{
       _type = GL_UNSIGNED_BYTE;
       _dataSize = sizeof(unsigned char);
     }
-    _pixels = Calloc(_numComp * _width * _height, _dataSize);
+    int n = _numComp * _width * _height * _dataSize;
+    _pixels = new unsigned char[n];
+    for(int i = 0; i < n; i++) _pixels[i] = 0;
   }
   ~PixelBuffer()
   {
-    Free(_pixels);
+    delete [] _pixels;
   }
-  int GetWidth(){ return _width; }
-  int GetHeight(){ return _height; }
-  int GetNumComp(){ return _numComp; }
-  int GetDataSize(){ return _dataSize; }
-  GLenum GetFormat(){ return _format; }
-  GLenum GetType(){ return _type; }
-  void *GetPixels(){ return _pixels; }
-  void Fill(int offscreen)
+  int getWidth(){ return _width; }
+  int getHeight(){ return _height; }
+  int getNumComp(){ return _numComp; }
+  int getDataSize(){ return _dataSize; }
+  GLenum getFormat(){ return _format; }
+  GLenum getType(){ return _type; }
+  void *getPixels(){ return (void*)_pixels; }
+  void copyPixels(int x, int y, PixelBuffer *buffer)
+  {
+    if(x + buffer->getWidth() > _width || y + buffer->getHeight() > _height){
+      Msg::Error("Destination pixel buffer too small for holding copy");
+      return;
+    }
+    if(buffer->getNumComp() != _numComp || buffer->getDataSize() != _dataSize ||
+       buffer->getFormat() != _format || buffer->getType() != _type){
+      Msg::Error("Pixel buffer type mismatch: impossible to copy");
+      return;
+    }
+    for(int i = 0; i < buffer->getWidth(); i++)
+      for(int j = 0; j < buffer->getHeight(); j++)
+        memcpy(_pixels + ((j + y) * _width + (i + x)) * _dataSize * _numComp,
+               (unsigned char*)buffer->getPixels() + (j * buffer->getWidth() + i) *
+               _dataSize * _numComp, _dataSize * _numComp);
+  }
+  void fill(int offscreen)
   {
     if(!offscreen){
       DrawCurrentOpenglWindow(true);
       glFinish();
       glPixelStorei(GL_PACK_ALIGNMENT, 1);
       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-      glReadPixels(0, 0, _width, _height, _format, _type, _pixels);
+      glReadPixels(0, 0, _width, _height, _format, _type, (void*)_pixels);
     }
     else{
 #if defined(HAVE_OSMESA)
@@ -81,14 +100,14 @@ class PixelBuffer{
 	Msg::Error("OSMesaCreateContext failed");
         return;
       }
-      if(!OSMesaMakeCurrent(ctx, _pixels, GL_UNSIGNED_BYTE, _width, _height)){
+      if(!OSMesaMakeCurrent(ctx, (void*)_pixels, GL_UNSIGNED_BYTE, _width, _height)){
 	Msg::Error("OSMesaMakeCurrent failed");
       }
       DrawCurrentOpenglWindow(false);
       glFinish();
       OSMesaDestroyContext(ctx);
 #else
-      Msg::Warning("Offscreen rendering not available in this version");
+      Msg::Warning("Gmsh must be compiled with OSMesa to support offscreen rendering");
 #endif
     }
   }
diff --git a/Graphics/ReadImg.cpp b/Graphics/ReadImg.cpp
index b52e60e62a073d9f1a902f20aefd5341893da3a0..68d97ea87c5c6e110d254c54c020666a57cbc23c 100644
--- a/Graphics/ReadImg.cpp
+++ b/Graphics/ReadImg.cpp
@@ -21,13 +21,13 @@ static PViewDataList *Img2Data(Fl_RGB_Image &img_init, int quads=1,
   img_init.desaturate(); // convert to grayscale
 
   // resize if necessary
-  Fl_RGB_Image * img;
+  Fl_RGB_Image *img;
   if(!resizex || !resizey)
-    img = (Fl_RGB_Image *) img_init.copy();
+    img = (Fl_RGB_Image*)img_init.copy();
   else
-    img = (Fl_RGB_Image *) img_init.copy(resizex, resizey);
+    img = (Fl_RGB_Image*)img_init.copy(resizex, resizey);
 
-  const uchar *data = img->array;
+  const unsigned char *data = img->array;
   int height = img->h();
   int width = img->w();
   int dim = img->d();
@@ -41,8 +41,8 @@ static PViewDataList *Img2Data(Fl_RGB_Image &img_init, int quads=1,
 
   double z = 0.;
   for(int i = 0; i < height - 1; i++) {
-    const uchar *a = data + i * width * dim;
-    const uchar *a1 = data + (i + 1) * width * dim;
+    const unsigned char *a = data + i * width * dim;
+    const unsigned char *a1 = data + (i + 1) * width * dim;
     double y = height - i - 1;
     double y1 = height - i - 2;
     for(int j = 0; j < width - 1; j++) {
diff --git a/Graphics/drawContext.cpp b/Graphics/drawContext.cpp
index 834d94b890abafc721ed851cfe2878fb9c7f1826..10e18ce5ccf356c2fc3400603b01e475d0cf3c53 100644
--- a/Graphics/drawContext.cpp
+++ b/Graphics/drawContext.cpp
@@ -3,7 +3,10 @@
 // See the LICENSE.txt file for license information. Please report all
 // bugs and problems to <gmsh@geuz.org>.
 
+#include <string>
 #include <FL/gl.h>
+#include <FL/Fl_JPEG_Image.H>
+#include <FL/Fl_PNG_Image.H>
 #include "GmshMessage.h"
 #include "drawContext.h"
 #include "Trackball.h"
@@ -12,6 +15,7 @@
 #include "GModel.h"
 #include "PView.h"
 #include "PViewOptions.h"
+#include "gl2ps.h"
 
 drawContext::drawContext(drawTransform *transform) 
   : _transform(transform)
@@ -33,6 +37,8 @@ drawContext::drawContext(drawTransform *transform)
   vxmin = vymin = vxmax = vymax = 0.;
   pixel_equiv_x = pixel_equiv_y = 0.;
 
+  _bgImageSize[0] = _bgImageSize[1] = 0;
+
   _quadric = 0; // cannot create it here: needs valid opengl context
   _displayLists = 0;
 }
@@ -247,6 +253,7 @@ void drawContext::draw2d()
   glLoadIdentity();
   glOrtho((double)viewport[0], (double)viewport[2],
           (double)viewport[1], (double)viewport[3], -1., 1.);
+
   // hack to make the 2D primitives appear "in front" in GL2PS
   glTranslated(0., 0., CTX::instance()->clipFactor > 1. ? 
                1. / CTX::instance()->clipFactor : CTX::instance()->clipFactor);
@@ -298,95 +305,164 @@ void drawContext::initProjection(int xpick, int ypick, int wpick, int hpick)
   // no initial translation of the model
   t_init[0] = t_init[1] = t_init[2] = 0.;
 
-  // setup ortho or perspective projection matrix
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-
-  // restrict picking to a rectangular region around xpick,ypick in SELECT mode
-  if(render_mode == GMSH_SELECT)
-    gluPickMatrix((GLdouble)xpick, (GLdouble)(viewport[3] - ypick),
-                  (GLdouble)wpick, (GLdouble)hpick, (GLint *)viewport);
-
-  double grad_z, grad_xy;
-  double zmax = std::max(fabs(CTX::instance()->min[2]), fabs(CTX::instance()->max[2]));
+  // set up the near and far clipping planes so that the box is large
+  // enough to manipulate the model and zoom, but not too big
+  // (otherwise the z-buffer resolution e.g. with Mesa can become
+  // insufficient)
+  double zmax = std::max(fabs(CTX::instance()->min[2]),
+                         fabs(CTX::instance()->max[2]));
   if(zmax < CTX::instance()->lc) zmax = CTX::instance()->lc;
 
+  double clip_near, clip_far;
   if(CTX::instance()->ortho) {
-    // setting up the near and far clipping planes so that the box is
-    // large enough to manipulate the model and zoom, but not too big
-    // (the z-buffer resolution, e.g., on software Mesa can become
-    // insufficient)
-    double clip = zmax * s[2] * CTX::instance()->clipFactor;
-    glOrtho(vxmin, vxmax, vymin, vymax, -clip, clip);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-    grad_z = 0.99 * clip;
-    grad_xy = 1.;
+    clip_near = -zmax * s[2] * CTX::instance()->clipFactor;
+    clip_far = -clip_near;
   }
   else {
-    double clip_near = 0.75 * CTX::instance()->clipFactor * zmax;
-    double clip_far = 75. * CTX::instance()->clipFactor * zmax;
-    double coef = (75./0.75) / 3.;
-    // recenter the model such that the perspective is always at the
-    // center of gravity (we should maybe add an option to choose
-    // this, as we do for the rotation center)
-    t_init[0] = CTX::instance()->cg[0];
-    t_init[1] = CTX::instance()->cg[1];
-    vxmin -= t_init[0];
-    vxmax -= t_init[0];
-    vymin -= t_init[1];
-    vymax -= t_init[1];
-    glFrustum(vxmin, vxmax, vymin, vymax, clip_near, clip_far);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-    glTranslated(-coef * t_init[0], -coef * t_init[1], -coef * clip_near);
-    glScaled(coef, coef, coef);
-    grad_z = 0.99 * clip_far;
-    grad_xy = clip_far / clip_near;
+    clip_near = 0.75 * CTX::instance()->clipFactor * zmax;
+    clip_far = 75. * CTX::instance()->clipFactor * zmax;
   }
 
-  // draw background gradient
-  if(render_mode != GMSH_SELECT && CTX::instance()->bgGradient){
+  // setup projection matrix
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+
+  // restrict picking to a rectangular region around xpick,ypick
+  if(render_mode == GMSH_SELECT){
+    gluPickMatrix((GLdouble)xpick, (GLdouble)(viewport[3] - ypick),
+                  (GLdouble)wpick, (GLdouble)hpick, (GLint *)viewport);
+  }
+
+  // draw background if not in selection mode
+  if(render_mode != GMSH_SELECT && (CTX::instance()->bgGradient || 
+                                    CTX::instance()->bgImageFileName.size())){
+    glDisable(GL_DEPTH_TEST);
     glPushMatrix();
     glLoadIdentity();
-    glTranslated(0., 0., -grad_z);
+    // the z values and the translation are only needed for GL2PS,
+    // which does not understand "no depth test" (hence we must make
+    // sure that we draw the background behing the rest of the scene)
+    glOrtho((double)viewport[0], (double)viewport[2],
+            (double)viewport[1], (double)viewport[3], 
+            clip_near, clip_far);
+    glTranslated(0., 0., -0.99 * clip_far);
+
+    // background gradient
     if(CTX::instance()->bgGradient == 1){ // vertical
       glBegin(GL_QUADS);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bg);
-      glVertex3d(grad_xy * vxmin, grad_xy * vymin, 0.);
-      glVertex3d(grad_xy * vxmax, grad_xy * vymin, 0.);
+      glVertex2i(viewport[0], viewport[1]);
+      glVertex2i(viewport[2], viewport[1]);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bgGrad);
-      glVertex3d(grad_xy * vxmax, grad_xy * vymax, 0.);
-      glVertex3d(grad_xy * vxmin, grad_xy * vymax, 0.);
+      glVertex2i(viewport[2], viewport[3]);
+      glVertex2i(viewport[0], viewport[3]);
       glEnd();
     }
     else if(CTX::instance()->bgGradient == 2){ // horizontal
       glBegin(GL_QUADS);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bg);
-      glVertex3d(grad_xy * vxmax, grad_xy * vymin, 0.);
-      glVertex3d(grad_xy * vxmax, grad_xy * vymax, 0.);
+      glVertex2i(viewport[2], viewport[1]);
+      glVertex2i(viewport[2], viewport[3]);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bgGrad);
-      glVertex3d(grad_xy * vxmin, grad_xy * vymax, 0.);
-      glVertex3d(grad_xy * vxmin, grad_xy * vymin, 0.);
+      glVertex2i(viewport[0], viewport[3]);
+      glVertex2i(viewport[0], viewport[1]);
       glEnd();
     }
-    else{ // radial
-      double cx = grad_xy * (vxmin + vxmax) / 2.;
-      double cy = grad_xy * (vymin + vymax) / 2.;
-      double r = grad_xy * std::max(vxmax - vxmin, vymax - vymin) / 2.;
+    else if(CTX::instance()->bgGradient == 3){ // radial
+      double cx = 0.5 * (viewport[0] + viewport[2]);
+      double cy = 0.5 * (viewport[1] + viewport[3]);
+      double r = 0.5 * std::max(viewport[2] - viewport[0],
+                                viewport[3] - viewport[1]);
       glBegin(GL_TRIANGLE_FAN);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bgGrad);
-      glVertex3d(cx, cy, 0.);
+      glVertex2d(cx, cy);
       glColor4ubv((GLubyte *) & CTX::instance()->color.bg);
-      glVertex3d(cx + r, cy, 0.);
+      glVertex2d(cx + r, cy);
       int ntheta = 36;
       for(int i = 1; i < ntheta + 1; i ++){
         double theta = i * 2 * M_PI / (double)ntheta;
-        glVertex3d(cx + r * cos(theta), cy + r * sin(theta), 0.);       
+        glVertex2d(cx + r * cos(theta), cy + r * sin(theta));       
       }
       glEnd();
     }
+
+    // hack for GL2PS (to make sure that the image is in front of the
+    // gradient)
+    glTranslated(0., 0., 0.01 * clip_far);
+
+    // background image
+    if(CTX::instance()->bgImageFileName.size()){
+      if(_bgImage.empty()){
+        int idot = CTX::instance()->bgImageFileName.find_last_of('.');
+        std::string ext;
+        if(idot > 0 && idot < CTX::instance()->bgImageFileName.size())
+          ext = CTX::instance()->bgImageFileName.substr(idot + 1);
+        Fl_RGB_Image *img = 0;
+        if(ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG")
+          img = new Fl_JPEG_Image(CTX::instance()->bgImageFileName.c_str());
+        else if(ext == "png" || ext == "PNG")
+          img = new Fl_PNG_Image(CTX::instance()->bgImageFileName.c_str());
+        if(img && img->d() >= 3){
+          const unsigned char *data = img->array;
+          for(int j = img->h() - 1; j >= 0; j--) {
+            for(int i = 0; i < img->w(); i++) {
+              int idx = j * img->w() * img->d() + i * img->d();
+              _bgImage.push_back((GLfloat)data[idx] / 255.);
+              _bgImage.push_back((GLfloat)data[idx + 1] / 255.);
+              _bgImage.push_back((GLfloat)data[idx + 2] / 255.);
+            }
+          }
+          _bgImageSize[0] = img->w();
+          _bgImageSize[1] = img->h();
+        }
+        if(!_bgImageSize[0] || !_bgImageSize[1]){
+          Msg::Error("Could not load valid background image");
+          for(int i = 0; i < 3; i++) _bgImage.push_back(0);
+          _bgImageSize[0] = _bgImageSize[1] = 1.;
+        }
+      }
+      double x = CTX::instance()->bgImagePosition[0];
+      double y = CTX::instance()->bgImagePosition[1];
+      int c = fix2dCoordinates(&x, &y);
+      if(c & 1) x -= _bgImageSize[0] / 2.;
+      if(c & 2) y -= _bgImageSize[1] / 2.;
+      if(x < viewport[0]) x = viewport[0];
+      if(y < viewport[1]) y = viewport[1];
+      glRasterPos2d(x, y);
+      glPixelStorei(GL_PACK_ALIGNMENT, 1);
+      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+      glDrawPixels(_bgImageSize[0], _bgImageSize[1], GL_RGB, GL_FLOAT,
+                   (void*)&_bgImage[0]);
+      gl2psDrawPixels(_bgImageSize[0], _bgImageSize[1], 0, 0, GL_RGB, GL_FLOAT,
+                      (void*)&_bgImage[0]);
+    }
+
     glPopMatrix();
+    glEnable(GL_DEPTH_TEST);
+  }
+
+
+  if(CTX::instance()->ortho) {
+    glOrtho(vxmin, vxmax, vymin, vymax, clip_near, clip_far);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+  }
+  else {
+    // recenter the model such that the perspective is always at the
+    // center of gravity (we should maybe add an option to choose
+    // this, as we do for the rotation center)
+    t_init[0] = CTX::instance()->cg[0];
+    t_init[1] = CTX::instance()->cg[1];
+    vxmin -= t_init[0];
+    vxmax -= t_init[0];
+    vymin -= t_init[1];
+    vymax -= t_init[1];
+    glFrustum(vxmin, vxmax, vymin, vymax, clip_near, clip_far);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    double coef = (clip_far / clip_near) / 3.;
+    glTranslated(-coef * t_init[0], -coef * t_init[1], -coef * clip_near);
+    glScaled(coef, coef, coef);
   }
 }
 
diff --git a/Graphics/drawContext.h b/Graphics/drawContext.h
index 6ffcb864f465c2a7910452bb54beb48c5a5e7e38..f8d62fbab5bdf22b40e4d4d87c855592cfd53b4a 100644
--- a/Graphics/drawContext.h
+++ b/Graphics/drawContext.h
@@ -7,6 +7,7 @@
 #define _DRAW_CONTEXT_H_
 
 #include <string>
+#include <vector>
 #include <set>
 #include <FL/gl.h>
 
@@ -74,6 +75,8 @@ class drawContext {
   GLuint _displayLists;
   std::set<GModel*> _hiddenModels;
   std::set<PView*> _hiddenViews;
+  std::vector<GLfloat> _bgImage;
+  int _bgImageSize[2];
 
  public:
   double r[3]; // current Euler angles (in degrees!) 
diff --git a/Graphics/gl2gif.cpp b/Graphics/gl2gif.cpp
index 2d7f15323bbb08392cfcf6c84fb827382e2fc7bf..7f94fa6b4115d1bf2cd362ccecda4b8cb17c875f 100644
--- a/Graphics/gl2gif.cpp
+++ b/Graphics/gl2gif.cpp
@@ -1164,9 +1164,9 @@ void create_gif(FILE * outfile, PixelBuffer *buffer,
   register long sr = 0, sg = 0, sb = 0, err = 0;
   int fs_direction = 0;
 
-  int width = buffer->GetWidth();
-  int height = buffer->GetHeight();
-  int numcomp = buffer->GetNumComp();
+  int width = buffer->getWidth();
+  int height = buffer->getHeight();
+  int numcomp = buffer->getNumComp();
 
   if(numcomp != 3){
     Msg::Error("GIF only implemented for GL_RGB");
@@ -1177,7 +1177,7 @@ void create_gif(FILE * outfile, PixelBuffer *buffer,
   for(i = 0; i < height; i++)
     static_pixels[i] = (pixel *) Malloc(3 * width * sizeof(pixel));
 
-  unsigned char *pixels = (unsigned char*)buffer->GetPixels();
+  unsigned char *pixels = (unsigned char*)buffer->getPixels();
   for(i = 0; i < height; i++)
     for(j = 0; j < width; j++)
       PPM_ASSIGN(static_pixels[height - 1 - i][j],
diff --git a/Graphics/gl2jpeg.cpp b/Graphics/gl2jpeg.cpp
index 9db30b79fb8077989751ba49826712e2fb747d13..190796d6be061546606c08609646bd82b8a3ff58 100644
--- a/Graphics/gl2jpeg.cpp
+++ b/Graphics/gl2jpeg.cpp
@@ -43,7 +43,7 @@ static void my_output_message(j_common_ptr cinfo)
 
 void create_jpeg(FILE *outfile, PixelBuffer *buffer, int quality, int smoothing)
 {
-  if(buffer->GetFormat() != GL_RGB || buffer->GetType() != GL_UNSIGNED_BYTE){
+  if(buffer->getFormat() != GL_RGB || buffer->getType() != GL_UNSIGNED_BYTE){
     Msg::Error("JPEG only implemented for GL_RGB and GL_UNSIGNED_BYTE");
     return;
   }
@@ -55,8 +55,8 @@ void create_jpeg(FILE *outfile, PixelBuffer *buffer, int quality, int smoothing)
 
   jpeg_create_compress(&cinfo);
   jpeg_stdio_dest(&cinfo, outfile);
-  cinfo.image_width = buffer->GetWidth();
-  cinfo.image_height = buffer->GetHeight();
+  cinfo.image_width = buffer->getWidth();
+  cinfo.image_height = buffer->getHeight();
   cinfo.input_components = 3;
   cinfo.in_color_space = JCS_RGB;
   jpeg_set_defaults(&cinfo);
@@ -65,7 +65,7 @@ void create_jpeg(FILE *outfile, PixelBuffer *buffer, int quality, int smoothing)
   cinfo.smoothing_factor = smoothing;
   jpeg_start_compress(&cinfo, TRUE);
 
-  unsigned char *pixels = (unsigned char*)buffer->GetPixels();
+  unsigned char *pixels = (unsigned char*)buffer->getPixels();
   JSAMPROW row_pointer[1]; 
   int row_stride = cinfo.image_width * cinfo.input_components;
   int i = cinfo.image_height - 1;
diff --git a/Graphics/gl2png.cpp b/Graphics/gl2png.cpp
index 2af7102195786874dc5568d30f6a33f4bef22938..e462fe5fee4e1a17e357925bdb5632dcc08a7b92 100644
--- a/Graphics/gl2png.cpp
+++ b/Graphics/gl2png.cpp
@@ -23,8 +23,8 @@ void create_png(FILE *file, PixelBuffer *buffer, int quality)
 
 void create_png(FILE *file, PixelBuffer *buffer, int quality)
 {
-  if((buffer->GetFormat() != GL_RGB && buffer->GetFormat() != GL_RGBA) ||
-     buffer->GetType() != GL_UNSIGNED_BYTE){
+  if((buffer->getFormat() != GL_RGB && buffer->getFormat() != GL_RGBA) ||
+     buffer->getType() != GL_UNSIGNED_BYTE){
     Msg::Error("PNG only implemented for GL_RGB/GL_RGBA and GL_UNSIGNED_BYTE");
     return;
   }
@@ -52,9 +52,9 @@ void create_png(FILE *file, PixelBuffer *buffer, int quality)
   
   png_init_io(png_ptr, file);
   
-  int height = buffer->GetHeight();
-  int width = buffer->GetWidth();
-  int numcomp = buffer->GetNumComp();
+  int height = buffer->getHeight();
+  int width = buffer->getWidth();
+  int numcomp = buffer->getNumComp();
 
   // Z_DEFAULT_COMPRESSION, Z_BEST_SPEED, Z_BEST_COMPRESSION, Z_NO_COMPRESSION
   png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
@@ -73,7 +73,7 @@ void create_png(FILE *file, PixelBuffer *buffer, int quality)
   png_set_text(png_ptr, info_ptr, text_ptr, 2);
   png_write_info(png_ptr, info_ptr);
 
-  unsigned char *pixels = (unsigned char *)buffer->GetPixels();
+  unsigned char *pixels = (unsigned char *)buffer->getPixels();
   for(int row = height - 1; row >= 0; row--) {
     unsigned char *row_pointer = &pixels[row * width * numcomp];
     png_write_row(png_ptr, (png_bytep)row_pointer);
diff --git a/Graphics/gl2ppm.cpp b/Graphics/gl2ppm.cpp
index df67b697744b5b12b18b7cf7b9bfae214d5a53d3..0a52c5b753e759018ccf5af46518388beb3f894b 100644
--- a/Graphics/gl2ppm.cpp
+++ b/Graphics/gl2ppm.cpp
@@ -7,14 +7,14 @@
 
 void create_ppm(FILE *outfile, PixelBuffer *buffer)
 {
-  if(buffer->GetFormat() != GL_RGB || buffer->GetType() != GL_UNSIGNED_BYTE){
+  if(buffer->getFormat() != GL_RGB || buffer->getType() != GL_UNSIGNED_BYTE){
     Msg::Error("PPM only implemented for GL_RGB and GL_UNSIGNED_BYTE");
     return;
   }
 
-  int width = buffer->GetWidth();
-  int height = buffer->GetHeight();
-  unsigned char *pixels = (unsigned char*)buffer->GetPixels();
+  int width = buffer->getWidth();
+  int height = buffer->getHeight();
+  unsigned char *pixels = (unsigned char*)buffer->getPixels();
 
   fprintf(outfile, "P6\n");
   fprintf(outfile, "%d %d\n", width, height);
diff --git a/Graphics/gl2ps.cpp b/Graphics/gl2ps.cpp
index da66f4903348186b25ef61dea4383b82e3e8fe07..86f632117b9e2983f54dd8c82bd186ce2e77bd83 100644
--- a/Graphics/gl2ps.cpp
+++ b/Graphics/gl2ps.cpp
@@ -1465,7 +1465,8 @@ static GLint gl2psFindRoot(GL2PSlist *primitives, GL2PSprimitive **root)
   }
 }
 
-static void gl2psFreeImagemap(GL2PSimagemap *list){
+static void gl2psFreeImagemap(GL2PSimagemap *list)
+{
   GL2PSimagemap *next;
   while(list != NULL){
     next = list->next;
@@ -5028,7 +5029,7 @@ static void gl2psPrintSVGPixmap(GLfloat x, GLfloat y, GL2PSimage *pixmap)
   gl2psPrintf("\"/>\n");
   gl2psListDelete(png);
 #else
-  gl2psMsg(GL2PS_WARNING, "GL2PS has to be compiled with PNG support in "
+  gl2psMsg(GL2PS_WARNING, "GL2PS must be compiled with PNG support in "
            "order to embed images in SVG streams");
 #endif
 }
diff --git a/Graphics/gl2ps.h b/Graphics/gl2ps.h
index 20b1caf831bcfaa134cad91e3ab0ad03f560ef67..c9803c15b9ea6e049cb603a298f4ab5cd90b182a 100644
--- a/Graphics/gl2ps.h
+++ b/Graphics/gl2ps.h
@@ -36,6 +36,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include "GmshConfig.h"
 
 /* Define GL2PSDLL at compile time to build a Windows DLL */
 
diff --git a/Graphics/gl2yuv.cpp b/Graphics/gl2yuv.cpp
index f62caf9fdcea116b29e3d590d45d49a9b0fb7075..552882188db77f8a107cb4824180ed3cd8ffc565 100644
--- a/Graphics/gl2yuv.cpp
+++ b/Graphics/gl2yuv.cpp
@@ -34,7 +34,7 @@
 
 void create_yuv(FILE * outfile, PixelBuffer *buffer)
 {
-  if(buffer->GetFormat() != GL_RGB || buffer->GetType() != GL_UNSIGNED_BYTE){
+  if(buffer->getFormat() != GL_RGB || buffer->getType() != GL_UNSIGNED_BYTE){
     Msg::Error("YUV only implemented for GL_RGB and GL_UNSIGNED_BYTE");
     return;
   }
@@ -72,9 +72,9 @@ void create_yuv(FILE * outfile, PixelBuffer *buffer)
     first = 0;
   }
 
-  int width = buffer->GetWidth();
-  int height = buffer->GetHeight();
-  unsigned char *pixels = (unsigned char *)buffer->GetPixels();
+  int width = buffer->getWidth();
+  int height = buffer->getHeight();
+  unsigned char *pixels = (unsigned char *)buffer->getPixels();
   
   // yuv format assumes even number of rows and columns
   height -= height % 2;
diff --git a/Plugin/Evaluate.cpp b/Plugin/Evaluate.cpp
index a6fa85999469af2772a180389f0458ed09b5c023..21a166298f0e01b74569bbfd22505c2d97626e05 100644
--- a/Plugin/Evaluate.cpp
+++ b/Plugin/Evaluate.cpp
@@ -163,6 +163,7 @@ void GMSH_EvaluatePlugin::evaluate(PView *v1, List_T *list1, int nbElm1,
         double tmp[9];
         val2 = tmp;
         if(_octree->searchScalar(x[j], y[j], z[j], val2, timeStep2)){
+        //if(_octree->searchScalarWithTol(x[j], y[j], z[j], val2, timeStep2, 0, 0.1)){
           w[0] = val2[0];
         }
         else if(_octree->searchVector(x[j], y[j], z[j], val2, timeStep2)){
diff --git a/Post/OctreePost.cpp b/Post/OctreePost.cpp
index 310c05305d9e7889af8f329e8b7f15c920e6cb31..4c28078c4b04d5b50840b5983cb2f30df65ebf9b 100644
--- a/Post/OctreePost.cpp
+++ b/Post/OctreePost.cpp
@@ -417,7 +417,7 @@ bool OctreePost::searchScalarWithTol(double x, double y, double z, double *value
                                      int step, double *size, double tol)
 {
   bool a = searchScalar(x, y, z, values, step, size);
-  if(!a){
+  if(!a && tol != 0.){
     double oldtol1 = element::getTolerance();
     double oldtol2 = MElement::getTolerance();
     element::setTolerance(tol);
diff --git a/doc/TODO.txt b/doc/TODO.txt
index 19ea4e51ba64bb1071f99310c65084bfb449b125..f820dd55bdccc1226fb32d202692c9e883d72d4b 100644
--- a/doc/TODO.txt
+++ b/doc/TODO.txt
@@ -1,4 +1,8 @@
-$Id: TODO.txt,v 1.18 2009-03-01 18:27:01 geuzaine Exp $
+$Id: TODO.txt,v 1.19 2009-03-08 19:12:34 geuzaine Exp $
+
+********************************************************************
+
+Should we take embedded vertices into account in extrusions?
 
 ********************************************************************
 
@@ -164,12 +168,6 @@ disable all replacements)
 
 ********************************************************************
 
-Add a "bitmap" object in the views, e.g. to add a logo. Maybe would
-be good enough to add another mode in the "background gradient" stuff
-to display an image in the background
-
-********************************************************************
-
 keep a table (stack) with the N last file positions when add_infile()
 is called; we could then easily implement a simple (but real) UNDO
 strategy
diff --git a/doc/VERSIONS.txt b/doc/VERSIONS.txt
index 0d0df4c2574422c485274c639db9b7e1eb8eaed7..d508689debd2c95c114166eed40024ec3ae4cf78 100644
--- a/doc/VERSIONS.txt
+++ b/doc/VERSIONS.txt
@@ -1,8 +1,8 @@
-$Id: VERSIONS.txt,v 1.36 2009-02-07 17:14:49 geuzaine Exp $
+$Id: VERSIONS.txt,v 1.37 2009-03-08 19:12:34 geuzaine Exp $
 
 (?): removed GSL dependency (Gmsh now simply requires Blas and
-Lapack); new per-window visibility; fixes for string options in
-parser.
+Lapack); new per-window visibility; added support for composite window
+printing and background image; fixes for string options in parser.
 
 2.3.0 (Jan 23, 2009): major graphics and GUI code refactoring; new
 full-quad/hexa subdivision algorithm (removed Mesh.RecombineAlgo);
diff --git a/doc/texinfo/gmsh.texi b/doc/texinfo/gmsh.texi
index 4fa4750aa4a57ad5fabe1ea46dc7c4799b5a3a2d..8af6a22708fc5eaa13631dbb260fbc7a011f8016 100644
--- a/doc/texinfo/gmsh.texi
+++ b/doc/texinfo/gmsh.texi
@@ -929,7 +929,7 @@ meaning and syntax as in the C or C++ programming languages.
 Floating point expressions (or, more simply, ``expressions'') are denoted by
 the metasyntactic variable @var{expression} (remember the definition of the
 syntactic rules in @ref{Syntactic rules}), and are evaluated during the
-parsing of the data file:
+parsing of the script file:
 
 @example
 @var{expression}:
@@ -1069,11 +1069,11 @@ Colors expressions are hybrids between fixed-length braced
 
 @noindent The first case permits to use the X Windows names to refer to colors,
 e.g., @code{Red}, @code{SpringGreen}, @code{LavenderBlush3}, @dots{}
-(see @file{Common/Colors.h} in Gmsh's source tree for a complete
-list). The second case permits to define colors by using three
-expressions to specify their red, green and blue components (with values
-comprised between 0 and 255). The third case permits to define colors by
-using their red, green and blue color components as well as their alpha
+(see @file{Common/Colors.h} in the source code for a complete list). The
+second case permits to define colors by using three expressions to
+specify their red, green and blue components (with values comprised
+between 0 and 255). The third case permits to define colors by using
+their red, green and blue color components as well as their alpha
 channel. The last case permits to use the value of a @var{color-option}
 as a @var{color-expression}. The various @w{@var{color-option}s} are
 listed in @ref{Options}.
@@ -1402,7 +1402,7 @@ The following commands can be used anywhere in a Gmsh script:
 
 @item @var{string} = @var{expression};
 Creates a new expression identifier @var{string}, or affects
-@var{expression} to an existing expression identifier. Eleven expression
+@var{expression} to an existing expression identifier. Thirteen expression
 identifiers are predefined (hardcoded in Gmsh's parser):
 
 @ftable @code
@@ -1451,11 +1451,11 @@ Returns the next available surface loop number.
 
 @item newreg
 Returns the next available region number. That is, @code{newreg} returns
-the maximum of @code{newp}, @code{newl}, @code{news}, @code{newv} and
-all physical entity numbers@footnote{For compatibility purposes, the
-behavior of @code{newl}, @code{news}, @code{newv} and @code{newreg} can
-be modified with the @code{Geometry.OldNewReg} option (@pxref{Geometry
-options list}).}.
+the maximum of @code{newp}, @code{newl}, @code{news}, @code{newv},
+@code{newll}, @code{newsl} and all physical entity numbers@footnote{For
+compatibility purposes, the behavior of @code{newl}, @code{news},
+@code{newv} and @code{newreg} can be modified with the
+@code{Geometry.OldNewReg} option (@pxref{Geometry options list}).}.
 @end ftable
 
 @item @var{string} [ ] = @{ @};
@@ -1634,13 +1634,14 @@ the same time.
 @cindex Geometry, module
 @cindex Module, geometry
 
-Gmsh's geometry module provides a simple CAD engine, using a bottom-up
-(boundary representation) approach: you need to first define points (using
-the @code{Point} command: see below), then lines (using @code{Line},
-@code{Circle}, @code{Spline}, @dots{}, commands or by extruding points),
-then surfaces (using for example the @code{Plane Surface} or @code{Ruled
-Surface} commands, or by extruding lines), and finally volumes (using the
-@code{Volume} command or by extruding surfaces).
+Gmsh's geometry module provides a simple CAD engine, using a boundary
+representation (``BRep'') approach: you need to first define points
+(using the @code{Point} command: see below), then lines (using
+@code{Line}, @code{Circle}, @code{Spline}, @dots{}, commands or by
+extruding points), then surfaces (using for example the @code{Plane
+Surface} or @code{Ruled Surface} commands, or by extruding lines), and
+finally volumes (using the @code{Volume} command or by extruding
+surfaces).
 
 These geometrical entities are called ``elementary'' in Gmsh's jargon, and
 are assigned identification numbers when they are created:
@@ -1653,8 +1654,10 @@ are assigned identification numbers when they are created:
 @end enumerate
 
 @noindent Elementary geometrical entities can then be manipulated in various
-ways, for example using the @code{Translate}, @code{Rotate}, @code{Scale} or
-@code{Symmetry} commands.
+ways, for example using the @code{Translate}, @code{Rotate},
+@code{Scale} or @code{Symmetry} commands. They can be deleted with the
+@code{Delete} command, provided that no higher-dimension entity
+references them.
 
 Compound groups of elementary geometrical entities can also be defined and
 are called ``physical'' entities. These physical entities cannot be modified
@@ -2070,7 +2073,10 @@ transformation, unless @code{Geometry.AutoCoherence} is set to zero
 
 @item Delete @{ Point | Line | Surface | Volume @{ @var{expression-list} @}; @dots{} @}
 Deletes all elementary entities whose identification numbers are given
-in @var{expression-list}.
+in @var{expression-list}. If an entity is linked to another entity (for
+example, if a point is used as a control point of a curve),
+@code{Delete} has no effect (the line will have to be deleted before the
+point can).
 
 @item Hide @{ Point | Line | Surface | Volume @{ @var{expression-list} @}; @dots{} @}
 Hide the entities listed in @var{expression-list}, if
@@ -2160,13 +2166,13 @@ algorithm can be quite poor.
 If only elementary geometrical entities are defined (or if the
 @code{Mesh.SaveAll} option is set; see @ref{Mesh options list}), the
 grid produced by the mesh module will be saved ``as is''. That is, all
-the elements in the grid will be saved to disk using the identification
-number of the elementary entities they discretize as their elementary
-region number (and 0 as their physical region number@footnote{This
-behaviour was introduced in Gmsh 2.0. In older versions, both the
-elementary and the physical region numbers would be set to the
-identification number of the elementary region.}; @ref{File
-formats}). This can sometimes be inconvenient:
+the elements in the grid will be saved using the identification number
+of the elementary entities they discretize as their elementary region
+number (and 0 as their physical region number@footnote{This behaviour
+was introduced in Gmsh 2.0. In older versions, both the elementary and
+the physical region numbers would be set to the identification number of
+the elementary region.}; @ref{File formats}). This can sometimes be
+inconvenient:
 
 @itemize @bullet
 @item
diff --git a/doc/texinfo/opt_general.texi b/doc/texinfo/opt_general.texi
index aa181881850c8170356f6e9abaa50dc58e5014a6..02e8bb1c45915fafa3a6b42e765cf062772d9877 100644
--- a/doc/texinfo/opt_general.texi
+++ b/doc/texinfo/opt_general.texi
@@ -34,6 +34,11 @@ Z-axis label@*
 Default value: @code{""}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.BackgroundImageFileName
+Background image file in JPEG or PNG format@*
+Default value: @code{""}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.DefaultFileName
 Default project file name@*
 Default value: @code{"untitled.geo"}@*
@@ -179,6 +184,16 @@ Draw background gradient (0=none, 1=vertical, 2=horizontal, 3=radial)@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.BackgroundImagePositionX
+X position (in pixels) of background image (< 0: right alignment; > 1e5: centered)@*
+Default value: @code{100000}@*
+Saved in: @code{General.OptionsFileName}
+
+@item General.BackgroundImagePositionY
+Y position (in pixels) of background image (< 0: right alignment; > 1e5: centered)@*
+Default value: @code{100000}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Clip0A
 First coefficient in equation for clipping plane 0 (`A' in `AX+BY+CZ+D=0')@*
 Default value: @code{1}@*
@@ -201,12 +216,12 @@ Saved in: @code{-}
 
 @item General.Clip1A
 First coefficient in equation for clipping plane 1@*
-Default value: @code{1}@*
+Default value: @code{0}@*
 Saved in: @code{-}
 
 @item General.Clip1B
 Second coefficient in equation for clipping plane 1@*
-Default value: @code{0}@*
+Default value: @code{1}@*
 Saved in: @code{-}
 
 @item General.Clip1C
@@ -221,7 +236,7 @@ Saved in: @code{-}
 
 @item General.Clip2A
 First coefficient in equation for clipping plane 2@*
-Default value: @code{1}@*
+Default value: @code{0}@*
 Saved in: @code{-}
 
 @item General.Clip2B
@@ -231,7 +246,7 @@ Saved in: @code{-}
 
 @item General.Clip2C
 Third coefficient in equation for clipping plane 2@*
-Default value: @code{0}@*
+Default value: @code{1}@*
 Saved in: @code{-}
 
 @item General.Clip2D
@@ -241,7 +256,7 @@ Saved in: @code{-}
 
 @item General.Clip3A
 First coefficient in equation for clipping plane 3@*
-Default value: @code{1}@*
+Default value: @code{-1}@*
 Saved in: @code{-}
 
 @item General.Clip3B
@@ -261,12 +276,12 @@ Saved in: @code{-}
 
 @item General.Clip4A
 First coefficient in equation for clipping plane 4@*
-Default value: @code{1}@*
+Default value: @code{0}@*
 Saved in: @code{-}
 
 @item General.Clip4B
 Second coefficient in equation for clipping plane 4@*
-Default value: @code{0}@*
+Default value: @code{-1}@*
 Saved in: @code{-}
 
 @item General.Clip4C
@@ -281,7 +296,7 @@ Saved in: @code{-}
 
 @item General.Clip5A
 First coefficient in equation for clipping plane 5@*
-Default value: @code{1}@*
+Default value: @code{0}@*
 Saved in: @code{-}
 
 @item General.Clip5B
@@ -291,7 +306,7 @@ Saved in: @code{-}
 
 @item General.Clip5C
 Third coefficient in equation for clipping plane 5@*
-Default value: @code{0}@*
+Default value: @code{-1}@*
 Saved in: @code{-}
 
 @item General.Clip5D
@@ -820,12 +835,12 @@ Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
 @item General.SmallAxesPositionX
-X position of small axes (use negative values for right alignment)@*
+X position (in pixels) of small axes (< 0: right alignment; > 1e5: centered)@*
 Default value: @code{-60}@*
 Saved in: @code{General.OptionsFileName}
 
 @item General.SmallAxesPositionY
-Y position of small axes (use negative values for bottom alignment)@*
+Y position (in pixels) of small axes (< 0: right alignment; > 1e5: centered)@*
 Default value: @code{-40}@*
 Saved in: @code{General.OptionsFileName}
 
diff --git a/doc/texinfo/opt_print.texi b/doc/texinfo/opt_print.texi
index 30e6e046a6ac040378a892a2e2673e5e476791d4..baa3295eb9c2953df9e5ff365874f7a79db6a4f2 100644
--- a/doc/texinfo/opt_print.texi
+++ b/doc/texinfo/opt_print.texi
@@ -4,6 +4,11 @@
 @c
 
 @ftable @code
+@item Print.CompositeWindows
+Composite all window tiles in the same output image (for bitmap output only)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item Print.EpsBackground
 Save image background in PostScript/PDF output@*
 Default value: @code{1}@*
diff --git a/doc/texinfo/opt_view.texi b/doc/texinfo/opt_view.texi
index 899bd12bff6f3b7a7fdf0b8993cf146b29b0d083..4eab4d51210f38e3e2bcad3cb24ccc1d87c8751c 100644
--- a/doc/texinfo/opt_view.texi
+++ b/doc/texinfo/opt_view.texi
@@ -495,12 +495,12 @@ Default value: @code{0}@*
 Saved in: @code{General.OptionsFileName}
 
 @item View.PositionX
-Horizontal position (in pixels) of the upper left corner of the scale or 2D plot@*
+X position (in pixels) of the scale or 2D plot (< 0: right alignment; > 1e5: centered)@*
 Default value: @code{100}@*
 Saved in: @code{General.OptionsFileName}
 
 @item View.PositionY
-Vertical position (in pixels) of the upper left corner of the scale or 2D plot@*
+Y position (in pixels) of the scale or 2D plot (< 0: right alignment; > 1e5: centered)@*
 Default value: @code{50}@*
 Saved in: @code{General.OptionsFileName}