Skip to content

Handle Binary STLs with "solid" prefix better. #26

@MHebes

Description

@MHebes

There's an issue in real-world STLs where even though they're in binary format, they still start with the characters solid, tricking the library into thinking they are ASCII. This results in an "invalid vertex format" exception.

See nschloe/meshio#530 (comment) which is the same issue on a separate library.

Here is the solution applied in that library applied here as well:

--- a/STLDocument.cs
+++ b/STLDocument.cs
@@ -129,22 +129,7 @@ namespace Example.Stl
         /// <returns>True if the <see cref="STLDocument"/> is text-based, otherwise false.</returns>
         public static bool IsText(Stream stream)
         {
-            if (stream == null) throw new NullReferenceException(nameof(stream));
-
-            const string solid = "solid";
-
-            byte[] buffer = new byte[5];
-            string header;
-
-            // Reset the stream to tbe beginning and read the first few bytes, then reset the stream to the beginning again.
-            stream.Seek(0, SeekOrigin.Begin);
-            stream.Read(buffer, 0, buffer.Length);
-            stream.Seek(0, SeekOrigin.Begin);
-
-            // Read the header as ASCII.
-            header = Encoding.ASCII.GetString(buffer);
-
-            return solid.Equals(header, StringComparison.InvariantCultureIgnoreCase);
+            return !IsBinary(stream);
         }
 
         /// <summary>Determines if the <see cref="STLDocument"/> contained within the <paramref name="stream"/> is binary-based.</summary>
@@ -153,7 +138,19 @@ namespace Example.Stl
         /// <returns>True if the <see cref="STLDocument"/> is binary-based, otherwise false.</returns>
         public static bool IsBinary(Stream stream)
         {
-            return !IsText(stream);
+            if (stream == null) throw new NullReferenceException(nameof(stream));
+
+            using var reader = new BinaryReader(stream, Encoding.Default, true);
+
+            stream.Seek(80, SeekOrigin.Begin);
+
+            var numTriangles = reader.ReadUInt32();
+
+            var expectedSize = 84 + numTriangles * 50;
+
+            stream.Seek(0, SeekOrigin.Begin);
+
+            return stream.Length == expectedSize;
         }
 
         /// <summary>Reads the <see cref="STLDocument"/> contained within the <paramref name="stream"/> into a new <see cref="STLDocument"/>.</summary>

This tries to read the number of triangles and check that the file size matches up.

I believe this still needs to be improved to handle additional attribute bytes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions