using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Packaging; using System.Reflection; using System.Text; using System.Windows.Media.Imaging; using System.Xml; namespace ShrlAlgoToolkit.RevitAddins.Assists { /// /// 直接读取文件的信息(不依赖RevitAPI) /// public class RevitFileAssist { public RevitFileAssist(string filePath) { if (filePath.EndsWith(".rvt") || filePath.EndsWith(".rte") || filePath.EndsWith(".rfa") || filePath.EndsWith(".rft")) { ParserRevitFile(filePath); } else { throw new Exception("文件格式不正确!"); } } /// /// 图片资源转字节 /// /// /// private static byte[] BitSourceToArray(BitmapSource bitmapSource) { BitmapEncoder encoder = new JpegBitmapEncoder(); using var ms = new MemoryStream(); encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); encoder.Save(ms); return ms.ToArray(); } private static int GetPngStartingOffset(byte[] previewData) { var markerFound = false; var startingOffset = 0; var previousValue = 0; using var ms = new MemoryStream(previewData); for (var i = 0; i < previewData.Length; i++) { var currentValue = ms.ReadByte(); // possible start of PNG file data if (currentValue == 137) // 0x89 { markerFound = true; startingOffset = i; previousValue = currentValue; continue; } switch (currentValue) { case 80: // 0x50 if (markerFound && previousValue == 137) { previousValue = currentValue; continue; } markerFound = false; break; case 78: // 0x4E if (markerFound && previousValue == 80) { previousValue = currentValue; continue; } markerFound = false; break; case 71: // 0x47 if (markerFound && previousValue == 78) { previousValue = currentValue; continue; } markerFound = false; break; case 13: // 0x0D if (markerFound && previousValue == 71) { previousValue = currentValue; continue; } markerFound = false; break; case 10: // 0x0A if (markerFound && previousValue == 26) { return startingOffset; } if (markerFound && previousValue == 13) { previousValue = currentValue; continue; } markerFound = false; break; case 26: // 0x1A if (markerFound && previousValue == 10) { previousValue = currentValue; continue; } markerFound = false; break; } } return 0; } /// /// 获取缩略图 /// /// /// private static Image GetPreviewAsImage(byte[] previewData) { if (previewData is not { Length: > 0 }) { using var newBitmap = new Bitmap(128, 128); return newBitmap.Clone() as Bitmap; } // read past the Revit metadata to the start of the PNG image var startingOffset = GetPngStartingOffset(previewData); if (startingOffset == 0) { using var newBitmap = new Bitmap(128, 128); return newBitmap.Clone() as Bitmap; } try { var pngDataBuffer = new byte[previewData.GetUpperBound(0) - startingOffset + 1]; // read the PNG image data into a byte array using (var ms = new MemoryStream(previewData)) { ms.Position = startingOffset; ms.Read(pngDataBuffer, 0, pngDataBuffer.Length); } byte[] decoderData = null; // if the image data is valid if (pngDataBuffer != null) { // use a memory stream to decode the PNG image data // and copy the decoded data into a byte array using var ms = new MemoryStream(pngDataBuffer); var decoder = new PngBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); decoderData = BitSourceToArray(decoder.Frames[0]); } if (decoderData is { Length: > 0 }) { // use another memory stream to create a Bitmap // and then an Image from that Bitmap using var ms = new MemoryStream(decoderData); using var newBitmap = new Bitmap(ms); using Image newImage = newBitmap; return newImage.Clone() as Image; } } catch (Exception ex) { Debug.WriteLine(ex.Message); } using (var newBitmap = new Bitmap(128, 128)) { return newBitmap.Clone() as Bitmap; } } private static StringBuilder ParseContents(string unicodeData) { var s = new StringBuilder(); var basicFileInfoParts = unicodeData.Split('\0'); foreach (var basicFileInfoPart in basicFileInfoParts) { if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0) { var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions()); foreach (var detailPart in detailInfoParts) { s.Append(ParseDetailInfo(detailPart) + "\r\n"); } } } return s; } private static string ParseDetailInfo(string detailInfo) { try { detailInfo = detailInfo.Trim(); var index = detailInfo.IndexOf(":", StringComparison.Ordinal); var detailValue = detailInfo.Substring(index + 1); var detailKey = detailInfo.Substring(0, index); detailKey = detailKey.Trim().ToUpper().Replace(" ", string.Empty); detailKey = PurgeUnprintableCharacters(detailKey); detailValue = PurgeUnprintableCharacters(detailValue); return $"{detailKey}:{detailValue}"; //Console.WriteLine($"{detailKey}:{detailValue}"); } catch (Exception) { return detailInfo; } //switch (detailKey) //{ // case "WORKSHARING": // if (string.IsNullOrEmpty(detailValue)) // { // WorkSharing = WorkSharingMode.HCLight; // return; // } // string workSharing = detailValue.Replace(" ", string.Empty).Trim().ToUpper(); // switch (workSharing) // { // case "NOTENABLED": // WorkSharing = WorkSharingMode.NotEnabled; // break; // case "LOCAL": // WorkSharing = WorkSharingMode.Local; // break; // case "CENTRAL": // WorkSharing = WorkSharingMode.Central; // break; // default: // WorkSharing = WorkSharingMode.HCLight; // break; // } // break; // case "USERNAME": // UserName = detailValue.Trim(); // break; // case "CENTRALFILEPATH": // CentralFilePath = detailValue.Trim(); // break; // case "REVITBUILD": // RevitBuild = detailValue.Trim(); // break; // case "LASTSAVEPATH": // LastSavedpath = detailValue.Trim(); // break; // case "OPENWORKSETDEFAULT": // OpenWorksetDefault = Convert.ToInt32(detailValue.Trim()); // break; // default: // Console.WriteLine($"{detailKey}:{detailValue}"); // //Debug.Assert(false, string.Format("{0} was not found in the case tests.", detailKey)); // break; //} } private static StringBuilder ParseFileInfo(string unicodeData) { var s = new StringBuilder(); var basicFileInfoParts = unicodeData.Split('\0'); foreach (var basicFileInfoPart in basicFileInfoParts) { if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0) { var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions()); foreach (var detailPart in detailInfoParts) { s.Append(ParseDetailInfo(detailPart) + "\r\n"); } } } return s; } /// /// 解析参数 /// /// private List ParseParameterDefinition(StreamInfo streamInfo) { var document = new XmlDocument(); string xmlStr; byte[] streamData; using var streamReader = streamInfo.GetStream(FileMode.Open, FileAccess.Read); streamData = new byte[streamReader.Length]; streamReader.Read(streamData, 0, streamData.Length); xmlStr = Encoding.UTF8.GetString(streamData); document.LoadXml(xmlStr); //string xmldocpath = Path.GetFileNameWithoutExtension(FileName) + ".xml"; //document.Save(xmldocpath); //读取参数信息 var root = document.DocumentElement; //节点前缀的命名空间 var nameSpace = root.GetNamespaceOfPrefix("A"); //string nameSpace = root.NamespaceURI; var nsmgr = new XmlNamespaceManager(document.NameTable); ; nsmgr.AddNamespace("entry", nameSpace); //族类型 var xnlist = document.GetElementsByTagName("A:part"); var fileinfo = document.GetElementsByTagName("A:design-file"); foreach (XmlNode xn in fileinfo) { if (xn.HasChildNodes) { foreach (XmlNode child in xn.ChildNodes) { if (child.Name == "A:title") { SafeName = child.InnerText; } if (child.Name == "A:product") { Product = child.InnerText; } if (child.Name == "A:product-version") { RevitVersion = child.InnerText; } if (child.Name == "A:product-updated") { UpdateTime = child.InnerText; } } } } //XmlNode rootnode = document.SelectSingleNode("/entry/A: family/A:part", nsmgr); //XmlNodeList xnlist = rootnode.ChildNodes; var symbols = new List(); foreach (XmlNode xn in xnlist) { //XmlAttributeCollection attriCol = xn.Attributes; //foreach (XmlAttribute xmlAttri in attriCol) //{ // string name = xmlAttri.Name; // string value = xmlAttri.Value; //} var symbol = new FamilyTypeDefinition(); if (xn.HasChildNodes) { foreach (XmlNode child in xn.ChildNodes) { var p = new ParameterDefinition(); if (child.Name == "title") { symbol.Name = child.InnerText; continue; } //族类型节点 p.Name = child.Name; //族名称 p.Value = child.InnerText; symbol.ParameterDefinitions.Add(p); } } symbols.Add(symbol); } return symbols; } private static StringBuilder ParsePartAtom(string unicodeData) { var s = new StringBuilder(); var basicFileInfoParts = unicodeData.Split('\0'); foreach (var basicFileInfoPart in basicFileInfoParts) { if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0) { var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions()); foreach (var detailPart in detailInfoParts) { s.Append(ParseDetailInfo(detailPart) + "\r\n"); } } } return s; } /// /// 解析预览图像(使用循环确保完整读取,避免 CA2022 警告) /// /// /// private static Image ParsePreviewImage(StreamInfo stream) { byte[] streamData; using (var streamReader = stream.GetStream(FileMode.Open, FileAccess.Read)) { // 获取长度并校验 long lengthLong = streamReader.Length; if (lengthLong < 0) { throw new IOException("Stream length is negative."); } if (lengthLong > int.MaxValue) { throw new IOException("Stream is too large to be read into a single byte array."); } var totalBytes = (int)lengthLong; streamData = new byte[totalBytes]; int offset = 0; while (offset < totalBytes) { int bytesRead = streamReader.Read(streamData, offset, totalBytes - offset); if (bytesRead == 0) { // 流在预期字节读取完成前就结束,抛出异常以避免不完整数据处理 throw new EndOfStreamException("Unable to read the full stream data."); } offset += bytesRead; } } return GetPreviewAsImage(streamData); } /// /// 解析传输数据 /// /// /// private static StringBuilder ParseTransMissionData(string unicodeData) { var s = new StringBuilder(); var basicFileInfoParts = unicodeData.Split('\0'); foreach (var basicFileInfoPart in basicFileInfoParts) { if (basicFileInfoPart.IndexOf("\r\n") >= 0) { var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions()); foreach (var detailPart in detailInfoParts) { s.Append(ParseDetailInfo(detailPart) + "\r\n"); } } } return s; } //public static StringBuilder GetFamilyInfo(List symbols) //{ // var sb = new StringBuilder(); // foreach (var symbol in symbols) // { // sb.Append($"族类型:{symbol.Name}{symbol.Value}\r\n"); // foreach (var param in symbol.ParameterDefinitions) // { // sb.Append($" {param.Name}:{param.Value}\r\n"); // } // } // return sb; //} /// /// 解析Revit文件信息 /// /// private void ParserRevitFile(string filename) { //获取Storageroot Object var bindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod; var storageRootType = typeof(StorageInfo).Assembly.GetType("System.IO.Packaging.StorageRoot", true, false); //Type storageRootType = typeof(StorageInfo).Assembly.GetType("System.IO.Packaging", true, false); object[] file = { filename, FileMode.Open, FileAccess.Read, FileShare.Read }; var Storage = (StorageInfo)storageRootType.InvokeMember("Open", bindingFlags, null, null, file); //if (Storage != null) //{ // storageRootType.InvokeMember("CloseTrigger", bindingFlags, null, Storage, file); // //Console.Write($"{filePath}文件无法作为结构存储,并打开打开"); //} //var x = Storage.ThumbnailImage.GetPreviewAsImage(); //读取结构化存储文件 var streams = Storage.GetStreams(); foreach (var streamInfo in streams) { string unicodeData; using (var streamReader = streamInfo.GetStream(FileMode.Open, FileAccess.Read)) { byte[] streamData = new byte[streamReader.Length]; streamReader.Read(streamData, 0, streamData.Length); unicodeData = Encoding.Unicode.GetString(streamData); //unicodeData = Encoding.UTF8.GetString(streamData); } if (streamInfo.Name.ToUpper().Equals("BASICFILEINFO")) { FileInfo = ParseFileInfo(unicodeData); } if (streamInfo.Name.ToUpper().Equals("PARTATOM")) { //Console.WriteLine("部分原子:\r\n" + ParsePartAtom(unicodeData)); if (filename.EndsWith(".rfa")) { SymbolTypes = ParseParameterDefinition(streamInfo); //Console.WriteLine("族参数:\r\n" + GetFamilyInfo(symbols)); } } if (streamInfo.Name.ToUpper().Equals("TRANSMISSIONDATA")) { TransMissionData = ParseTransMissionData(unicodeData); } if (streamInfo.Name.ToUpper().Equals("REVITPREVIEW4.0")) { PreviewImage = ParsePreviewImage(streamInfo); } if (streamInfo.Name.ToUpper().Equals("CONTENTS")) { Content = ParseContents(unicodeData); } } //if (Storage != null) //{ // try // { // storageRootType.InvokeMember("CloseTrigger", bindingFlags, null, Storage, file); // } // catch (Exception ex) // { // MessageBox.ShowAhead(ex.ViewMessage); // } //} } /// /// 清理无效字符 /// /// /// public static string PurgeUnprintableCharacters(string oldValue) { var sb = new StringBuilder(); var oldValueArray = oldValue.ToCharArray(); foreach (var letter in oldValueArray) { int decimalValue = letter; if (decimalValue is >= 32 and <= 126) { sb.Append(letter); } } oldValue = sb.ToString(); sb.Length = 0; sb.Capacity = 0; sb = null; return oldValue; } public StringBuilder Content { get; private set; } public StringBuilder FileInfo { get; private set; } public Image PreviewImage { get; private set; } public string Product { get; private set; } public string RevitVersion { get; private set; } public string SafeName { get; private set; } public List SymbolTypes { get; private set; } public StringBuilder TransMissionData { get; private set; } public string UpdateTime { get; private set; } public class FamilyTypeDefinition { public string Name { get; set; } public List ParameterDefinitions { get; set; } = new(); public string Value { get; set; } } public class ParameterDefinition { public string Name { get; set; } public string TypeOfParameterDefinition { get; set; } public string Value { get; set; } } } }