changeset 17383:f983570ba8b8

Merged with main, after adding temporary directory in latex_render.
author Andrej Lojdl <andrej.lojdl@gmail.com>
date Wed, 04 Sep 2013 18:40:34 +0200
parents dc103ce7c8cf (current diff) 3e95b22f5287 (diff)
children 609d93683f15
files
diffstat 11 files changed, 582 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS
+++ b/NEWS
@@ -128,6 +128,25 @@
     expression would have searched for a literal '\' followed by 't' and
     replaced the two characters with the sequence '\', 'n'.
 
+ ** A TeX parser has been implemented for the FLTK toolkit and is the default
+    for any text object including titles and axis labels.  The TeX parser is
+    supported only for display on a monitor, not for printing.
+    A quick summary of features:
+
+    Code         Feature     Example             Comment
+    _            subscript   H_2O                formula for water
+    ^            exponent    y=x^2               formula for parabola
+    \char        symbol      \beta               Greek symbol beta
+    \fontname    font        \fontname{Arial}    set Arial font
+    \fontsize    fontsize    \fontsize{16}       set fontsize 16
+    \color[rgb]  fontcolor   \color[rgb]{1 0 1}  set magenta color 
+    \bf          bold        \bfBold Text        bold font
+    \it          italic      \itItalic Text      italic font
+    \sl          slanted     \slOblique Text     slanted font
+    \rm          normal      \bfBold\rmNormal    normal font
+    {}           group       {\bf Bold}Normal    group objects
+                             e^{i*\pi} = -1      complex example
+
  ** The m-files in the image directory have been overhauled.
 
     The principal benefit is that Octave will now no longer automatically
--- a/doc/interpreter/contributors.in
+++ b/doc/interpreter/contributors.in
@@ -42,9 +42,9 @@
 Michael Creel
 Jeff Cunningham
 Martin Dalecki
+Jacob Dawid
 Jorge Barros de Abreu
 Carlo de Falco
-Jacob Dawid
 Thomas D. Dean
 Philippe Defert
 Bill Denney
@@ -161,21 +161,21 @@
 Massimo Lorenzin
 Emil Lucretiu
 Hoxide Ma
+Colin Macdonald
 James Macnicol
 Jens-Uwe Mager
-Colin Macdonald
 Rob Mahurin
+Alexander Mamonov
 Ricardo Marranita
 Orestes Mas
 Axel Mathéi
 Makoto Matsumoto
 Tatsuro Matsuoka
+Christoph Mayer
 Laurent Mazet
 G. D. McBain
-Alexander Mamonov
-Christoph Mayer
+Ronald van der Meer
 Júlio Hoffimann Mendes
-Ronald van der Meer
 Thorsten Meyer
 Petr Mikulik
 Mike Miller
@@ -229,6 +229,7 @@
 E. Joshua Rigler
 Petter Risholm
 Matthew W. Roberts
+Peter Rosin
 Andrew Ross
 Fabio Rossi
 Mark van Rossum
@@ -273,25 +274,25 @@
 Ivan Sutoris
 John Swensen
 Daisuke Takago
+Ariel Tankus
 Falk Tannhäuser
-Ariel Tankus
+Duncan Temple Lang
 Matthew Tenny
+Kris Thielemans
 Georg Thimm
-Duncan Temple Lang
-Kris Thielemans
 Olaf Till
 Christophe Tournery
 Thomas Treichl
 Karsten Trulsen
 Frederick Umminger
 Utkarsh Upadhyay
-Daniel Wagenaar
 Stefan van der Walt
 Peter Van Wieren
 James R. Van Zandt
 Risto Vanhanen
 Gregory Vanuxem
 Ivana Varekova
+Daniel Wagenaar
 Thomas Walter
 Andreas Weber
 Olaf Weber
--- a/doc/interpreter/signal.txi
+++ b/doc/interpreter/signal.txi
@@ -19,18 +19,23 @@
 @node Signal Processing
 @chapter Signal Processing
 
-
 This chapter describes the signal processing and fast Fourier
 transform functions available in Octave.  Fast Fourier transforms are
 computed with the @sc{fftw} or @sc{fftpack} libraries depending on how
 Octave is built.
- 
-
-
-@DOCSTRING(detrend)
 
 @DOCSTRING(fft)
 
+@DOCSTRING(ifft)
+
+@DOCSTRING(fft2)
+
+@DOCSTRING(ifft2)
+
+@DOCSTRING(fftn)
+
+@DOCSTRING(ifftn)
+
 Octave uses the @sc{fftw} libraries to perform FFT computations.  When Octave
 starts up and initializes the @sc{fftw} libraries, they read a system wide
 file (on a Unix system, it is typically @file{/etc/fftw/wisdom}) that
@@ -45,16 +50,6 @@
 
 @DOCSTRING(fftw)
 
