Files
Shrlalgo.RvKits/ShrlAlgoToolkit.RevitAddins/Assists/RevitFileAssist.cs
2025-12-28 11:47:54 +08:00

645 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 直接读取文件的信息不依赖RevitAPI
/// </summary>
public class RevitFileAssist
{
public RevitFileAssist(string filePath)
{
if (filePath.EndsWith(".rvt") || filePath.EndsWith(".rte") || filePath.EndsWith(".rfa") ||
filePath.EndsWith(".rft"))
{
ParserRevitFile(filePath);
}
throw new Exception("文件格式不正确!");
}
/// <summary>
/// 图片资源转字节
/// </summary>
/// <param name="bitmapSource"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 获取缩略图
/// </summary>
/// <param name="previewData"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 解析参数
/// </summary>
/// <param name="streamInfo"></param>
private List<FamilyTypeDefinition> 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<FamilyTypeDefinition>();
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;
}
/// <summary>
/// 解析预览图像(使用循环确保完整读取,避免 CA2022 警告)
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 解析传输数据
/// </summary>
/// <param name="unicodeData"></param>
/// <returns></returns>
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<FamilyTypeDefinition> 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;
//}
/// <summary>
/// 解析Revit文件信息
/// </summary>
/// <param name="filename"></param>
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);
// }
//}
}
/// <summary>
/// 清理无效字符
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
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<FamilyTypeDefinition> 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<ParameterDefinition> 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; }
}
}
}