diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files
index 8919e3392d620678737974d272ee3dd44f807754..87fd35ee22b3c227489cbc79bad227c58ebc1051 100644
--- a/src/fileFormats/Make/files
+++ b/src/fileFormats/Make/files
@@ -10,6 +10,9 @@ ensight/type/ensightPTraits.C
 nas/NASCore.C
 fire/FIRECore.C
 starcd/STARCDCore.C
+stl/STLCore.C
+stl/STLReader.C
+stl/STLReaderASCII.L
 
 vtk/foamVtkCore.C
 vtk/format/foamVtkAppendBase64Formatter.C
diff --git a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.C b/src/fileFormats/stl/STLCore.C
similarity index 51%
rename from src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.C
rename to src/fileFormats/stl/STLCore.C
index 022b6df5a9e802abde0ea4bbf2a6cf797647df1f..a38d3a3cdf919407e8570e3b35204994e2959ab2 100644
--- a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.C
+++ b/src/fileFormats/stl/STLCore.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -23,36 +23,75 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "STLsurfaceFormatCore.H"
+#include "STLCore.H"
 #include "gzstream.h"
 #include "OSspecific.H"
-#include "Map.H"
 #include "IFstream.H"
-#include "Ostream.H"
 
-#undef DEBUG_STLBINARY
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+//! \cond fileScope
 
-// check binary by getting the header and number of facets
+//  The number of bytes in the STL binary header
+static const unsigned STLHeaderSize = 80;
+
+//! \endcond
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::fileFormats::STLCore::STLCore()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::fileFormats::STLCore::isBinaryName
+(
+    const fileName& filename,
+    const STLFormat& format
+)
+{
+    return (format == DETECT ? (filename.ext() == "stlb") : format == BINARY);
+}
+
+
+// Check binary by getting the header and number of facets
 // this seems to work better than the old token-based method
 // - some programs (eg, pro-STAR) have 'solid' as the first word in
 //   the binary header.
 // - using wordToken can cause an abort if non-word (binary) content
 //   is detected ... this is not exactly what we want.
-int Foam::fileFormats::STLsurfaceFormatCore::detectBINARY
+int Foam::fileFormats::STLCore::detectBinaryHeader
 (
     const fileName& filename
 )
 {
-    off_t dataFileSize = Foam::fileSize(filename);
+    bool compressed = false;
+    autoPtr<istream> streamPtr
+    (
+        new ifstream(filename.c_str(), std::ios::binary)
+    );
+
+    // If the file is compressed, decompress it before further checking.
+    if (!streamPtr->good() && isFile(filename + ".gz", false))
+    {
+        compressed = true;
+        streamPtr.reset(new igzstream((filename + ".gz").c_str()));
+    }
+    istream& is = streamPtr();
 
-    IFstream str(filename, IOstream::BINARY);
-    istream& is = str().stdStream();
+    if (!is.good())
+    {
+        FatalErrorInFunction
+            << "Cannot read file " << filename
+            << " or file " << filename + ".gz"
+            << exit(FatalError);
+    }
 
     // Read the STL header
-    char header[headerSize];
-    is.read(header, headerSize);
+    char header[STLHeaderSize];
+    is.read(header, STLHeaderSize);
 
     // Check that stream is OK, if not this may be an ASCII file
     if (!is.good())
@@ -60,7 +99,7 @@ int Foam::fileFormats::STLsurfaceFormatCore::detectBINARY
         return 0;
     }
 
-    // Read the number of triangles in the STl file
+    // Read the number of triangles in the STL file
     // (note: read as int so we can check whether >2^31)
     int nTris;
     is.read(reinterpret_cast<char*>(&nTris), sizeof(unsigned int));
@@ -74,33 +113,73 @@ int Foam::fileFormats::STLsurfaceFormatCore::detectBINARY
     (
         !is
      || nTris < 0
-     || nTris < (dataFileSize - headerSize)/50
-     || nTris > (dataFileSize - headerSize)/25
     )
     {
         return 0;
     }
+    else if (!compressed)
+    {
+        const off_t dataFileSize = Foam::fileSize(filename);
+
+        if
+        (
+            nTris < int(dataFileSize - STLHeaderSize)/50
+         || nTris > int(dataFileSize - STLHeaderSize)/25
+        )
+        {
+            return 0;
+        }
+    }
 
     // looks like it might be BINARY, return number of triangles
     return nTris;
 }
 
 