-@DOCSTRING(ifft)
-
-@DOCSTRING(fft2)
-
-@DOCSTRING(ifft2)
-
-@DOCSTRING(fftn)
-
-@DOCSTRING(ifftn)
-
 @DOCSTRING(fftconv)
 
 @DOCSTRING(fftfilt)
@@ -71,7 +66,7 @@
 
 @DOCSTRING(unwrap)
 
-@c FIXME -- someone needs to organize these...
+@c FIXME: someone needs to organize these ...
 
 @DOCSTRING(arch_fit)
 
@@ -87,6 +82,8 @@
 
 @DOCSTRING(blackman)
 
+@DOCSTRING(detrend)
+
 @DOCSTRING(diffpara)
 
 @DOCSTRING(durbinlevinson)
@@ -107,10 +104,6 @@
 
 @DOCSTRING(periodogram)
 
-@DOCSTRING(rectangle_lw)
-
-@DOCSTRING(rectangle_sw)
-
 @DOCSTRING(sinetone)
 
 @DOCSTRING(sinewave)
@@ -125,8 +118,5 @@
 
 @DOCSTRING(synthesis)
 
-@DOCSTRING(triangle_lw)
+@DOCSTRING(yulewalker)
 
-@DOCSTRING(triangle_sw)
-
-@DOCSTRING(yulewalker)
--- a/libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp
+++ b/libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp
@@ -96,13 +96,15 @@
 
 static QString translateKey (QKeyEvent *ev)
 {
+  QString esc = "\x1b";
   QString s;
 
-  if (!ev->text ().isEmpty ())
+  if (ev->key () == Qt::Key_Delete)
+    s = esc + "[C\b";
+  else if (!ev->text ().isEmpty ())
     s = ev->text ();
   else
     {
-      QString esc = "\x1b";
 
       switch (ev->key ())
         {
@@ -123,21 +125,17 @@
           break;
 
         case Qt::Key_Home:
-          s = esc + "[1~";
+          s = esc + "[H";
           break;
 
         case Qt::Key_End:
-          s = esc + "[4~";
+          s = esc + "[F";
           break;
 
         case Qt::Key_Insert:
           s = esc + "[2~";
           break;
 
-        case Qt::Key_Delete:
-          s = esc + "[3~";
-          break;
-
         case Qt::Key_PageUp:
           s = esc + "[5~";
           break;
@@ -146,6 +144,10 @@
           s = esc + "[6~";
           break;
 
+        case Qt::Key_Escape:
+          s = esc;
+          break;
+
         default:
           break;
         }
--- a/libgui/src/octave-gui.cc
+++ b/libgui/src/octave-gui.cc
@@ -123,6 +123,11 @@
 
           if (term.empty ())
             octave_env::putenv ("TERM", "xterm");
+#else
+          std::string term = octave_env::getenv ("TERM");
+
+          if (term.empty ())
+            octave_env::putenv ("TERM", "cygwin");
 #endif
 
           // create main window, read settings, and show window
--- a/libinterp/dldfcn/__init_fltk__.cc
+++ b/libinterp/dldfcn/__init_fltk__.cc
@@ -1392,7 +1392,7 @@
 
                   // Determine if we're zooming in or out.
                   const double factor =
-                    (Fl::event_dy () > 0) ? 1.0 + Vwheel_zoom_speed
+                    (Fl::event_dy () > 0) ? 1 / (1.0 - Vwheel_zoom_speed)
                                           : 1.0 - Vwheel_zoom_speed;
 
                   // Get the point we're zooming about.
--- a/libinterp/dldfcn/__magick_read__.cc
+++ b/libinterp/dldfcn/__magick_read__.cc
@@ -1481,14 +1481,21 @@
         return octave_value ("rle");
       case Magick::ZipCompression:
         return octave_value ("deflate");
-      case Magick::LZMACompression:
-        return octave_value ("lzma");
-      case Magick::JPEG2000Compression:
-        return octave_value ("jpeg2000");
-      case Magick::JBIG1Compression:
-        return octave_value ("jbig1");
-      case Magick::JBIG2Compression:
-        return octave_value ("jbig2");
+
+      // The following are present only in recent versions of GraphicsMagick.
+      // At the moment the only use of this would be to have imfinfo report
+      // the compression method. In the future, someone could implement
+      // the Compression option for imwrite in which case a macro in
+      // configure.ac will have to check for their presence of this.
+      // See bug #39913
+//      case Magick::LZMACompression:
+//        return octave_value ("lzma");
+//      case Magick::JPEG2000Compression:
+//        return octave_value ("jpeg2000");
+//      case Magick::JBIG1Compression:
+//        return octave_value ("jbig1");
+//      case Magick::JBIG2Compression:
+//        return octave_value ("jbig2");
       default:
         return octave_value ("undefined");
     }
@@ -1549,9 +1556,9 @@
     }
 }
 
-// We return a map so this can be used both in imwrite and imfinfo.
+// Meant to be shared with both imfinfo and imwrite.
 static std::map<octave_idx_type, std::string>
