Skip to content

Commit 42d7236

Browse files
committed
fixed BMP thumbnail extraction for non-Windows OSes
1 parent 0c6fa6f commit 42d7236

11 files changed

+141
-134
lines changed

src/CodeCave.Revit.Toolkit.csproj

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
5+
<LangVersion>latest</LangVersion>
6+
<WarningLevel>7</WarningLevel>
57
<AppendTargetFrameworkToOutputPath Condition="'$(TargetFrameworks)' != ''">true</AppendTargetFrameworkToOutputPath>
68
</PropertyGroup>
79

@@ -29,14 +31,15 @@
2931
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
3032
<Product>$(AssemblyName)</Product>
3133
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
34+
<AutoGenerateBindingRedirects>False</AutoGenerateBindingRedirects>
3235
</PropertyGroup>
3336

3437
<ItemGroup>
3538
<PackageReference Include="CodeCave.Extensions" Version="1.1.*" />
3639
<PackageReference Include="CsvHelper" Version="18.0.*" />
3740
<PackageReference Include="OpenMcdf" Version="2.3.*" />
3841
<PackageReference Include="System.ComponentModel.Annotations" Version="$(NetCorePackageVersion)" />
39-
<PackageReference Include="System.Drawing.Common" Version="7.0.*" Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'netcoreapp2.1'" />
42+
<PackageReference Include="System.Drawing.Common" Version="5.0.*" Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'netcoreapp2.1'" />
4043
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.*" Condition="'$(TargetFramework)' != 'net45'" />
4144
</ItemGroup>
4245

src/OLE/RevitFileInfo.cs