-bool Foam::fileFormats::STLsurfaceFormatCore::readBINARY
+Foam::autoPtr<std::istream>
+Foam::fileFormats::STLCore::readBinaryHeader
 (
-    istream& is,
-    const off_t dataFileSize
+    const fileName& filename,
+    label& nTrisEstimated
 )
 {
-    sorted_ = true;
+    bool bad = false;
+    bool compressed = false;
+    nTrisEstimated = 0;
+
+    autoPtr<istream> streamPtr
+    (
+        new ifstream(filename.c_str(), std::ios::binary)
+    );
+
+    // If the file is compressed, decompress it before reading.
+    if (!streamPtr->good() && isFile(filename + ".gz", false))
+    {
+        compressed = true;
+        streamPtr.reset(new igzstream((filename + ".gz").c_str()));
+    }
+    istream& is = streamPtr();
+
+    if (!is.good())
+    {
+        streamPtr.clear();
+
+        FatalErrorInFunction
+            << "Cannot read file " << filename
+            << " or file " << filename + ".gz"
+            << exit(FatalError);
+    }
+
 
     // Read the STL header
-    char header[headerSize];
-    is.read(header, headerSize);
+    char header[STLHeaderSize];
+    is.read(header, STLHeaderSize);
 
     // Check that stream is OK, if not this may be an ASCII file
     if (!is.good())
     {
+        streamPtr.clear();
+
         FatalErrorInFunction
             << "problem reading header, perhaps file is not binary "
             << exit(FatalError);
@@ -116,156 +195,56 @@ bool Foam::fileFormats::STLsurfaceFormatCore::readBINARY
     //
     // Also compare the file size with that expected from the number of tris
     // If the comparison is not sensible then it may be an ASCII file
-    if
-    (
-        !is
-     || nTris < 0
-     || nTris < int(dataFileSize - headerSize)/50
-     || nTris > int(dataFileSize - headerSize)/25
-    )
+    if (!is || nTris < 0)
     {
-        FatalErrorInFunction
-            << "problem reading number of triangles, perhaps file is not binary"
-            << exit(FatalError);
+        bad = true;
     }
-
-#ifdef DEBUG_STLBINARY
-    Info<< "# " << nTris << " facets" << endl;
-    label prevZone = -1;
-#endif
-
-    points_.setSize(3*nTris);
-    zoneIds_.setSize(nTris);
-
-    Map<label> lookup;
-    DynamicList<label> dynSizes;
-
-    label ptI = 0;
-    label zoneI = -1;
-    forAll(zoneIds_, facei)
+    else if (!compressed)
     {
-        // Read an STL triangle
-        STLtriangle stlTri(is);
-
-        // transcribe the vertices of the STL triangle -> points
-        points_[ptI++] = stlTri.a();
-        points_[ptI++] = stlTri.b();
-        points_[ptI++] = stlTri.c();
-
-        // interprete stl attribute as a zone
-        const label origId = stlTri.attrib();
+        const off_t dataFileSize = Foam::fileSize(filename);
 
-        Map<label>::const_iterator fnd = lookup.find(origId);
-        if (fnd != lookup.end())
-        {
-            if (zoneI != fnd())
-            {
-                // group appeared out of order
-                sorted_ = false;
-            }
-            zoneI = fnd();
-        }
-        else
-        {
-            zoneI = dynSizes.size();
-            lookup.insert(origId, zoneI);
-            dynSizes.append(0);
-        }
-
-        zoneIds_[facei] = zoneI;
-        dynSizes[zoneI]++;
-
-#ifdef DEBUG_STLBINARY
-        if (prevZone != zoneI)
+        if
+        (
+            nTris < int(dataFileSize - STLHeaderSize)/50
+         || nTris > int(dataFileSize - STLHeaderSize)/25
+        )
         {
-            if (prevZone != -1)
-            {
-                Info<< "endsolid zone" << prevZone << nl;
-            }
-            prevZone = zoneI;
-
-            Info<< "solid zone" << prevZone << nl;
+            bad = true;
         }
-
-        Info<< " facet normal " << stlTri.normal() << nl
-            << "  outer loop" << nl
-            << "   vertex " << stlTri.a() << nl
-            << "   vertex " << stlTri.b() << nl
-            << "   vertex " << stlTri.c() << nl
-            << "  outer loop" << nl
-            << " endfacet" << endl;
-#endif
     }
 
-    names_.clear();
-    sizes_.transfer(dynSizes);
-
-    return true;
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::fileFormats::STLsurfaceFormatCore::STLsurfaceFormatCore
-(
-    const fileName& filename
-)
-:
-    sorted_(true),
-    points_(0),
-    zoneIds_(0),
-    names_(0),
-    sizes_(0)
-{
-    off_t dataFileSize = Foam::fileSize(filename);
-
-    // auto-detect ascii/binary
-    if (detectBINARY(filename))
-    {
-        readBINARY
-        (
-            IFstream(filename, IOstream::BINARY)().stdStream(),
-            dataFileSize
-        );
-    }
-    else
+    if (bad)
     {
-        readASCII
-        (
-            IFstream(filename)().stdStream(),
-            dataFileSize
-        );
-    }
-}
-
+        streamPtr.clear();
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::fileFormats::STLsurfaceFormatCore::~STLsurfaceFormatCore()
-{}
+        FatalErrorInFunction
+            << "problem reading number of triangles, perhaps file is not binary"
+            << exit(FatalError);
+    }
 
+    nTrisEstimated = nTris;
+    return streamPtr;
+}
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-void Foam::fileFormats::STLsurfaceFormatCore::writeHeaderBINARY
+void Foam::fileFormats::STLCore::writeBinaryHeader
 (
     ostream& os,
     unsigned int nTris
 )
 {
     // STL header with extra information about nTris
-    char header[headerSize];
+    char header[STLHeaderSize];
     sprintf(header, "STL binary file %u facets", nTris);
 
     // avoid trailing junk
-    for (size_t i = strlen(header); i < headerSize; ++i)
+    for (size_t i = strlen(header); i < STLHeaderSize; ++i)
     {
         header[i] = 0;
     }
 
-    os.write(header, headerSize);
+    os.write(header, STLHeaderSize);
     os.write(reinterpret_cast<char*>(&nTris), sizeof(unsigned int));
-
 }
 
 