-disposal_methods ()
+init_disposal_methods ()
 {
   //  GIF Specifications:
   //
@@ -1578,6 +1585,72 @@
     }
   return methods;
 }
+
+static bool
+is_valid_exif (const std::string& val)
+{
+  // Sometimes GM will return the string "unknown" instead of empty
+  // for an empty value.
+  return (! val.empty () && val != "unknown");
+}
+
+static void
+fill_exif (octave_scalar_map& map, Magick::Image& img,
+           const std::string& key)
+{
+  const std::string attr = img.attribute ("EXIF:" + key);
+  if (is_valid_exif (attr))
+    map.setfield (key, octave_value (attr));
+  return;
+}
+
+static void
+fill_exif_ints (octave_scalar_map& map, Magick::Image& img,
+                const std::string& key)
+{
+  const std::string attr = img.attribute ("EXIF:" + key);
+  if (is_valid_exif (attr))
+    {
+      // string of the type "float,float,float....."
+      float number;
+      ColumnVector values (std::count (attr.begin (), attr.end (), ',') +1);
+      std::string sub;
+      std::istringstream sstream (attr);
+      octave_idx_type n = 0;
+      while (std::getline (sstream, sub, char (',')))
+        {
+          sscanf (sub.c_str (), "%f", &number);
+          values(n++) = number;
+        }
+      map.setfield (key, octave_value (values));
+    }
+  return;
+}
+
+static void
+fill_exif_floats (octave_scalar_map& map, Magick::Image& img,
+                  const std::string& key)
+{
+  const std::string attr = img.attribute ("EXIF:" + key);
+  if (is_valid_exif (attr))
+    {
+      // string of the type "int/int,int/int,int/int....."
+      int numerator;
+      int denominator;
+      ColumnVector values (std::count (attr.begin (), attr.end (), ',') +1);
+      std::string sub;
+      std::istringstream sstream (attr);
+      octave_idx_type n = 0;
+      while (std::getline (sstream, sub, ','))
+        {
+          sscanf (sub.c_str (), "%i/%i", &numerator, &denominator);
+          values(n++) = double (numerator) / double (denominator);
+        }
+      map.setfield (key, octave_value (values));
+    }
+  return;
+}
+
 #endif
 
 DEFUN_DLD (__magick_finfo__, args, ,
@@ -1609,8 +1682,27 @@
   read_file (filename, imvec);
   if (error_state)
     return retval;
+  const octave_idx_type nFrames = imvec.size ();
+  const std::string format = imvec[0].magick ();
 
-  // Matlab has different list of fields for each file format. We don't.
+  // Here's how this function works. We need to return a struct array, one
+  // struct for each image in the file (remember, there are image
+  // that allow for multiple images in the same file). Now, Matlab seems
+  // to have format specific code so the fields on the struct are different
+  // for each format. It only has a small subset that is common to all
+  // of them, the others are undocumented. Because we try to abstract from
+  // the formats we always return the same list of fields (note that with
+  // GM we support more than 88 formats. That's way more than Matlab, and
+  // I don't want to write specific code for each of them).
+  //
+  // So what we do is we create an octave_scalar_map, fill it with the
+  // information for that image, and then insert it into an octave_map.
+  // Because in the same file, different images may have values for
+  // different fields, we can't create a field only if there's a value.
+  // Bad things happen if we merge octave_scalar_maps with different
+  // fields from the others (suppose for example a TIFF file with 4 images,
+  // where only the third image has a colormap.
+
   static const char *fields[] =
     {
       // These are fields that must always appear for Matlab.
@@ -1626,7 +1718,7 @@
 
       // These are format specific or not existent in Matlab. The most
       // annoying thing is that Matlab may have different names for the
-      // same thing, in different formats.
+      // same thing in different formats.
       "DelayTime",
       "DisposalMethod",
       "LoopCount",
@@ -1641,29 +1733,33 @@
       "ResolutionUnit",
       "XResolution",
       "YResolution",
+      "Software",           // sometimes is an Exif tag
+      "Make",               // actually an Exif tag
+      "Model",              // actually an Exif tag
+      "DateTime",           // actually an Exif tag
+      "ImageDescription",   // actually an Exif tag
+      "Artist",             // actually an Exif tag
+      "Copyright",          // actually an Exif tag
+      "DigitalCamera",
+      "GPSInfo",
+      // Notes for the future: GM allows to get many attributes, and even has
+      // attribute() to obtain arbitrary ones, that may exist in only some
+      // cases. The following is a list of some methods and into what possible
+      // Matlab compatible values they may be converted.
+      //
+      //  colorSpace()      -> PhotometricInterpretation
+      //  backgroundColor() -> BackgroundColor
+      //  interlaceType()   -> Interlaced, InterlaceType, and PlanarConfiguration
+      //  label()           -> Title
       0
     };
 
-  // Notes for the future: GM allows to get many attributes, and even has
-  // attribute() to obtain arbitrary ones, that may be set in only some
-  // cases. The following is a list of some methods and into what possible
-  // Matlab value they may be converted.
-  //
-  //  colorSpace()      -> PhotometricInterpretation
-  //  backgroundColor() -> BackgroundColor
-  //  interlaceType()   -> Interlaced, InterlaceType, and PlanarConfiguration
-  //  label()           -> Title
-
-  // Create the right size for the output.
-  const octave_idx_type nFrames = imvec.size ();
+  // The one we will return at the end
   octave_map info (dim_vector (nFrames, 1), string_vector (fields));
 
-  const std::string format (imvec[0].magick ());
-  // For each frame in the image (some images contain multiple
-  // layers, each to be treated like a separate image). So we create
-  // octave_scalar_map and insert them in the octave_map during the
-  // loop.  Since some fields will never change value, we set the
-  // template
+  // Some of the fields in the struct are about file information and will be
+  // the same for all images in the file. So we create a template, fill in
+  // those values, and make a copy of the template for each image.
   octave_scalar_map template_info = (string_vector (fields));
 
   template_info.setfield ("Format", octave_value (format));
@@ -1686,8 +1782,6 @@
       return retval;
     }
 