-3
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@ public static int GetFormatFromFile(string filePath)
122122
/// Gets a <see cref="RevitFileInfo" /> instance from the given file path.
123123
/// </summary>
124124
/// <param name="filePath">The file path.</param>
125-
/// <param name="readProperties">if set to <c>true</c> [read properties].</param>
126-
/// <param name="readTypes">if set to <c>true</c> [read types].</param>
127125
/// <returns></returns>
128126
/// <exception cref="T:System.ArgumentException">filePath is invalid.</exception>
129127
public static RevitFileInfo GetFromFile(string filePath)
@@ -174,7 +172,6 @@ public static RevitFileInfo GetFromFile(string filePath)
174172
/// <summary>
175173
/// Gets the properties.
176174
/// </summary>
177-
/// <param name="filePath">The file path.</param>
178175
/// <returns></returns>
179176
internal static Dictionary<string, string> GetProperties(byte[] basicInfo)
180177
{

src/OLE/Xml/ParameterList.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Xml;
44
using System.Xml.Schema;
@@ -29,9 +29,10 @@ public void ReadXml(XmlReader reader)
2929
var unitSymbol = reader.GetAttribute("units").TryGetFromSymbol(out var unitSymbolType)
3030
? unitSymbolType
3131
: UnitSymbolType.UST_NONE;
32+
3233
var units = unitSymbol.TryGetFromUnitSymbol(out var displayUnitType)
33-
? displayUnitType
34-
: DisplayUnitType.DUT_CUSTOM;
34+
? displayUnitType
35+
: DisplayUnitType.DUT_CUSTOM;
3536

3637
var partAtomParam = new PartAtomParameter(reader.Name, paramType, group, units)
3738
{

src/OLE/Xml/PartAtomParameter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using CodeCave.Revit.Toolkit.Parameters;
1+
using CodeCave.Revit.Toolkit.Parameters;
22
using System;
33
using System.Diagnostics;
44
using System.Linq;

src/Parameters/Catalog/TypeCatalogFile.ParameterDefinition.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Diagnostics;
33
using System.Linq;
44

@@ -37,8 +37,8 @@ public ParameterDefinition
3737
(
3838
string name,
3939
ParameterType type,
40-
DisplayUnitType displayUnits)
41-
{
40+
DisplayUnitType displayUnits
41+
){
4242
Name = !string.IsNullOrWhiteSpace(name) ? name : throw new ArgumentException(nameof(name));
4343
ParameterType = (ParameterType.Invalid != type) ? type : throw new ArgumentException(nameof(type));
4444
DisplayUnitType = (DisplayUnitType.DUT_UNDEFINED != displayUnits) ? displayUnits : throw new ArgumentException(nameof(displayUnits));

src/Parameters/Catalog/TypeCatalogFile.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Globalization;
44
using System.IO;
@@ -86,9 +86,11 @@ public TypeCatalogFile(string typeCatalogFile, Encoding encoding = null)
8686
var units = headerParts.ElementAt(1).TryGetUnitTypeFromCatalogString(out var unitType)
8787
? unitType
8888
: UnitType.UT_Undefined;
89+
8990
var type = units.TryGetParameterType(out var paramType)
9091
? paramType
9192
: ParameterType.Invalid;
93+
9294
var displayUnits = headerParts.ElementAt(2).TryGetFromCatalogString(out var displayUnitType)
9395
? displayUnitType
9496
: DisplayUnitType.DUT_UNDEFINED;

src/Parameters/Shared/SharedParameterFile.ParameterDefinition.cs

+2-4
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,8 @@ public ParameterCollection(SharedParameterFile parameterFile, IEnumerable<Parame
253253
public new void Add(ParameterDefinition parameter)
254254
{
255255
if (parameter == null) throw new ArgumentNullException(nameof(parameter));
256-
if (parameter.Group == null) throw new ArgumentNullException(
257-
nameof(parameter), $"Parameter you have provided has no {nameof(ParameterDefinition.Group)} assigned");
258-
if (parameter.Guid == null) throw new ArgumentNullException(
259-
nameof(parameter), $"Parameter you have provided has no {nameof(ParameterDefinition.Guid)} assigned");
256+
if (parameter.Group == null) throw new ArgumentNullException(nameof(parameter), $"Parameter you have provided has no {nameof(ParameterDefinition.Group)} assigned");
257+
if (parameter.Guid == null) throw new ArgumentNullException(nameof(parameter), $"Parameter you have provided has no {nameof(ParameterDefinition.Guid)} assigned");
260258

261259
if (this.Any(p => Equals(parameter.Guid, p.Guid))) throw new ArgumentException(
262260
nameof(parameter),

src/Thumbnails/DwgThumbnailExtractor.cs

+118-40
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ namespace CodeCave.Revit.Toolkit.Thumbnails
1010
/// </summary>
1111
public class DwgThumbnailExtractor : ThumbnailExtractor
1212
{
13+
private static readonly Color ColorBlack = Color.Black;
14+
15+
private static readonly Color ColorWhite = Color.White;
16+
17+
private bool RetainBackgroudColor { get; set; } = true;
18+
19+
/// <inheritdoc/>
1320
public override byte[] ExtractThumbnailBytes(Stream stream)
1421
{
1522
using var binaryReader = new BinaryReader(stream);
@@ -24,51 +31,102 @@ public override byte[] ExtractThumbnailBytes(Stream stream)
2431

2532
for (short i = 1; i <= byteCount; i++)
2633
{
27-
var imageCode = binaryReader.ReadByte();
28-
var imageHeaderStart = binaryReader.ReadInt32();
29-
var imageHeaderSize = binaryReader.ReadInt32();
34+
var thumbInfo = new ThumbnailInfo
35+
{
36+
Type = binaryReader.ReadByte(),
37+
Start = binaryReader.ReadInt32(),
38+
Length = binaryReader.ReadInt32(),
39+
};
3040

3141
#pragma warning disable CC0120 // default just skips to the next byte
32-
switch (imageCode)
42+
switch (thumbInfo.Type)
3343
{
3444
// PNG thumbnail - ACAD 2013 and later
35-
case ThumbnailImageCodes.PNG:
45+
case ThumbnailInfo.PngType:
3646
var ms2013 = new MemoryStream();
37-
stream.Seek(imageHeaderStart, SeekOrigin.Begin);
38-
stream.CopyTo(ms2013, imageHeaderSize);
47+
stream.Seek(thumbInfo.Start, SeekOrigin.Begin);
48+
stream.CopyTo(ms2013, thumbInfo.Length);
3949
var byte2013 = ms2013.ToArray();
4050

4151
return byte2013;
4252

4353
// BMP Preview (2010 file format and lower)
44-
case ThumbnailImageCodes.BMP:
45-
binaryReader.ReadBytes(0xe);
46-
var biBitCount = binaryReader.ReadUInt16();
47-
binaryReader.ReadBytes(4);
48-
var biSizeImage = binaryReader.ReadUInt32();
49-
50-
stream.Seek(imageHeaderStart, SeekOrigin.Begin);
51-
var bitmapBuffer = binaryReader.ReadBytes(imageHeaderSize);
52-
var bitCountRaw = (biBitCount < 9) ? 4 * Math.Pow(2, biBitCount) : 0;
53-
var bitCount = Math.Truncate(bitCountRaw);
54-
var colorTableSize = Convert.ToUInt32(bitCount);
55-
using (var ms = new MemoryStream())
54+
case ThumbnailInfo.BmpType:
55+
56+
for (var intCnt = 0; intCnt < byteCount; intCnt++)
5657
{
57-
using var binaryWriter = new BinaryWriter(ms);
58-
binaryWriter.Write(Convert.ToUInt16(0x4d42));
59-
binaryWriter.Write(54u + colorTableSize + biSizeImage);
60-
binaryWriter.Write(default(ushort));
61-
binaryWriter.Write(default(ushort));
62-
binaryWriter.Write(54u + colorTableSize);
63-
binaryWriter.Write(bitmapBuffer);
64-
65-
using var imageTmp2010 = new Bitmap(ms);
66-
var bytes2010 = ToByteArray(imageTmp2010, ImageFormat.Png);
67-
68-
return bytes2010;
58+
stream.Seek(thumbInfo.Start, SeekOrigin.Begin);
59+
var udtHeader = new BitmapHeader
60+
{
61+
Size = binaryReader.ReadInt32(),
62+
Width = binaryReader.ReadInt32(),
63+
Height = binaryReader.ReadInt32(),
64+
Planes = binaryReader.ReadInt16(),
65+
BitCount = binaryReader.ReadInt16(),
66+
Compression = binaryReader.ReadInt32(),
67+
SizeImage = binaryReader.ReadInt32(),
68+
XPelsPerMeter = binaryReader.ReadInt32(),
69+
YPelsPerMeter = binaryReader.ReadInt32(),
70+
ClrUsed = binaryReader.ReadInt32(),
71+
ClrImportant = binaryReader.ReadInt32(),
72+
};
73+
74+
var bytBMPBuff = new byte[thumbInfo.Length + 1];
75+
76+
if (udtHeader.BitCount != 8)
77+
{
78+
continue;
79+
}
80+
81+
var udtColors = new RgbQuad[256];
82+
for (int count = 0; count < 256; count++)
83+
{
84+
udtColors[count].Blue = binaryReader.ReadByte();
85+
udtColors[count].Green = binaryReader.ReadByte();
86+
udtColors[count].Red = binaryReader.ReadByte();
87+
udtColors[count].Reserved = binaryReader.ReadByte();
88+
}
89+
stream.Seek(thumbInfo.Start - 1, SeekOrigin.Begin);
90+
91+
for (int count = 0; count <= thumbInfo.Length; count++)
92+
bytBMPBuff[count] = binaryReader.ReadByte();
93+
94+
var bmp = new Bitmap(udtHeader.Width, udtHeader.Height);
95+
var lngCnt = 0;
96+
97+
for (var lngY = 1; lngY <= udtHeader.Height; lngY++)
98+
{
99+
for (var lngX = udtHeader.Width; lngX >= 1; lngX--)
100+
{
101+
int lngColor = bytBMPBuff[bytBMPBuff.GetUpperBound(0) - lngCnt];
102+
var udtColor = udtColors[lngColor];
103+
104+
var intRed = Convert.ToInt16(udtColor.Red);
105+
var intGreen = Convert.ToInt16(udtColor.Green);
106+
var intBlue = Convert.ToInt16(udtColor.Blue);
107+
lngColor = ColorTranslator.ToOle(Color.FromArgb(intRed, intGreen, intBlue));
108+
109+
if (!RetainBackgroudColor)
110+
{
111+
if (lngColor == ColorTranslator.ToOle(ColorBlack))
112+
lngColor = ColorTranslator.ToOle(ColorWhite);
113+
else if (lngColor == ColorTranslator.ToOle(ColorWhite))
114+
lngColor = ColorTranslator.ToOle(ColorBlack);
115+
}
116+
117+
bmp.SetPixel(lngX - 1, lngY - 1, ColorTranslator.FromOle(lngColor));
118+
lngCnt++;
119+
}
120+
}
121+
122+
using var outputStream = new MemoryStream();
123+
bmp.Save(outputStream, ImageFormat.Png);
124+
return outputStream.ToArray();
69125
}
70126

71-
case ThumbnailImageCodes.NULL:
127+
return new byte[0];
128+
129+
case ThumbnailInfo.NullType:
72130
break; // DWG file doesn't contain a thumbnail
73131
}
74132
#pragma warning restore CC0120
@@ -77,18 +135,38 @@ public override byte[] ExtractThumbnailBytes(Stream stream)
77135
return new byte[0];
78136
}
79137

80-
public static byte[] ToByteArray(Image image, ImageFormat format)
138+
private struct ThumbnailInfo
139+
{
140+
public const byte BmpType = 2;
141+
public const byte PngType = 6;
142+
public const byte NullType = 3;
143+
144+
public byte Type;
145+
public int Start;
146+
public int Length;
147+
}
148+
149+
private struct BitmapHeader
81150
{
82-
using var ms = new MemoryStream();
83-
image.Save(ms, format);
84-
return ms.ToArray();
151+
public int Size;
152+
public int Width;
153+
public int Height;
154+
public short Planes;
155+
public short BitCount;
156+
public int Compression;
157+
public int SizeImage;
158+
public int XPelsPerMeter;
159+
public int YPelsPerMeter;
160+
public int ClrUsed;
161+
public int ClrImportant;
85162
}
86163

87-
private struct ThumbnailImageCodes
164+
private struct RgbQuad
88165
{
89-
internal const byte BMP = 2;
90-
internal const byte PNG = 6;
91-
internal const byte NULL = 3;
166+
public byte Blue;
167+
public byte Green;
168+
public byte Red;
169+
public byte Reserved;
92170
}
93171
}
94172
}

src/OLE/RevitTumbnailExtractor.cs renamed to src/Thumbnails/RevitTumbnailExtractor.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ namespace CodeCave.Revit.Toolkit.Thumbnails
1111
/// <seealso cref="ThumbnailExtractor" />
1212
public partial class RevitTumbnailExtractor : ThumbnailExtractor
1313
{
14-
15-
14+
/// <inheritdoc/>
1615
public override byte[] ExtractThumbnailBytes(Stream stream)
1716
{
1817
byte[] thumbnailBytes;

0 commit comments

Comments
 (0)