想做一个族库管理插件,其中有一个功能是预览其缩略图.
revit api中有一个ElementType.GetPreviewImage方法,ElementType的常用子类就是familySymbol
需要注意的是GetPreviewImage返回的是一个Bitmap,在winform中是直接可以用于pictureBox的image控件的,
但是wpf中的image控件的source一般接受的是一个BitmapImage.
bitmap和bitmapimage的区别以及如何相互转换在此不说,搜一搜即可,但要注意一点:
bitmap转换为bitmapImage或者bitmap.save()时,一定要保证创建bitmap的stream没有关闭,也就是说要避免使用using语句!
不使用任何revit api,使用System.IO.Packaging.StorageInfo的GetStreams方法,以下是链接:
https://thebuildingcoder.typepad.com/blog/2010/06/open-revit-ole-storage.html
他提供了一个示例文件的下载:
https://thebuildingcoder.typepad.com/files/openrevitolestorage.zip
简单概括下原理:反射System.IO.Packaging.StorageRoot的Open方法获取rfa文件的package信息,其中有一个叫做RevitPreview4.0的就存储着预览缩略图信息.将其读取为byte[],然后跳过revit的metadata,得到缩略图的原始二进制数据,
接下来就是byte[]转BitmapImage或者Bitmap的问题了,同样的要注意在某些地方避免使用using语句
提供如何解析rfa文件和获得其跳过metadata后的起始位置的两个方法的代码:
///
/// rfa等格式实际上是一个package,需通过StorageInfo来访问,但是要获得StorageInfo必须
/// 通过反射调用StorageRoot的一个Open方法来获得之
///
///
///
private byte[] ParseRevitPreview4_0(string filename)
{
var srType = typeof(StorageInfo).Assembly.GetType("System.IO.Packaging.StorageRoot");
if (srType?.InvokeMember("Open",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod
, null, null, new object[]{filename,
FileMode.Open,FileAccess.Read,FileShare.Read}) is StorageInfo stInfo)
{
foreach (StreamInfo streamInfo in stInfo.GetStreams())
{
if (streamInfo.Name.Equals("RevitPreview4.0", StringComparison.Ordinal))
{
using (var reader = streamInfo.GetStream(FileMode.Open, FileAccess.Read))
{
var imageData = new byte[reader.Length];
reader.Read(imageData, 0, imageData.Length);
return imageData;
}
}
}
}
return default;
}
///
/// RevitPreview4.0的二进制流中需要跳过revit的一些metadata,来获得图片的起始位置
///
///
///
private int GetPngStartingOffset(byte[] rawData)
{
bool markerFound = false;
int startingOffset = 0;
int previousValue = 0;
using (MemoryStream ms = new MemoryStream(rawData))
{
for (int i = 0; i < rawData.Length; i++)
{
int 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;
}