-  std::map<octave_idx_type, std::string> gif_methods = disposal_methods ();
-
   for (octave_idx_type frame = 0; frame < nFrames; frame++)
     {
       octave_scalar_map info_frame (template_info);
@@ -1738,14 +1832,13 @@
                   color_type = "undefined";
               }
           }
-        info_frame.setfield ("ColorType",           octave_value (color_type));
-        info_frame.setfield ("Colormap",            octave_value (cmap));
+        info_frame.setfield ("ColorType", octave_value (color_type));
+        info_frame.setfield ("Colormap",  octave_value (cmap));
       }
 
-      info_frame.setfield ("Gamma",     octave_value (img.gamma ()));
       {
         // Not all images have chroma values. In such cases, they'll
-        // be all zeros. SO rather than send a matrix of zeros, we will
+        // be all zeros. So rather than send a matrix of zeros, we will
         // check for that, and send an empty vector instead.
         RowVector chromaticities (8);
         double* chroma_fvec = chromaticities.fortran_vec ();
@@ -1758,6 +1851,7 @@
         info_frame.setfield ("Chromaticities", octave_value (chromaticities));
       }
 
+      info_frame.setfield ("Gamma",         octave_value (img.gamma ()));
       info_frame.setfield ("XResolution",   octave_value (img.xResolution ()));
       info_frame.setfield ("YResolution",   octave_value (img.yResolution ()));
       info_frame.setfield ("DelayTime",     octave_value (img.animationDelay ()));
@@ -1765,10 +1859,6 @@
       info_frame.setfield ("Quality",       octave_value (img.quality ()));
       info_frame.setfield ("Comment",       octave_value (img.comment ()));
 
-      info_frame.setfield ("DisposalMethod",
-        octave_value (format == "GIF"?
-                      gif_methods[img.gifDisposeMethod ()] : ""));
-
       info_frame.setfield ("Compression",
         magick_to_octave_value (img.compressType ()));
       info_frame.setfield ("Orientation",
@@ -1778,8 +1868,227 @@
       info_frame.setfield ("ByteOrder",
         magick_to_octave_value (img.endian ()));
 
+      // It is not possible to know if there's an Exif field so we just
+      // check for the Exif Version value. If it does exists, then we
+      // bother about looking for specific fields.
+      {
+        Magick::Image& cimg = const_cast<Magick::Image&> (img);
+
+        // These will be in Exif tags but must appear as fields in the
+        // base struct array, not as another struct in one of its fields.
+        // This is likely because they belong to the Baseline TIFF specs
+        // and may appear out of the Exif tag. So first we check if it
+        // exists outside the Exif tag.
+        // See Section 4.6.4, table 4, page 28 of Exif specs version 2.3
+        // (CIPA DC- 008-Translation- 2010)
+        static const char *base_exif_str_fields[] = {
+          "DateTime",
+          "ImageDescription",
+          "Make",
+          "Model",
+          "Software",
+          "Artist",
+          "Copyright",
+          0,
+        };
+        static const string_vector base_exif_str (base_exif_str_fields);
+        static const octave_idx_type n_base_exif_str = base_exif_str.numel ();
+        for (octave_idx_type field = 0; field < n_base_exif_str; field++)
+          {
+            info_frame.setfield (base_exif_str[field],
+              octave_value (cimg.attribute (base_exif_str[field])));
+            fill_exif (info_frame, cimg, base_exif_str[field]);
+          }
+
+        octave_scalar_map camera;
+        octave_scalar_map gps;
+        if (! cimg.attribute ("EXIF:ExifVersion").empty ())
+          {
+            // See Section 4.6.5, table 7 and 8, over pages page 42 to 43
+            // of Exif specs version 2.3 (CIPA DC- 008-Translation- 2010)
+
+            // Listed on the Exif specs as being of type ASCII.
+            static const char *exif_str_fields[] = {
+              "RelatedSoundFile",
+              "DateTimeOriginal",
+              "DateTimeDigitized",
+              "SubSecTime",
+              "DateTimeOriginal",
+              "SubSecTimeOriginal",
+              "SubSecTimeDigitized",
+              "ImageUniqueID",
+              "CameraOwnerName",
+              "BodySerialNumber",
+              "LensMake",
+              "LensModel",
+              "LensSerialNumber",
+              "SpectralSensitivity",
+              // These last two are of type undefined but most likely will
+              // be strings. Even if they're not GM returns a string anyway.
+              "UserComment",
+              "MakerComment",
+              0
+            };
+            static const string_vector exif_str (exif_str_fields);
+            static const octave_idx_type n_exif_str = exif_str.numel ();
+            for (octave_idx_type field = 0; field < n_exif_str; field++)
+              fill_exif (camera, cimg, exif_str[field]);
+
+            // Listed on the Exif specs as being of type SHORT or LONG.
+            static const char *exif_int_fields[] = {
+              "ColorSpace",
+              "ExifImageWidth",  // PixelXDimension (CPixelXDimension in Matlab)
+              "ExifImageHeight", // PixelYDimension (CPixelYDimension in Matlab)
+              "PhotographicSensitivity",
+              "StandardOutputSensitivity",
+              "RecommendedExposureIndex",
+              "ISOSpeed",
+              "ISOSpeedLatitudeyyy",
+              "ISOSpeedLatitudezzz",
+              "FocalPlaneResolutionUnit",
+              "FocalLengthIn35mmFilm",
+              // Listed as SHORT or LONG but with more than 1 count.
+              "SubjectArea",
+              "SubjectLocation",
+              // While the following are an integer, their value have a meaning
+              // that must be represented as a string for Matlab compatibility.
+              // For example, a 3 on ExposureProgram, would return
+              // "Aperture priority" as defined on the Exif specs.
+              "ExposureProgram",
+              "SensitivityType",
+              "MeteringMode",
+              "LightSource",
+              "Flash",
+              "SensingMethod",
+              "FileSource",
+              "CustomRendered",
+              "ExposureMode",
+              "WhiteBalance",
+              "SceneCaptureType",
+              "GainControl",
+              "Contrast",
+              "Saturation",
+              "Sharpness",
+              "SubjectDistanceRange",
+              0
+            };
+            static const string_vector exif_int (exif_int_fields);
+            static const octave_idx_type n_exif_int = exif_int.numel ();
+            for (octave_idx_type field = 0; field < n_exif_int; field++)
+              fill_exif_ints (camera, cimg, exif_int[field]);
+
+            // Listed as RATIONAL or SRATIONAL
+            static const char *exif_float_fields[] = {
+              "Gamma",
+              "CompressedBitsPerPixel",
+              "ExposureTime",
+              "FNumber",
+              "ShutterSpeedValue",  // SRATIONAL
+              "ApertureValue",
+              "BrightnessValue",    // SRATIONAL
+              "ExposureBiasValue",  // SRATIONAL
+              "MaxApertureValue",
+              "SubjectDistance",
+              "FocalLength",
+              "FlashEnergy",
+              "FocalPlaneXResolution",
+              "FocalPlaneYResolution",
+              "ExposureIndex",
+              "DigitalZoomRatio",
+              // Listed as RATIONAL or SRATIONAL with more than 1 count.
+              "LensSpecification",
+              0
+            };
+            static const string_vector exif_float (exif_float_fields);
+            static const octave_idx_type n_exif_float = exif_float.numel ();
+            for (octave_idx_type field = 0; field < n_exif_float; field++)
+              fill_exif_floats (camera, cimg, exif_float[field]);
+
+            // Inside a Exif field, it is possible that there is also a
+            // GPS field. This is not the same as ExifVersion but seems
+            // to be how we have to check for it.
+            if (cimg.attribute ("EXIF:GPSInfo") != "unknown")
+              {
+                // The story here is the same as with Exif.
+                // See Section 4.6.6, table 15 on page 68 of Exif specs
+                // version 2.3 (CIPA DC- 008-Translation- 2010)
+
+                static const char *gps_str_fields[] = {
+                  "GPSLatitudeRef",
+                  "GPSLongitudeRef",
+                  "GPSAltitudeRef",
+                  "GPSSatellites",
+                  "GPSStatus",
+                  "GPSMeasureMode",
+                  "GPSSpeedRef",
+                  "GPSTrackRef",
+                  "GPSImgDirectionRef",
+                  "GPSMapDatum",
+                  "GPSDestLatitudeRef",
+                  "GPSDestLongitudeRef",
+                  "GPSDestBearingRef",
+                  "GPSDestDistanceRef",
+                  "GPSDateStamp",
+                  0
+                };
+                static const string_vector gps_str (gps_str_fields);
+                static const octave_idx_type n_gps_str = gps_str.numel ();
+                for (octave_idx_type field = 0; field < n_gps_str; field++)
+                  fill_exif (gps, cimg, gps_str[field]);
+
+                static const char *gps_int_fields[] = {
+                  "GPSDifferential",
+                  0
+                };
+                static const string_vector gps_int (gps_int_fields);
+                static const octave_idx_type n_gps_int = gps_int.numel ();
+                for (octave_idx_type field = 0; field < n_gps_int; field++)
+                  fill_exif_ints (gps, cimg, gps_int[field]);
+
+                static const char *gps_float_fields[] = {
+                  "GPSAltitude",
+                  "GPSDOP",
+                  "GPSSpeed",
+                  "GPSTrack",
+                  "GPSImgDirection",
+                  "GPSDestBearing",
+                  "GPSDestDistance",
+                  "GPSHPositioningError",
+                  // Listed as RATIONAL or SRATIONAL with more than 1 count.
+                  "GPSLatitude",
+                  "GPSLongitude",
+                  "GPSTimeStamp",
+                  "GPSDestLatitude",
+                  "GPSDestLongitude",
+                  0
+                };
+                static const string_vector gps_float (gps_float_fields);
+                static const octave_idx_type n_gps_float = gps_float.numel ();
+                for (octave_idx_type field = 0; field < n_gps_float; field++)
+                  fill_exif_floats (gps, cimg, gps_float[field]);
+
+              }
+          }
+        info_frame.setfield ("DigitalCamera", octave_value (camera));
+        info_frame.setfield ("GPSInfo",       octave_value (gps));
+      }
+
       info.fast_elem_insert (frame, info_frame);
     }