diff --git a/src/fileFormats/stl/STLCore.H b/src/fileFormats/stl/STLCore.H
new file mode 100644
index 0000000000000000000000000000000000000000..548cfabde206b862f5c3ee433ca5c0e45ba5e703
--- /dev/null
+++ b/src/fileFormats/stl/STLCore.H
@@ -0,0 +1,115 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::fileFormats::STLCore
+
+Description
+    Core routines used when reading/writing STL files.
+
+SourceFiles
+    STLCore.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef STLCore_H
+#define STLCore_H
+
+#include "STLpoint.H"
+#include "STLtriangle.H"
+#include "autoPtr.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class fileFormats::STLCore Declaration
+\*---------------------------------------------------------------------------*/
+
+class STLCore
+{
+public:
+
+    // Public data types
+
+        //- Enumeration for the format of data in the stream
+        enum STLFormat
+        {
+            ASCII,  //!< ASCII
+            BINARY, //!< BINARY
+            DETECT  //!< Detect based on (input) content or (output) extension
+        };
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Detect 'stlb' extension as binary
+        static bool isBinaryName
+        (
+            const fileName& filename,
+            const STLFormat& format
+        );
+
+
+        //- Check contents to detect if the file is a binary STL.
+        //  Return the estimated number of triangles or 0 on error.
+        static int detectBinaryHeader(const fileName&);
+
+
+        //- Read STL binary file header.
+        //  Return the opened file stream and estimated number of triangles.
+        //  The stream is invalid and number of triangles is 0 on error.
+        static autoPtr<std::istream> readBinaryHeader
+        (
+            const fileName& filename,
+            label& nTrisEstimated
+        );
+
+        //- Write STL binary file and number of triangles to stream
+        static void writeBinaryHeader(ostream&, unsigned int);
+
+
+    // Constructors
+
+        //- Construct null
+        STLCore();
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/stl/STLReader.C b/src/fileFormats/stl/STLReader.C
new file mode 100644
index 0000000000000000000000000000000000000000..c507b026735658d30e25ccd14375cc4083d9f8e4
--- /dev/null
+++ b/src/fileFormats/stl/STLReader.C
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "STLReader.H"
+#include "Map.H"
+#include "IFstream.H"
+
+#undef DEBUG_STLBINARY
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::fileFormats::STLReader::readBINARY
+(
+    const fileName& filename
+)
+{
+    sorted_ = true;
+
+    label nTris = 0;
+    autoPtr<istream> streamPtr = readBinaryHeader(filename, nTris);
+
+    if (!streamPtr.valid())
+    {
+        FatalErrorInFunction
+            << "Error reading file " << filename
+            << " or file " << filename + ".gz"
+            << exit(FatalError);
+    }
+
+    istream& is = streamPtr();
+
+#ifdef DEBUG_STLBINARY
+    Info<< "# " << nTris << " facets" << endl;
+    label prevZone = -1;
+#endif
+
+    points_.setSize(3*nTris);
+    zoneIds_.setSize(nTris);
+
+    Map<label> lookup;
+    DynamicList<label> dynSizes;
+
+    label ptI = 0;
+    label zoneI = -1;
+    forAll(zoneIds_, facei)
+    {
+        // Read STL triangle
+        STLtriangle stlTri(is);
+
+        // transcribe the vertices of the STL triangle -> points
+        points_[ptI++] = stlTri.a();
+        points_[ptI++] = stlTri.b();
+        points_[ptI++] = stlTri.c();
+
+        // interpret STL attribute as a zone
+        const label origId = stlTri.attrib();
+
+        Map<label>::const_iterator fnd = lookup.find(origId);
+        if (fnd != lookup.end())
+        {
+            if (zoneI != fnd())
+            {
+                // group appeared out of order
+                sorted_ = false;
+            }
+            zoneI = fnd();
+        }
+        else
+        {
+            zoneI = dynSizes.size();
+            lookup.insert(origId, zoneI);
+            dynSizes.append(0);
+        }
+
+        zoneIds_[facei] = zoneI;
+        dynSizes[zoneI]++;
+
+#ifdef DEBUG_STLBINARY
+        if (prevZone != zoneI)
+        {
+            if (prevZone != -1)
+            {
+                Info<< "endsolid zone" << prevZone << nl;
+            }
+            prevZone = zoneI;
+
+            Info<< "solid zone" << prevZone << nl;
+        }
+
+        stlTri.print(Info);
+#endif
+    }
+
+#ifdef DEBUG_STLBINARY
+    if (prevZone != -1)
+    {
+        Info<< "endsolid zone" << prevZone << nl;
+    }
+#endif
+
+    names_.clear();
+    sizes_.transfer(dynSizes);
+
+    return true;
+}
+
+
+bool Foam::fileFormats::STLReader::readFile
+(
+    const fileName& filename,
+    const STLFormat& format
+)
+{
+    if (format == DETECT ? detectBinaryHeader(filename) : format == BINARY)
+    {
+        return readBINARY(filename);
+    }
+    else
+    {
+        return readASCII(filename);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::fileFormats::STLReader::STLReader
+(
+    const fileName& filename
+)
+:
+    sorted_(true),
+    points_(),
+    zoneIds_(),
+    names_(),
+    sizes_()
+{
+    // Auto-detect ASCII/BINARY format
+    readFile(filename, STLCore::DETECT);
+}
+
+
+Foam::fileFormats::STLReader::STLReader
+(
+    const fileName& filename,
+    const STLFormat& format
+)
+:
+    sorted_(true),
+    points_(),
+    zoneIds_(),
+    names_(),
+    sizes_()
+{
+    // Manually specified ASCII/BINARY format
+    readFile(filename, format);
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::fileFormats::STLReader::~STLReader()
+{}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.H b/src/fileFormats/stl/STLReader.H
similarity index 78%
rename from src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.H
rename to src/fileFormats/stl/STLReader.H
index a5aba97cfd930664cc7f15d2cf4a82a896127fc9..db985181d0b2384c935c3944da9279ebd7a79f32 100644
--- a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatCore.H
+++ b/src/fileFormats/stl/STLReader.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -22,22 +22,22 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Class
-    Foam::fileFormats::STLsurfaceFormatCore
+    Foam::fileFormats::STLReader
 
 Description
     Internal class used by the STLsurfaceFormat
 
 SourceFiles
-    STLsurfaceFormatCore.C
+    STLReader.C
     STLsurfaceFormatASCII.L
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef STLsurfaceFormatCore_H
-#define STLsurfaceFormatCore_H
+#ifndef STLReader_H
+#define STLReader_H
 
-#include "STLtriangle.H"
-#include "triFace.H"
+#include "STLCore.H"
+#include "labelledTri.H"
 #include "IFstream.H"
 #include "Ostream.H"
 
@@ -49,10 +49,12 @@ namespace fileFormats
 {
 
 /*---------------------------------------------------------------------------*\
-                    Class STLsurfaceFormatCore Declaration
+                   Class fileFormats::STLReader Declaration
 \*---------------------------------------------------------------------------*/
 
-class STLsurfaceFormatCore
+class STLReader
+:
+    public STLCore
 {
     // Private Data
 
@@ -73,44 +75,37 @@ class STLsurfaceFormatCore
 
     // Private Member Functions
 
-        //- Disallow default bitwise copy construct
-        STLsurfaceFormatCore(const STLsurfaceFormatCore&);
-
-        //- Disallow default bitwise assignment
-        void operator=(const STLsurfaceFormatCore&);
-
-        //- Determine the file type
-        static int detectBINARY(const fileName&);
-
         //- Read ASCII
-        bool readASCII(istream&, const off_t);
+        bool readASCII(const fileName&);
 
         //- Read BINARY
-        bool readBINARY(istream&, const off_t);
-
+        bool readBINARY(const fileName&);
 
-public:
-
-    // Static Data
+        //- Read ASCII or BINARY
+        bool readFile(const fileName&, const STLFormat&);
 
-        //- The number of bytes in the STL binary header
-        static const unsigned int headerSize = 80;
 
+        //- Disallow default bitwise copy construct
+        STLReader(const STLReader&) = delete;
 
-    // Static Member Functions
+        //- Disallow default bitwise assignment
+        void operator=(const STLReader&) = delete;
 
-        //- Write "STL binary file" and number of triangles to stream
-        static void writeHeaderBINARY(ostream&, unsigned int);
 
+public:
 
     // Constructors
 
         //- Read from file, filling in the information
-        STLsurfaceFormatCore(const fileName&);
+        STLReader(const fileName&);
+
+        //- Read from file, filling in the information.
+        //  Manually selected choice of ascii/binary/detect.
+        STLReader(const fileName&, const STLFormat&);
 
 
     //- Destructor
-    ~STLsurfaceFormatCore();
+    ~STLReader();
 
 
     // Member Functions
diff --git a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatASCII.L b/src/fileFormats/stl/STLReaderASCII.L
similarity index 93%
rename from src/surfMesh/surfaceFormats/stl/STLsurfaceFormatASCII.L
rename to src/fileFormats/stl/STLReaderASCII.L
index a3f9e47a6ce06cfb629bf7b78c2ab5fe01b9791e..515a1be90d8f3b5c2ec69c7d9cb1698f7486256b 100644
--- a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormatASCII.L
+++ b/src/fileFormats/stl/STLReaderASCII.L
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -31,7 +31,8 @@ License
    ------ local definitions
  \* ------------------------------------------------------------------------ */
 
-#include "STLsurfaceFormatCore.H"
+#include "STLReader.H"
+#include "OSspecific.H"
 
 using namespace Foam;
 
@@ -97,32 +98,32 @@ public:
     // Access
 
         //- Do all the solid groups appear in order
-        bool sorted() const
+        inline bool sorted() const
         {
             return sorted_;
         }
 
         //- A list of points corresponding to a pointField
-        DynamicList<point>& points()
+        inline DynamicList<point>& points()
         {
             return points_;
         }
 
         //- A list of facet IDs (group IDs)
         //  corresponds to the number of triangles
-        DynamicList<label>& facets()
+        inline DynamicList<label>& facets()
         {
             return facets_;
         }
 
         //- Names
-        DynamicList<word>& names()
+        inline DynamicList<word>& names()
         {
             return names_;
         }
 
         //- Sizes
-        DynamicList<label>& sizes()
+        inline DynamicList<label>& sizes()
         {
             return sizes_;
         }
@@ -390,20 +391,27 @@ endsolid              {space}("endsolid"|"ENDSOLID")({some_space}{word})*
 //
 // member function
 //
-bool Foam::fileFormats::STLsurfaceFormatCore::readASCII
+bool Foam::fileFormats::STLReader::readASCII
 (
-    istream& is,
-    const off_t dataFileSize
+    const fileName& filename
 )
 {
+    IFstream is(filename);
+    if (!is)
+    {
+        FatalErrorInFunction
+            << "file " << filename << " not found"
+            << exit(FatalError);
+    }
+
     // Create the lexer with the approximate number of vertices in the STL
     // from the file size
-    STLASCIILexer lexer(&is, dataFileSize/400);
+    STLASCIILexer lexer(&(is.stdStream()), Foam::fileSize(filename)/400);
     while (lexer.lex() != 0) {}
 
     sorted_ = lexer.sorted();
 
-    // transfer to normal lists
+    // Transfer to normal lists
     points_.transfer(lexer.points());
     zoneIds_.transfer(lexer.facets());
     names_.transfer(lexer.names());
@@ -413,5 +421,5 @@ bool Foam::fileFormats::STLsurfaceFormatCore::readASCII
 }
 
  /* ------------------------------------------------------------------------ *\
-    ------ End of STLfileFormatASCII.L
+    ------ End of STLReaderASCII.L
  \* ------------------------------------------------------------------------ */
diff --git a/src/surfMesh/surfaceFormats/stl/STLpoint.H b/src/fileFormats/stl/STLpoint.H
similarity index 91%
rename from src/surfMesh/surfaceFormats/stl/STLpoint.H
rename to src/fileFormats/stl/STLpoint.H
index 157e832ba5a66581799ac4049f1b30ef792929f3..c9862eaf9442d8a9755206616f1a9aa99ef630ec 100644
--- a/src/surfMesh/surfaceFormats/stl/STLpoint.H
+++ b/src/fileFormats/stl/STLpoint.H
@@ -2,8 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2013 OpenFOAM Foundation
-     \\/     M anipulation  |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,14 +27,13 @@ Class
 Description
     A vertex point representation for STL files.
 
-SourceFiles
-
 \*---------------------------------------------------------------------------*/
 
 #ifndef STLpoint_H
 #define STLpoint_H
 
 #include "point.H"
+#include "floatVector.H"
 #include "Istream.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -80,11 +79,13 @@ public:
 
     // Member Operators
 
-        //- Conversion to point
+        #ifdef WM_DP
+        //- Conversion to double-precision point
         inline operator point() const
         {
             return point(x(), y(), z());
         }
+        #endif
 };
 
 
diff --git a/src/surfMesh/surfaceFormats/stl/STLtriangle.H b/src/fileFormats/stl/STLtriangle.H
similarity index 80%
rename from src/surfMesh/surfaceFormats/stl/STLtriangle.H
rename to src/fileFormats/stl/STLtriangle.H
index 17783047f2107ad507a01d7d16b4dae6efbd3749..a80f5498c53f3e90cd4acb474b266d5f92d57881 100644
--- a/src/surfMesh/surfaceFormats/stl/STLtriangle.H
+++ b/src/fileFormats/stl/STLtriangle.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -45,12 +45,9 @@ namespace Foam
 {
 
 // Forward declaration of friend functions and operators
-
 class STLtriangle;
-
 Ostream& operator<<(Ostream&, const STLtriangle&);
 
-
 /*---------------------------------------------------------------------------*\
                          Class STLtriangle Declaration
 \*---------------------------------------------------------------------------*/
@@ -62,7 +59,7 @@ class STLtriangle
         //- Attribute is 16-bit
         typedef unsigned short STLattrib;
 
-        //- The face normal, many programs write zore or other junk
+        //- The face normal, many programs write zero or other junk
         STLpoint normal_;
 
         //- The three points defining the triangle
@@ -113,7 +110,30 @@ public:
         // Write
 
             //- Write to ostream (binary)
-            inline void write(ostream&);
+            inline void write(ostream&) const;
+
+            //- Write to Ostream (ASCII)
+            inline Ostream& print(Ostream& os) const;
+
+
+            //- Write components to Ostream (ASCII)
+            inline static void write
+            (
+                Ostream& os,
+                const vector& norm,
+                const point& pt0,
+                const point& pt1,
+                const point& pt2
+            );
+
+            //- Write components to Ostream (ASCII), calculating the normal
+            inline static void write
+            (
+                Ostream& os,
+                const point& pt0,
+                const point& pt1,
+                const point& pt2
+            );
 
 
     // Ostream operator
diff --git a/src/surfMesh/surfaceFormats/stl/STLtriangleI.H b/src/fileFormats/stl/STLtriangleI.H
similarity index 61%
rename from src/surfMesh/surfaceFormats/stl/STLtriangleI.H
rename to src/fileFormats/stl/STLtriangleI.H
index c6d17f4a832473b13041394a54d1d441d4cf8abf..85e02be3d483602e41944832275eb9c60b6812ac 100644
--- a/src/surfMesh/surfaceFormats/stl/STLtriangleI.H
+++ b/src/fileFormats/stl/STLtriangleI.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -23,6 +23,8 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+#include "triPointRef.H"
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline Foam::STLtriangle::STLtriangle()
@@ -91,10 +93,61 @@ inline void Foam::STLtriangle::read(istream& is)
 }
 
 
-inline void Foam::STLtriangle::write(ostream& os)
+inline void Foam::STLtriangle::write(ostream& os) const
+{
+    os.write(reinterpret_cast<const char*>(this), 4*sizeof(STLpoint));
+    os.write(reinterpret_cast<const char*>(&attrib_), sizeof(STLattrib));
+}
+
+
+inline Foam::Ostream& Foam::STLtriangle::print(Ostream& os) const
+{
+    os  << " facet normal "
+        << normal_.x() << ' ' << normal_.y() << ' ' << normal_.z() << nl
+        << "  outer loop" << nl
+        << "   vertex " << a_.x() << ' ' << a_.y() << ' ' << a_.z() << nl
+        << "   vertex " << b_.x() << ' ' << b_.y() << ' ' << b_.z() << nl
+        << "   vertex " << c_.x() << ' ' << c_.y() << ' ' << c_.z() << nl
+        << "  endloop" << nl
+        << " endfacet" << nl;
+
+    return os;
+}
+
+
+inline void Foam::STLtriangle::write
+(
+    Ostream& os,
+    const vector& norm,
+    const point& pt0,
+    const point& pt1,
+    const point& pt2
+)
+{
+    os  << " facet normal "
+        << norm.x() << ' ' << norm.y() << ' ' << norm.z() << nl
+        << "  outer loop" << nl
+        << "   vertex " << pt0.x() << ' ' << pt0.y() << ' ' << pt0.z() << nl
+        << "   vertex " << pt1.x() << ' ' << pt1.y() << ' ' << pt1.z() << nl
+        << "   vertex " << pt2.x() << ' ' << pt2.y() << ' ' << pt2.z() << nl
+        << "  endloop" << nl
+        << " endfacet" << nl;
+}
+
+
+inline void Foam::STLtriangle::write
+(
+    Ostream& os,
+    const point& pt0,
+    const point& pt1,
+    const point& pt2
+)
 {
-    os.write(reinterpret_cast<char*>(this), 4*sizeof(STLpoint));
-    os.write(reinterpret_cast<char*>(&attrib_), sizeof(STLattrib));
+    // calculate the normal ourselves
+    vector norm = triPointRef(pt0, pt1, pt2).normal();
+    norm /= mag(norm) + VSMALL;
+
+    write(os, norm, pt0, pt1, pt2);
 }
 
 
diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files
index b03fdc7e77c4c7321aed0970c52b11f73c549f95..e0a2b486e47e03a30422031786fc12372a61fd10 100644
--- a/src/surfMesh/Make/files
+++ b/src/surfMesh/Make/files
@@ -32,9 +32,7 @@ $(surfaceFormats)/off/OFFsurfaceFormatRunTime.C
 $(surfaceFormats)/smesh/SMESHsurfaceFormatRunTime.C
 $(surfaceFormats)/starcd/STARCDsurfaceFormatCore.C
 $(surfaceFormats)/starcd/STARCDsurfaceFormatRunTime.C
-$(surfaceFormats)/stl/STLsurfaceFormatCore.C
 $(surfaceFormats)/stl/STLsurfaceFormatRunTime.C
-$(surfaceFormats)/stl/STLsurfaceFormatASCII.L
 $(surfaceFormats)/tri/TRIsurfaceFormatCore.C
 $(surfaceFormats)/tri/TRIsurfaceFormatRunTime.C
 $(surfaceFormats)/vtk/VTKsurfaceFormatCore.C
diff --git a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.C b/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.C
index ce53aed44bcaf8e9da312d24cb87c8101a8f06e0..0529da9941d8fb954e21729541371be6d22187ac 100644
--- a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.C
+++ b/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.C
@@ -51,20 +51,17 @@ inline void Foam::fileFormats::STLsurfaceFormat<Face>::writeShell
     const point& p0 = pointLst[f[0]];
     for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
     {
-        label fp2 = f.fcIndex(fp1);
-
-        const point& p1 = pointLst[f[fp1]];
-        const point& p2 = pointLst[f[fp2]];
-
-        // write STL triangle
-        os  << " facet normal "
-            << norm.x() << ' ' << norm.y() << ' ' << norm.z() << nl
-            << "  outer loop\n"
-            << "   vertex " << p0.x() << ' ' << p0.y() << ' ' << p0.z() << nl
-            << "   vertex " << p1.x() << ' ' << p1.y() << ' ' << p1.z() << nl
-            << "   vertex " << p2.x() << ' ' << p2.y() << ' ' << p2.z() << nl
-            << "  endloop\n"
-            << " endfacet" << endl;
+        const label fp2 = f.fcIndex(fp1);
+
+        // Write ASCII
+        STLtriangle::write
+        (
+            os,
+            norm,
+            p0,
+            pointLst[f[fp1]],
+            pointLst[f[fp2]]
+        );
     }
 }
 
@@ -92,18 +89,17 @@ inline void Foam::fileFormats::STLsurfaceFormat<Face>::writeShell
     const point& p0 = pointLst[f[0]];
     for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
     {
-        label fp2 = f.fcIndex(fp1);
+        const label fp2 = f.fcIndex(fp1);
 
-        STLtriangle stlTri
+        // Write BINARY
+        STLtriangle
         (
             norm,
             p0,
             pointLst[f[fp1]],
             pointLst[f[fp2]],
             zoneI
-        );
-
-        stlTri.write(os);
+        ).write(os);
     }
 }
 
@@ -131,7 +127,7 @@ bool Foam::fileFormats::STLsurfaceFormat<Face>::read
     this->clear();
 
     // read in the values
-    STLsurfaceFormatCore reader(filename);
+    STLReader reader(filename);
 
     // transfer points
     this->storedPoints().transfer(reader.points());
@@ -273,7 +269,7 @@ void Foam::fileFormats::STLsurfaceFormat<Face>::writeBinary
 
     // Write the STL header
     unsigned int nTris = surf.nTriangles();
-    STLsurfaceFormatCore::writeHeaderBINARY(os, nTris);
+    STLCore::writeBinaryHeader(os, nTris);
 
     label faceIndex = 0;
     forAll(zones, zoneI)
@@ -379,7 +375,7 @@ void Foam::fileFormats::STLsurfaceFormat<Face>::writeBinary
 
     // Write the STL header
     unsigned int nTris = surf.nTriangles();
-    STLsurfaceFormatCore::writeHeaderBINARY(os, nTris);
+    STLCore::writeBinaryHeader(os, nTris);
 
     // always write unsorted
     forAll(faceLst, facei)
@@ -402,10 +398,20 @@ void Foam::fileFormats::STLsurfaceFormat<Face>::write
     const MeshedSurfaceProxy<Face>& surf
 )
 {
-    const word ext = filename.ext();
+    // Auto-detect ASCII/BINARY extension
+    write(filename, surf, STLCore::DETECT);
+}
 
-    // handle 'stlb' as binary directly
-    if (ext == "stlb")
+
+template<class Face>
+void Foam::fileFormats::STLsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const MeshedSurfaceProxy<Face>& surf,
+    const STLFormat& format
+)
+{
+    if (STLCore::isBinaryName(filename, format))
     {
         writeBinary(filename, surf);
     }
@@ -423,10 +429,20 @@ void Foam::fileFormats::STLsurfaceFormat<Face>::write
     const UnsortedMeshedSurface<Face>& surf
 )
 {
-    word ext = filename.ext();
+    // Auto-detect ASCII/BINARY extension
+    write(filename, surf, STLCore::DETECT);
+}
+
 
