diff --git a/src/Extensions/RevitFileInfoExtensions.cs b/src/Extensions/RevitFileInfoExtensions.cs index 8902a48..2b10e9c 100644 --- a/src/Extensions/RevitFileInfoExtensions.cs +++ b/src/Extensions/RevitFileInfoExtensions.cs @@ -1,4 +1,5 @@ -using System; +using CodeCave.Revit.Toolkit.OLE; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -18,6 +19,8 @@ internal struct KnownRevitInfoProps public const string USERNAME = "Username"; public const string CENTRAL_MODEL_PATH = "Central Model Path"; public const string REVIT_BUILD = "Revit Build"; + public const string BUILD = "Build"; + public const string FORMAT = "Format"; public const string LAST_SAVE_PATH = "Last Save Path"; public const string OPEN_WORKSET_DEFAULT = "Open Workset Default"; public const string PROJECT_SPARK_FILE = "Project Spark File"; @@ -36,6 +39,12 @@ internal struct KnownRevitInfoProps RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + private static readonly Regex versionExtractorSimple = + new Regex(@"(?\d*)_(?\d*)(\((?\w*)\))?", + RegexOptions.Compiled | + RegexOptions.IgnoreCase | + RegexOptions.CultureInvariant); + #endregion #region Methods @@ -68,23 +77,28 @@ public static void ParseRevit(this RevitFileInfo revitFileInfo, Dictionary diff --git a/src/OLE/RevitFileInfo.cs b/src/OLE/RevitFileInfo.cs index 51b3c2b..309f521 100644 --- a/src/OLE/RevitFileInfo.cs +++ b/src/OLE/RevitFileInfo.cs @@ -5,11 +5,20 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; +using static CodeCave.Revit.Toolkit.RevitFileInfoExtensions; -namespace CodeCave.Revit.Toolkit +namespace CodeCave.Revit.Toolkit.OLE { public class RevitFileInfo { + static RevitFileInfo() + { +#if !NET45 + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif + } + #region Properties /// @@ -58,15 +67,15 @@ public class RevitFileInfo /// public FamilyType[] Types => PartAtom?.Family?.Parts; - public Version Version { get; internal set; } + public string ProductVendor { get; internal set; } = "Autodesk"; - public string Name { get; internal set; } + public string ProductVersion { get; internal set; } - public string Vendor { get; internal set; } + public string ProductName { get; internal set; } = "Revit"; - public object Architecture { get; internal set; } + public int Format { get; internal set; } - public bool Is64Bit { get; internal set; } + public bool Is64Bit { get; internal set; } = true; public string FilePath { get; private set; } @@ -74,6 +83,18 @@ public class RevitFileInfo #region Methods + public static int GetFormatFromFile(string filePath) + { + if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) + throw new ArgumentException("Please supply a valid path to a Revit document", nameof(filePath)); + + var properties = GetProperties(filePath); + if (properties.TryGetValue(KnownRevitInfoProps.FORMAT, out var formatRaw) && int.TryParse(formatRaw, out var format)) + return format; + + throw new InvalidDataException($"Couldn't read format information from the followin file: '{filePath}'"); + } + /// /// Gets a instance from the given file path. /// @@ -84,8 +105,8 @@ public class RevitFileInfo /// filePath is invalid public static RevitFileInfo GetFromFile(string filePath) { - if (string.IsNullOrWhiteSpace(filePath)) - throw new ArgumentException("filePath is invalid"); + if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) + throw new ArgumentException("Please supply a valid path to a Revit document", nameof(filePath)); var rfi = new RevitFileInfo { @@ -130,28 +151,25 @@ public static RevitFileInfo GetFromFile(string filePath) /// internal static Dictionary GetProperties(string filePath) { - var basicInfo = OleDataReader.GetRawBytes(filePath, "BasicFileInfo"); + var basicInfo = OleDataReader.GetRawBytes(filePath, RevitFileMap.OleStreams.BASIC_FILE_INFO); var fileProps = new Dictionary(); - var supportedEncodeings = new[] - { - Encoding.Unicode, - Encoding.BigEndianUnicode, - Encoding.UTF8, - Encoding.ASCII, - Encoding.Default, - }; - foreach (var encoding in supportedEncodeings) + var asd = new Regex(@"\p{IsCJKUnifiedIdeographs}"); + + foreach (var encoding in RevitFileMap.SupportedEncodings) { var basicInfoString = encoding.GetString(basicInfo)?.TrimEnd('\u0a0d'); - using var basicInfoReader = new StringReader(basicInfoString); + if (asd.IsMatch(basicInfoString)) + continue; + + using var basicInfoReader = new StringReader(basicInfoString?.Replace("\0", string.Empty)); // ReSharper disable once RedundantAssignment var stringLine = basicInfoReader.ReadLine(); // skip the first line while (!string.IsNullOrWhiteSpace(stringLine = basicInfoReader.ReadLine())) { - var parts = stringLine.Split(new[] { ":" }, 2, StringSplitOptions.None); - if (parts.Length != 2) + var parts = stringLine.Split(new[] { ":" }, StringSplitOptions.None); + if (parts.Length != 2 || IsInvalidProperty(parts.FirstOrDefault())) continue; fileProps.Add(parts[0].Trim(), parts[1].Trim()); } @@ -163,6 +181,11 @@ internal static Dictionary GetProperties(string filePath) return fileProps; } + private static bool IsInvalidProperty(string input) + { + return input?.Any(c => char.IsControl(c)) ?? true; + } + #endregion } } diff --git a/src/OLE/RevitFileMap.cs b/src/OLE/RevitFileMap.cs new file mode 100644 index 0000000..9692641 --- /dev/null +++ b/src/OLE/RevitFileMap.cs @@ -0,0 +1,37 @@ +using System.Text; + +namespace CodeCave.Revit.Toolkit.OLE +{ + internal struct RevitFileMap + { + internal struct OleStreams + { + public const string IMAGE_STREAM = "RevitPreview4.0"; + public const string BASIC_FILE_INFO = "BasicFileInfo"; + } + + internal struct PngImageMarker + { + public const int MARKER_10 = 10; // 0x0A + public const int MARKER_13 = 13; // 0x0D + public const int MARKER_26 = 26; // 0x1A + public const int MARKER_71 = 71; // 0x47 + public const int MARKER_78 = 78; // 0x4E + public const int MARKER_80 = 80; // 0x50 + public const int MARKER_137 = 137; // 0x89 + } + + public static readonly Encoding[] SupportedEncodings = new[] + { + Encoding.BigEndianUnicode, + Encoding.UTF8, + Encoding.Unicode, + Encoding.ASCII, + Encoding.GetEncoding(1251), + Encoding.UTF32, + Encoding.GetEncoding(1252), + Encoding.UTF7, + Encoding.Default + }; + } +} diff --git a/src/OLE/RevitFileVersionInfo.cs b/src/OLE/RevitFileVersionInfo.cs deleted file mode 100644 index f92654c..0000000 --- a/src/OLE/RevitFileVersionInfo.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Diagnostics; -using System.Text; -using System.Text.RegularExpressions; - -namespace CodeCave.Revit.Toolkit.OLE -{ - /// - /// Represents basic file information about a Revit file (.rfa, .rvt etc) - /// Such as the version of Revit it was created with - /// - [DebuggerDisplay("Version = {Version}")] - public class RevitFileVersionInfo - { - private const string REVIT_BUILD_REGEX = @"^Revit Build\:(.*Revit(\w|\s)*)? (?\d{4})"; - private static readonly Regex RevitBuildRegex; - - /// - /// Gets Revit version. - /// - /// - /// Revit version. - /// - public int Version { get; } - - /// - /// Initializes the class. - /// - static RevitFileVersionInfo() - { - RevitBuildRegex = new Regex(REVIT_BUILD_REGEX, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Multiline); - } - - /// - /// Initializes a new instance of the class. - /// - /// The string representation of Revit version. - /// - /// The following version must be a non-empty string, castable to int - /// or - /// Failed to parse the version - /// - internal RevitFileVersionInfo(string version) - { - if (string.IsNullOrWhiteSpace(version) || !int.TryParse(version, out int versionInt)) - throw new ArgumentException($"The following {nameof(version)} must be a non-empty string, castable to int"); - - // Revit file has 2008 format or earlier - if (versionInt > 2000 && versionInt < 2009) - versionInt = 2009; - - Version = versionInt; - } - - /// - /// Reads BasicFileInfo from a Revit file. - /// - /// The path to Revit file. - /// object - public static RevitFileVersionInfo ReadFromFile(string pathToFile) - { - var content = OleDataReader.GetRawString(pathToFile, nameof(RevitFileVersionInfo), Encoding.Unicode); - var match = RevitBuildRegex.Match(content); - return new RevitFileVersionInfo(match.Groups["version"]?.ToString()); - } - } -} diff --git a/src/Thumbnails/RfaTumbnailExtractor.cs b/src/OLE/RevitTumbnailExtractor.cs similarity index 88% rename from src/Thumbnails/RfaTumbnailExtractor.cs rename to src/OLE/RevitTumbnailExtractor.cs index c3ae320..cd8d13a 100644 --- a/src/Thumbnails/RfaTumbnailExtractor.cs +++ b/src/OLE/RevitTumbnailExtractor.cs @@ -10,7 +10,7 @@ namespace CodeCave.Revit.Toolkit.Thumbnails /// http://thebuildingcoder.typepad.com/blog/2008/10/rvt-file-version.html /// /// - public class RfaTumbnailExtractor : ThumbnailExtractor + public partial class RevitTumbnailExtractor : ThumbnailExtractor { #region Methods @@ -60,7 +60,7 @@ public override MemoryStream ExtractStream(string pathToFile) { try { - var thumbnailBytes = OleDataReader.GetRawBytes(pathToFile, RevitFileMap.OleStorage.IMAGE_STREAM); + var thumbnailBytes = OleDataReader.GetRawBytes(pathToFile, RevitFileMap.OleStreams.IMAGE_STREAM); return ExtractStream(thumbnailBytes); } catch (Exception ex) @@ -73,7 +73,7 @@ public override MemoryStream ExtractStream(MemoryStream memoryStream) { try { - var thumbnailBytes = OleDataReader.GetRawBytes(memoryStream, RevitFileMap.OleStorage.IMAGE_STREAM); + var thumbnailBytes = OleDataReader.GetRawBytes(memoryStream, RevitFileMap.OleStreams.IMAGE_STREAM); return ExtractStream(thumbnailBytes); } catch (Exception ex) @@ -177,28 +177,5 @@ private static int GetPngOffset(byte[] thumbnailBytes) } #endregion Helpers - - #region Revit file map - - internal struct RevitFileMap - { - internal struct OleStorage - { - public const string IMAGE_STREAM = "RevitPreview4.0"; - } - - internal struct PngImageMarker - { - public const int MARKER_10 = 10; // 0x0A - public const int MARKER_13 = 13; // 0x0D - public const int MARKER_26 = 26; // 0x1A - public const int MARKER_71 = 71; // 0x47 - public const int MARKER_78 = 78; // 0x4E - public const int MARKER_80 = 80; // 0x50 - public const int MARKER_137 = 137; // 0x89 - } - } - - #endregion } } diff --git a/src/Revit.Toolkit.csproj b/src/Revit.Toolkit.csproj index f153a82..26f22ef 100644 --- a/src/Revit.Toolkit.csproj +++ b/src/Revit.Toolkit.csproj @@ -61,7 +61,7 @@ - + diff --git a/tests/ThumbnailFixture.cs b/tests/ThumbnailFixture.cs index 098dbf4..d98a563 100644 --- a/tests/ThumbnailFixture.cs +++ b/tests/ThumbnailFixture.cs @@ -30,7 +30,7 @@ public void RfaThumbnailIsGenerated(string rfaRelativePath) var rfaThumbnailPath = Path.ChangeExtension(Path.Combine(Environment.CurrentDirectory, Path.GetFileName(rfaFilePath)), "png"); // act - var rfaThumbnailer = new RfaTumbnailExtractor(); + var rfaThumbnailer = new RevitTumbnailExtractor(); var fileExtracted = rfaThumbnailer.TryExtractFile(rfaFilePath, rfaThumbnailPath); // assert