+
+  if (format == "GIF")
+    {
+      static std::map<octave_idx_type, std::string> disposal_methods
+        = init_disposal_methods ();
+      string_vector methods (nFrames);
+      for (octave_idx_type frame = 0; frame < nFrames; frame++)
+        methods[frame] = disposal_methods[imvec[frame].gifDisposeMethod ()];
+      info.setfield ("DisposalMethod", Cell (methods));
+    }
+  else
+    info.setfield ("DisposalMethod",
+                   Cell (dim_vector (nFrames, 1), octave_value ("")));
+
   retval = octave_value (info);
 #endif
   return retval;
--- a/scripts/help/__unimplemented__.m
+++ b/scripts/help/__unimplemented__.m
@@ -44,6 +44,10 @@
       txt = ["Basic video file support is provided in the video package.  ", ...
              "See @url{http://octave.sf.net/video/}."];
 
+    case "exifread"
+      txt = ["exifread is deprecated.  " ...
+             "The functionality is available in the imfinfo function."];
+
     case "gsvd"
       txt = ["gsvd is not currently part of core Octave.  ", ...
              "See the linear-algebra package at ", ...
@@ -207,7 +211,6 @@
   "dynamicprops",
   "echodemo",
   "evalc",
-  "exifread",
   "export2wsdlg",
   "figurepalette",
   "filebrowser",
--- a/scripts/image/imfinfo.m
+++ b/scripts/image/imfinfo.m
@@ -109,6 +109,37 @@
 ## The orientation of the image with respect to the rows and columns.  Value
 ## is an integer between 1 and 8 as defined in the TIFF 6 specifications, and
 ## for @sc{Matlab} compatibility.
+##
+## @item Software
+## Name and version of the software or firmware of the camera or image input
+## device used to generate the image.
+##
+## @item Make
+## The manufacturer of the recording equipment.  This is the manufacture of the
+## DSC, scanner, video digitizer or other equipment that generated the image.
+##
+## @item Model
+## The model name or model number of the recording equipment as mentioned
+## on the field @qcode{"Make"}.
+##
+## @item DateTime
+## The date and time of image creation as defined by the Exif standard, i.e,
+## it is the date and time the file was changed.
+##
+## @item ImageDescription
+## The title of the image as defined by the Exif standard.
+##
+## @item Artist
+## Name of the camera owner, photographer or image creator.
+##
+## @item Copyright
+## Copyright notice of the person or organization claiming rights to the image.
+##
+## @item DigitalCamera
+## A struct with information retrieved from the Exif tag.
+##
+## @item GPSInfo
+## A struct with geotagging information retrieved from the Exif tag.
 ## @end table
 ##
 ## @seealso{imread, imwrite, imshow, imformats}
--- a/scripts/image/imread.m
+++ b/scripts/image/imread.m
@@ -88,7 +88,7 @@
 ## Author: Stefan van der Walt <stefan@sun.ac.za>
 ## Author: Andy Adler
 
-function varargout = imread (varargin)
+function [img, varargout] = imread (varargin)
   if (nargin < 1)
     print_usage ();
   elseif (! ischar (varargin{1}))
@@ -104,7 +104,7 @@
     filename{2} = varargin{2};
   endif
 
-  [varargout{1:nargout}] = imageIO (@__imread__, "read", filename, varargin{:});
+  [img, varargout{2:nargout}] = imageIO (@__imread__, "read", filename, varargin{:});
 endfunction
 
 
--- a/scripts/specfun/expint.m
+++ b/scripts/specfun/expint.m
@@ -20,24 +20,54 @@
 
 ## -*- texinfo -*-
 ## @deftypefn {Function File} {} expint (@var{x})
-## Compute the exponential integral,
+## Compute the exponential integral:
 ## @tex
 ## $$
-##  E_1 (x) = \int_x^\infty {e^{-t} \over t} dt.
+## {\rm E_1} (x) = \int_x^\infty {e^{-t} \over t} dt
 ## $$
 ## @end tex
 ## @ifnottex
 ##
 ## @example
 ## @group
-##               infinity
-##              /
-## expint (x) = | exp (-t)/t dt
-##              /
-##             x
+##            infinity
+##           /
+## E_1 (x) = | exp (-t)/t dt
+##           /
+##          x
 ## @end group
 ## @end example
+## @end ifnottex
 ##
+## Note: For compatibility, this functions uses the @sc{matlab} definition
+## of the exponential integral.  Most other sources refer to this particular
+## value as @math{E_1 (x)}, and the exponential integral is
+## @tex
+## $$
+## {\rm Ei} (x) = - \int_{-x}^\infty {e^{-t} \over t} dt.
+## $$
+## @end tex
+## @ifnottex
+##
+## @example
+## @group
+##             infinity
+##            /
+## Ei (x) = - | exp (-t)/t dt
+##            /
+##          -x
+## @end group
+## @end example
+## @end ifnottex
+##
+## The two definititions are related, for positive real values of @var{x}, by
+## @tex
+## $
+## E_1 (-x) = -{\rm Ei} (x) - i\pi.
+## $
+## @end tex
+## @ifnottex
+## @w{@code{E_1 (-x) = -Ei (x) - i*pi}}.
 ## @end ifnottex
 ## @end deftypefn
 
@@ -47,98 +77,85 @@
     print_usage ();
   endif
 
-  y = expint_E1 (x);
-
-endfunction
+  y = x;  # Copy over all values, including NaNs
 
-## -*- texinfo -*-
-## @deftypefn {Function File} {@var{y} =} expint_E1 (@var{x})
-## Compute the exponential integral,
-## @verbatim
-##                    infinity
-##                   /
-##       expint(x) = | exp(-t)/t dt
-##                   /
-##                  x
-## @end verbatim
-## @end deftypefn
-
-function y = expint_E1 (x)
+  if (isreal (x))
+    idx = (x >= 0);
+    y(idx) = -expint_Ei (-x(idx));
 
-  if (nargin != 1)
-    print_usage ();
-  endif
-
-  y = x;
-
-  idx = (imag (x) > 0 & imag (x) != 0);
-  y(idx) = -expint_Ei (-y(idx)) - i.*pi;
+    idx = (x < 0);
+    y(idx) = -expint_Ei (-x(idx)) - i*pi;
+  else
+    idx = (imag (x) > 0);
+    y(idx) = -expint_Ei (-x(idx)) - i*pi;
 
-  idx = (imag (x) < 0 & imag (x) != 0);
-  y(idx) = -expint_Ei (-y(idx)) + i.*pi;
+    idx = (imag (x) < 0);
+    y(idx) = -expint_Ei (-x(idx)) + i*pi;
 
-  idx = (real (x) >= 0 & imag (x) == 0);
-  y(idx) = -expint_Ei (-y(idx));
+    isreal_idx = (imag (x) == 0);
+    idx = (isreal_idx & real (x) >= 0);
+    y(idx) = -expint_Ei (-x(idx));
 
-  idx = (real (x) < 0 & imag (x) == 0);
-  y(idx) = -expint_Ei (-y(idx)) - i.*pi;
+    idx = (isreal_idx & real (x) < 0);
+    y(idx) = -expint_Ei (-x(idx)) - i*pi;
+  endif
 
 endfunction
 
 ## -*- texinfo -*-
 ## @deftypefn {Function File} {@var{y} =} expint_Ei (@var{x})
-## Compute the exponential integral,
+## Compute the exponential integral:
 ## @verbatim
-##                      infinity
-##                     /
-##    expint_Ei(x) = - | exp(t)/t dt
-##                     /
-##                     -x
+##                       infinity
+##                      /
+##    expint_Ei (x) = - | exp(-t)/t dt
+##                      /
+##                    -x
 ## @end verbatim
 ## @end deftypefn
 
 function y = expint_Ei (x)
 
-  if (nargin != 1)
-    print_usage ();
-  endif
-
   y = zeros (size (x));
   F = @(x) exp (-x)./x;
-  s = prod (size (x));
 
-  for t = 1:s;
-    if (x(t) < 0 && imag (x(t)) == 0)
-      y(t) = -quad (F, -x(t), Inf);
+  for t = 1:numel (x)
+    xt = x(t);
+    if (xt < 0 && imag (xt) == 0)
+      ## Direct integration for most real inputs
+      y(t) = -quad (F, -xt, Inf, [0, 1e-10]);
+    elseif (xt > 2 && imag (xt) == 0)
+      persistent Ei_2 = 4.954234356001890;
+      y(t) = Ei_2 - quad (F, -xt, -2);
+    elseif (abs (xt) < 10)
+      ## Series Expansion for real (range [0,2]) or complex inputs (r < 10)
+      k = 1;
+      do
+        term = xt^k / (k*factorial (k));
+        y(t) += term;
+      until (abs (term) < eps (abs (y(t))) / 2 || k++ >= 100)
+      y(t) = 0.57721566490153286 + log (xt) + y(t);
     else
-      if (abs (x(t)) > 2 && imag (x(t)) == 0)
-        y(t) = expint_Ei (2) - quad (F, -x(t), -2);
+      ## FIXME: This expansion is accurate to only 1e-13 at the beginning
+      ##        near 10+i, although it becomes more accurate as the magnitude
+      ##        of xt grows.
+      if (imag (xt) <= 0)
+        persistent a1 = 4.03640;
+        persistent a2 = 1.15198;
+        persistent b1 = 5.03637;
+        persistent b2 = 4.19160;
+        y(t) = -(xt^2 - a1*xt + a2) ...
+               / ((xt^2 - b1*xt + b2) * (-xt) * exp (-xt)) ...
+               - i*pi;
       else
-        if (abs (x(t)) >= 10)
-          if (imag (x(t)) <= 0)
-            a1 = 4.03640;
-            a2 = 1.15198;
-            b1 = 5.03637;
-            b2 = 4.19160;
-            y(t) = -(x(t).^2 - a1.*x(t) + a2) ...
-                   ./ ((x(t).^2 - b1.*x(t) + b2) .* (-x(t)) .* exp (-x(t))) ...
-                   - i.*pi;
-          else
-            y(t) = conj (expint_Ei (conj (x(t))));
-          endif;
-        ## Serie Expansion
-        else
-          for k = 1:100;
-            y(t) = y(t) + x(t).^k ./ (k.*factorial (k));
-          endfor
-          y(t) = 0.577215664901532860606512090082402431 + log (x(t)) + y(t);
-        endif
-      endif
+        y(t) = conj (expint_Ei (conj (xt)));
+      endif;
     endif
   endfor
 endfunction
 
-%% Test against A&S Table 5.1
+
+## Test against A&S Table 5.1
 %!test
 %! x = [5:5:50]'/100;
 %! gamma = 0.5772156649;
@@ -198,7 +215,52 @@
 %! y = expint (x);
 %! assert (y, y_exp, 1e-9);
 
-%% Test input validation
+## Series expansion (-2 < x < 0)
+## Expected values from Mathematica
+%!test  
+%! x = [-0.1; -0.5; -1; -1.5; -2];
+%! y_exp = [ 1.6228128139692767  - i*pi;
+%!          -0.45421990486317358 - i*pi;
+%!          -1.8951178163559368  - i*pi;
+%!          -3.3012854491297978  - i*pi;
+%!          -4.9542343560018902  - i*pi];
+%! y = expint (x);
+%! assert (y, y_exp, eps (real (y_exp)));
+
+## (x < -2, x real)
+%!test  
+%! x = [-2.5; -3; -10;-15; -25];
+%! y_exp = [-7.0737658945786007   - i*pi;
+%!          -9.9338325706254165   - i*pi;
+%!          -2492.2289762418777   - i*pi;
+%!          -234955.85249076830   - i*pi;
+%!          -3.0059509065255486e9 - i*pi];
+%! y = expint (x);
+%! assert (y, y_exp, 8*eps (real (y_exp)));
+
+## Complex values
+%!test
+%! x = [i; -1-i; 10-i; 10+i];
+%! y_exp = [-0.33740392290096813   - i*0.62471325642771360;
+%!          -1.7646259855638540    + i*0.75382280207927082;
+%!          1.90746381979783120e-6 + i*3.67354374003294739e-6;
+%!          1.90746381979783120e-6 - i*3.67354374003294739e-6];
+%! y = expint (x);
+%! assert (y, y_exp, 1e-12);
+
+## Exceptional values (-Inf, Inf, NaN, 0, 0.37250741078)
+%!test  
+%! x = [-Inf; Inf; NaN; 0; -0.3725074107813668];
+%! y_exp = [-Inf - i*pi;
+%!          -Inf;  # should be 0;
+%!          NaN;
+%!          Inf;
+%!          0 - i*pi];
+%! y = expint (x);
+%! assert (y, y_exp, 5*eps);
+
+## Test input validation
 %!error expint ()
 %!error expint (1,2)
 
+