-    // handle 'stlb' as binary directly
-    if (ext == "stlb")
+template<class Face>
+void Foam::fileFormats::STLsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const UnsortedMeshedSurface<Face>& surf,
+    const STLFormat& format
+)
+{
+    if (STLCore::isBinaryName(filename, format))
     {
         writeBinary(filename, surf);
     }
diff --git a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.H b/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.H
index 02c12ae24b56dde6543bc2a22aa8062e85f14e5d..76aaf028a327350f74f4bb737e10e90ff844476b 100644
--- a/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.H
+++ b/src/surfMesh/surfaceFormats/stl/STLsurfaceFormat.H
@@ -25,7 +25,7 @@ Class
     Foam::fileFormats::STLsurfaceFormat
 
 Description
-    Provide a means of reading/writing STL files (ASCII and binary).
+    Provide a means of reading/writing STL files (ASCII and BINARY).
 
 Note
     For efficiency, the zones are sorted before creating the faces.
@@ -40,7 +40,7 @@ SourceFiles
 #ifndef STLsurfaceFormat_H
 #define STLsurfaceFormat_H
 
-#include "STLsurfaceFormatCore.H"
+#include "STLReader.H"
 #include "MeshedSurface.H"
 #include "MeshedSurfaceProxy.H"
 #include "UnsortedMeshedSurface.H"
@@ -59,7 +59,8 @@ namespace fileFormats
 template<class Face>
 class STLsurfaceFormat
 :
-    public MeshedSurface<Face>
+    public MeshedSurface<Face>,
+    public STLCore
 {
     // Private Member Functions
 
@@ -80,6 +81,7 @@ class STLsurfaceFormat
             const label zoneI
         );
 
+
         //- Disallow default bitwise copy construct
         STLsurfaceFormat(const STLsurfaceFormat<Face>&);
 
@@ -132,6 +134,15 @@ public:
         //  as ASCII or BINARY, depending on the extension
         static void write(const fileName&, const MeshedSurfaceProxy<Face>&);
 
+        //- Write surface mesh components by proxy
+        //  as ASCII or BINARY or dependent on the extension
+        static void write
+        (
+            const fileName&,
+            const MeshedSurfaceProxy<Face>&,
+            const STLFormat&
+        );
+
         //- Write UnsortedMeshedSurface (as ASCII) sorted by zone
         static void writeAscii
         (
@@ -150,6 +161,15 @@ public:
         //  as ASCII or BINARY, depending on the extension
         static void write(const fileName&, const UnsortedMeshedSurface<Face>&);
 
+        //- Write UnsortedMeshedSurface
+        //  as ASCII or BINARY or dependent on the extension
+        static void write
+        (
+            const fileName&,
+            const UnsortedMeshedSurface<Face>&,
+            const STLFormat&
+        );
+
         //- Read from file
         virtual bool read(const fileName&);