diff --git a/src/OpenMesh/Core/IO/exporter/ExporterT.hh b/src/OpenMesh/Core/IO/exporter/ExporterT.hh
index 9a9d9b3..13cf883 100644
--- a/src/OpenMesh/Core/IO/exporter/ExporterT.hh
+++ b/src/OpenMesh/Core/IO/exporter/ExporterT.hh
@@ -292,14 +292,14 @@ public:
 
   Vec3f colorf(FaceHandle _fh)    const
   {
-    return (mesh_.has_vertex_colors()
+    return (mesh_.has_face_colors()
 	    ? color_cast<Vec3f>(mesh_.color(_fh))
 	    : Vec3f(0, 0, 0));
   }
 
   Vec4f colorAf(FaceHandle _fh)   const
   {
-    return (mesh_.has_vertex_colors()
+    return (mesh_.has_face_colors()
       ? color_cast<Vec4f>(mesh_.color(_fh))
       : Vec4f(0, 0, 0, 0));
   }
diff --git a/src/OpenMesh/Core/IO/reader/PLYReader.cc b/src/OpenMesh/Core/IO/reader/PLYReader.cc
index 37e605a..dd519c2 100644
--- a/src/OpenMesh/Core/IO/reader/PLYReader.cc
+++ b/src/OpenMesh/Core/IO/reader/PLYReader.cc
@@ -422,6 +422,12 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 			// faces
 			for (i = 0; i < faceCount_ && !_in.eof(); ++i) {
 				FaceHandle fh;
+
+				c[0] = 0;
+				c[1] = 0;
+				c[2] = 0;
+				c[3] = 255;
+
 				for (size_t propertyIndex = 0; propertyIndex < e_it->properties_.size(); ++propertyIndex) {
 					PropertyInfo prop = e_it->properties_[propertyIndex];
 					switch (prop.property) {
@@ -453,6 +459,42 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 							++complex_faces;
 						break;
 
+					case COLORRED:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+                            _in >> tmp;
+							c[0] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+                            _in >> c[0];
+						break;
+
+					case COLORGREEN:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+                            _in >> tmp;
+							c[1] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+                            _in >> c[1];
+						break;
+
+					case COLORBLUE:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+                            _in >> tmp;
+							c[2] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+                            _in >> c[2];
+						break;
+
+					case COLORALPHA:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+                            _in >> tmp;
+							c[3] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+                            _in >> c[3];
+						break;
+
 					case CUSTOM_PROP:
 						if (_opt.check(Options::Custom) && fh.is_valid())
 							readCustomProperty<false>(_in, _bi, fh, prop.name, prop.value, prop.listIndexType);
@@ -465,7 +507,8 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 						break;
 					}
 				}
-
+				if (_opt.face_has_color())
+					_bi.set_color(fh, Vec4uc(c));
 			}
 		}
 		else
@@ -645,6 +688,12 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap
 		else if (e_it->element_ == FACE) {
 			for (unsigned i = 0; i < e_it->count_ && !_in.eof(); ++i) {
 				FaceHandle fh;
+
+				c[0] = 0;
+				c[1] = 0;
+				c[2] = 0;
+				c[3] = 255;
+
 				for (size_t propertyIndex = 0; propertyIndex < e_it->properties_.size(); ++propertyIndex)
 				{
 					PropertyInfo prop = e_it->properties_[propertyIndex];
@@ -679,7 +728,38 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap
 						if (!fh.is_valid())
 							++complex_faces;
 						break;
-
+					case COLORRED:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+							readValue(prop.value, _in, tmp);
+							c[0] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+							readInteger(prop.value, _in, c[0]);
+						break;
+					case COLORGREEN:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+							readValue(prop.value, _in, tmp);
+							c[1] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+							readInteger(prop.value, _in, c[1]);
+						break;
+					case COLORBLUE:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+							readValue(prop.value, _in, tmp);
+							c[2] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+							readInteger(prop.value, _in, c[2]);
+						break;
+					case COLORALPHA:
+						if (prop.value == ValueTypeFLOAT32 ||
+							prop.value == ValueTypeFLOAT) {
+							readValue(prop.value, _in, tmp);
+							c[3] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
+						} else
+							readInteger(prop.value, _in, c[3]);
+						break;
 					case CUSTOM_PROP:
 						if (_opt.check(Options::Custom) && fh.is_valid())
 							readCustomProperty<true>(_in, _bi, fh, prop.name, prop.value, prop.listIndexType);
@@ -692,6 +772,8 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap
 						break;
 					}
 				}
+				if (_opt.face_has_color())
+					_bi.set_color(fh, Vec4uc(c));
 			}
 		} 
 		else { 
@@ -1358,6 +1440,30 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
                 options_ += Options::ColorFloat;
             }
           }
+          else if (elementName == "face") {
+            if (propertyName == "red") {
+              entry = PropertyInfo(COLORRED, valueType);
+              options_ += Options::FaceColor;
+              if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
+                options_ += Options::ColorFloat;
+            } else if (propertyName == "green") {
+              entry = PropertyInfo(COLORGREEN, valueType);
+              options_ += Options::FaceColor;
+              if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
+                options_ += Options::ColorFloat;
+            } else if (propertyName == "blue") {
+              entry = PropertyInfo(COLORBLUE, valueType);
+              options_ += Options::FaceColor;
+              if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
+                options_ += Options::ColorFloat;
+            } else if (propertyName == "alpha") {
+              entry = PropertyInfo(COLORALPHA, valueType);
+              options_ += Options::FaceColor;
+              options_ += Options::ColorAlpha;
+              if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
+                options_ += Options::ColorFloat;
+            }
+          }
 
           //not a special property, load as custom
           if (entry.value == Unsupported){
diff --git a/src/OpenMesh/Core/IO/writer/PLYWriter.cc b/src/OpenMesh/Core/IO/writer/PLYWriter.cc
index 0314b5a..2c14810 100644
--- a/src/OpenMesh/Core/IO/writer/PLYWriter.cc
+++ b/src/OpenMesh/Core/IO/writer/PLYWriter.cc
@@ -127,14 +127,6 @@ write(std::ostream& _os, BaseExporter& _be, Options _opt, std::streamsize _preci
     omerr() << "[PLYWriter] : Warning: Face normals are not supported and thus not exported! " << std::endl;
   }
 
-  if ( _opt.check(Options::FaceColor) ) {
-    // Face normals are not supported
-    // Uncheck these options and output message that
-    // they are not written out even though they were requested
-    _opt.unset(Options::FaceColor);
-    omerr() << "[PLYWriter] : Warning: Face colors are not supported and thus not exported! " << std::endl;
-  }
-
   options_ = _opt;
 
 
@@ -318,6 +310,24 @@ void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _
   _out << "element face " << _be.n_faces() << '\n';
   _out << "property list uchar int vertex_indices" << '\n';
 
+  if ( _opt.face_has_color() ){
+    if ( _opt.color_is_float() ) {
+      _out << "property float red" << '\n';
+      _out << "property float green" << '\n';
+      _out << "property float blue" << '\n';
+
+      if ( _opt.color_has_alpha() )
+        _out << "property float alpha" << '\n';
+    } else {
+      _out << "property uchar red" << '\n';
+      _out << "property uchar green" << '\n';
+      _out << "property uchar blue" << '\n';
+
+      if ( _opt.color_has_alpha() )
+        _out << "property uchar alpha" << '\n';
+    }
+  }
+
   _ofProps = writeCustomTypeHeader(_out, _be.kernel()->fprops_begin(), _be.kernel()->fprops_end());
 
   _out << "end_header" << '\n';
@@ -340,6 +350,7 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const
   OpenMesh::Vec4f cAf;
   OpenMesh::Vec2f t;
   VertexHandle vh;
+  FaceHandle fh;
   std::vector<VertexHandle> vhandles;
 
   std::vector<CustomProperty> vProps;
@@ -405,12 +416,37 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const
   // faces (indices starting at 0)
   for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
   {
+    fh = FaceHandle(i);
+
     // write vertex indices per face
-    nV = _be.get_vhandles(FaceHandle(i), vhandles);
+    nV = _be.get_vhandles(fh, vhandles);
     _out << nV;
     for (size_t j=0; j<vhandles.size(); ++j)
       _out << " " << vhandles[j].idx();
 
+    // FaceColor
+    if ( _opt.face_has_color() ) {
+      //with alpha
+      if ( _opt.color_has_alpha() ){
+        if (_opt.color_is_float()) {
+          cAf = _be.colorAf(fh);
+          _out << " " << cAf;
+        } else {
+          cA  = _be.colorAi(fh);
+          _out << " " << cA;
+        }
+      }else{
+        //without alpha
+        if (_opt.color_is_float()) {
+          cf = _be.colorf(fh);
+          _out << " " << cf;
+        } else {
+          c  = _be.colori(fh);
+          _out << " " << c;
+        }
+      }
+    }
+
     // write custom props
     for (std::vector<CustomProperty>::iterator iter = fProps.begin(); iter < fProps.end(); ++iter)
       write_customProp<false>(_out,*iter,i);
@@ -567,6 +603,7 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const
   OpenMesh::Vec4uc c;
   OpenMesh::Vec4f cf;
   VertexHandle vh;
+  FaceHandle fh;
   std::vector<VertexHandle> vhandles;
 
   // vProps and fProps will be empty, until custom properties are supported by the binary writer
@@ -629,12 +666,35 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const
 
   for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
   {
+    fh = FaceHandle(i);
+
     //face
-    nV = _be.get_vhandles(FaceHandle(i), vhandles);
+    nV = _be.get_vhandles(fh, vhandles);
     writeValue(ValueTypeUINT8, _out, nV);
     for (size_t j=0; j<vhandles.size(); ++j)
       writeValue(ValueTypeINT32, _out, vhandles[j].idx() );
 
+    // face color
+    if ( _opt.face_has_color() ) {
+        if ( _opt.color_is_float() ) {
+          cf  = _be.colorAf(fh);
+          writeValue(ValueTypeFLOAT, _out, cf[0]);
+          writeValue(ValueTypeFLOAT, _out, cf[1]);
+          writeValue(ValueTypeFLOAT, _out, cf[2]);
+
+          if ( _opt.color_has_alpha() )
+            writeValue(ValueTypeFLOAT, _out, cf[3]);
+        } else {
+          c  = _be.colorA(fh);
+          writeValue(ValueTypeUCHAR, _out, (int)c[0]);
+          writeValue(ValueTypeUCHAR, _out, (int)c[1]);
+          writeValue(ValueTypeUCHAR, _out, (int)c[2]);
+
+          if ( _opt.color_has_alpha() )
+            writeValue(ValueTypeUCHAR, _out, (int)c[3]);
+        }
+    }
+
     for (std::vector<CustomProperty>::iterator iter = fProps.begin(); iter < fProps.end(); ++iter)
       write_customProp<true>(_out,*iter,i);
   }
diff --git a/src/OpenMesh/Core/IO/writer/PLYWriter.hh b/src/OpenMesh/Core/IO/writer/PLYWriter.hh
index 65addb6..3940383 100644
--- a/src/OpenMesh/Core/IO/writer/PLYWriter.hh
+++ b/src/OpenMesh/Core/IO/writer/PLYWriter.hh
@@ -87,6 +87,7 @@ namespace IO {
 
     currently supported options:
     - VertexColors
+    - FaceColors
     - Binary
     - Binary -> MSB
 */
diff --git a/src/Unittests/unittests_read_write_PLY.cc b/src/Unittests/unittests_read_write_PLY.cc
index 82370bb..32ae533 100644
--- a/src/Unittests/unittests_read_write_PLY.cc
+++ b/src/Unittests/unittests_read_write_PLY.cc
@@ -782,4 +782,143 @@ TEST_F(OpenMeshReadWritePLY, FailOnUnknownPropertyTypeForLists) {
     EXPECT_EQ(0u, mesh_.n_faces()) << "The number of loaded faces is not correct!";
 }
 
+/*
+ * Load an ASCII PLY file of a cube with float RGB face colors
+ */
+TEST_F(OpenMeshReadWritePLY, LoadSimplePLYWithFaceColors) {
+
+    mesh_.clear();
+
+    mesh_.request_face_colors();
+
+    OpenMesh::IO::Options options;
+    options += OpenMesh::IO::Options::FaceColor;
+
+    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube-minimal-faceColors.ply",options);
+
+    EXPECT_TRUE(ok) << "Unable to load cube-minimal-faceColors.ply";
+
+    EXPECT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
+    EXPECT_EQ(18u, mesh_.n_edges())    << "The number of loaded edges is not correct!";
+    EXPECT_EQ(12u, mesh_.n_faces())    << "The number of loaded faces is not correct!";
+
+    EXPECT_EQ(107, mesh_.color(mesh_.face_handle(0))[0] ) << "Wrong face color at face 0";
+    EXPECT_EQ(117, mesh_.color(mesh_.face_handle(0))[1] ) << "Wrong face color at face 0";
+    EXPECT_EQ(177, mesh_.color(mesh_.face_handle(0))[2] ) << "Wrong face color at face 0";
+
+    EXPECT_EQ(107, mesh_.color(mesh_.face_handle(3))[0] ) << "Wrong face color at face 3";
+    EXPECT_EQ(255, mesh_.color(mesh_.face_handle(3))[1] ) << "Wrong face color at face 3";
+    EXPECT_EQ(135, mesh_.color(mesh_.face_handle(3))[2] ) << "Wrong face color at face 3";
+
+    EXPECT_EQ(163, mesh_.color(mesh_.face_handle(4))[0] ) << "Wrong face color at face 4";
+    EXPECT_EQ(107, mesh_.color(mesh_.face_handle(4))[1] ) << "Wrong face color at face 4";
+    EXPECT_EQ(177, mesh_.color(mesh_.face_handle(4))[2] ) << "Wrong face color at face 4";
+
+    EXPECT_EQ(255, mesh_.color(mesh_.face_handle(7))[0] ) << "Wrong face color at face 7";
+    EXPECT_EQ(140, mesh_.color(mesh_.face_handle(7))[1] ) << "Wrong face color at face 7";
+    EXPECT_EQ(107, mesh_.color(mesh_.face_handle(7))[2] ) << "Wrong face color at face 7";
+
+    EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.vertex_has_color()) << "Wrong user options are returned!";
+    EXPECT_TRUE(options.face_has_color()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.color_has_alpha()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.is_binary()) << "Wrong user options are returned!";
+
+    mesh_.release_face_colors();
+}
+
+/*
+ * Write and read PLY files with face colors in various formats
+ */
+TEST_F(OpenMeshReadWritePLY, WriteAndReadPLYWithFaceColors) {
+    struct Format {
+        Format(const char* outFileName, OpenMesh::IO::Options options) :
+            _outFileName(outFileName),
+            _options(options)
+        {}
+        const char*  _outFileName;
+        const OpenMesh::IO::Options  _options;
+    }
+    formats[] =
+    {
+        Format("cube-minimal-faceColors_ascii_uchar.ply",
+               OpenMesh::IO::Options::Default),
+        Format("cube-minimal-faceColors_ascii_float.ply",
+               OpenMesh::IO::Options::ColorFloat),
+        Format("cube-minimal-faceColors_binary_uchar.ply",
+               OpenMesh::IO::Options::Binary),
+        Format("cube-minimal-faceColors_binary_float.ply",
+               OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorFloat),
+        // Test writing/reading alpha values (all default 1.0/255), but the test mesh
+        // Color type has no alpha channel so there's nothing to test below
+        Format("cube-minimal-faceColors_alpha_ascii_uchar.ply",
+               OpenMesh::IO::Options::ColorAlpha),
+        Format("cube-minimal-faceColors_alpha_ascii_float.ply",
+               OpenMesh::IO::Options::ColorFloat | OpenMesh::IO::Options::ColorAlpha),
+        Format("cube-minimal-faceColors_alpha_binary_uchar.ply",
+               OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorAlpha),
+        Format("cube-minimal-faceColors_alpha_binary_float.ply",
+               OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorFloat | OpenMesh::IO::Options::ColorAlpha),
+    };
+
+    for (size_t i = 0; i < sizeof(formats) / sizeof(Format); ++i)
+    {
+        const char*  outFileName = formats[i]._outFileName;
+
+        mesh_.clear();
+
+        mesh_.request_face_colors();
+
+        OpenMesh::IO::Options options;
+        options += OpenMesh::IO::Options::FaceColor;
+
+        bool ok = OpenMesh::IO::read_mesh(mesh_, "cube-minimal-faceColors.ply", options);
+
+        EXPECT_TRUE(ok) << "Unable to load cube-minimal-faceColors.ply";
+
+        options = formats[i]._options;
+        options += OpenMesh::IO::Options::FaceColor;
+        ok = OpenMesh::IO::write_mesh(mesh_, outFileName, options);
+        EXPECT_TRUE(ok) << "Unable to write " << outFileName;
+
+        // Reset for reading: let the reader determine binary/float/etc.
+        options = OpenMesh::IO::Options::FaceColor;
+        mesh_.clear();
+        ok = OpenMesh::IO::read_mesh(mesh_, outFileName, options);
+        EXPECT_TRUE(ok) << "Unable to load " << outFileName;
+
+        EXPECT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct: " << outFileName;
+        EXPECT_EQ(18u, mesh_.n_edges())    << "The number of loaded edges is not correct: " << outFileName;
+        EXPECT_EQ(12u, mesh_.n_faces())    << "The number of loaded faces is not correct: " << outFileName;
+
+        EXPECT_EQ(107, mesh_.color(mesh_.face_handle(0))[0] ) << "Wrong face color at face 0: " << outFileName;
+        EXPECT_EQ(117, mesh_.color(mesh_.face_handle(0))[1] ) << "Wrong face color at face 0: " << outFileName;
+        EXPECT_EQ(177, mesh_.color(mesh_.face_handle(0))[2] ) << "Wrong face color at face 0: " << outFileName;
+
+        EXPECT_EQ(107, mesh_.color(mesh_.face_handle(3))[0] ) << "Wrong face color at face 3: " << outFileName;
+        EXPECT_EQ(255, mesh_.color(mesh_.face_handle(3))[1] ) << "Wrong face color at face 3: " << outFileName;
+        EXPECT_EQ(135, mesh_.color(mesh_.face_handle(3))[2] ) << "Wrong face color at face 3: " << outFileName;
+
+        EXPECT_EQ(163, mesh_.color(mesh_.face_handle(4))[0] ) << "Wrong face color at face 4: " << outFileName;
+        EXPECT_EQ(107, mesh_.color(mesh_.face_handle(4))[1] ) << "Wrong face color at face 4: " << outFileName;
+        EXPECT_EQ(177, mesh_.color(mesh_.face_handle(4))[2] ) << "Wrong face color at face 4: " << outFileName;
+
+        EXPECT_EQ(255, mesh_.color(mesh_.face_handle(7))[0] ) << "Wrong face color at face 7: " << outFileName;
+        EXPECT_EQ(140, mesh_.color(mesh_.face_handle(7))[1] ) << "Wrong face color at face 7: " << outFileName;
+        EXPECT_EQ(107, mesh_.color(mesh_.face_handle(7))[2] ) << "Wrong face color at face 7: " << outFileName;
+
+        EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned: " << outFileName;
+        EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned: " << outFileName;
+        EXPECT_FALSE(options.vertex_has_color()) << "Wrong user options are returned: " << outFileName;
+        EXPECT_TRUE(options.face_has_color()) << "Wrong user options are returned: " << outFileName;
+        EXPECT_EQ(formats[i]._options.color_is_float(), options.color_is_float()) <<
+            "Wrong user options are returned: " << outFileName;
+        EXPECT_EQ(formats[i]._options.is_binary(), options.is_binary()) <<
+            "Wrong user options are returned: " << outFileName;
+
+        mesh_.release_face_colors();
+    }
+}
+
 }
