Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / VBScript
Article

VBDwgImageExtractor

Rate me:
Please Sign up or sign in to vote.
3.88/5 (9 votes)
22 Nov 20074 min read 60.7K   2K   30   13
An article on trying to read the BMP preview Image data from Autocad (R13C3 and later) DWG drawing.
Screenshot - VBDwgImageExtractor.jpg

Introduction

Some time ago, a friend and coworker of mine needed to get the preview image stored in a DWG Autocad Drawing from a Visual Basic application to automate some tasks in AutoCAD. While searching the internet, we found the OpenDWG specification (rtf) from the Open Design Alliance Website, and were able to do it. We try here to get the BMP drawing preview image into a pictureBox using Visual Basic .NET.

Create the Visual Basic Application Project:

  1. File -> New -> New project
  2. Create a New VBDwgImageExtractor Visual Basic Windows Application
  3. Change the Form1 Text field to VBDwgImageExtractor
  4. From the Toolbox, drag-n-drop a PictureBox into the form
  5. Drag-n-drop a Button and an openFileDialog into the form
  6. Set the openFileDialog1 Filter property to Drawing files (*.dwg)|*.dwg|All files (*.*)|*.*, and the DefaultExt to dwg
  7. Now double click the Button1 button, to create and edit the Button1_Click event

Change the Button1_Click method with code to open and show the BMP preview as follows:

VB
Private Sub Button1_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click

        If (OpenFileDialog1.ShowDialog() = DialogResult.OK) Then
            Dim path As String = OpenFileDialog1.FileName
            ' Create the reader for data.
            Dim fs As FileStream = _
                New FileStream(path, FileMode.Open, FileAccess.Read, 
                    FileShare.ReadWrite)
            Dim r As BinaryReader = New BinaryReader(fs)

            ' Get the image position in the DWG file
            r.BaseStream.Seek(&HD, SeekOrigin.Begin)
            Dim imgPos As Int32 = r.ReadInt32()
            r.BaseStream.Seek(imgPos, SeekOrigin.Begin)

            ' Image sentinel to check if the image data 
            ' is not corrupted
            Dim imgBSentinel() As Byte = _
                {&H1F, &H25, &H6D, &H7, &HD4, &H36, &H28, _
                &H28, &H9D, &H57, &HCA, &H3F, &H9D, &H44, &H10, &H2B}

            ' Read Image sentinel
            Dim imgCSentinel(16) As Byte
            imgCSentinel = r.ReadBytes(16)

            ' if image sentinel is correct
            If (imgBSentinel.ToString() = imgCSentinel.ToString()) Then
                ' Get image size
                Dim imgSize As UInt32 = r.ReadUInt32()

                ' Get number of images present
                Dim imgPresent As Byte = r.ReadByte()

                ' header
                Dim imgHeaderStart As Int32 = 0
                Dim imgHeaderSize As Int32 = 0

                ' bmp data
                Dim imgBmpStart As Int32 = 0
                Dim imgBmpSize As Int32 = 0
                Dim bmpDataPresent As Boolean = False

                ' wmf data
                Dim imgWmfStart As Int32
                Dim imgWmfSize As Int32
                Dim wmfDataPresent As Boolean = False

                ' get each image present
                For I As Integer = 1 To imgPresent

                    ' Get image type
                    Dim imgCode As Byte = r.ReadByte()
                    Select Case imgCode

                        Case 1
                            ' Header data
                            imgHeaderStart = r.ReadInt32()
                            imgHeaderSize = r.ReadInt32()
                        Case 2
                            ' bmp data
                            imgBmpStart = r.ReadInt32()
                            imgBmpSize = r.ReadInt32()
                            bmpDataPresent = True
                        Case 3
                            ' wmf data
                            imgWmfStart = r.ReadInt32()
                            imgWmfSize = r.ReadInt32()
                            wmfDataPresent = True
                    End Select
                Next

                If (bmpDataPresent) Then

                    r.BaseStream.Seek(imgBmpStart, SeekOrigin.Begin)

                    Dim tempPixelData(imgBmpSize + 14) As Byte

                    ' indicate it is a bit map
                    tempPixelData(0) = &H42
                    tempPixelData(1) = &H4D

                    ' offBits
                    tempPixelData(10) = &H36
                    tempPixelData(11) = &H4

                    Dim tempBuffData(imgBmpSize) As Byte

                    tempBuffData = r.ReadBytes(imgBmpSize)

                    tempBuffData.CopyTo(tempPixelData, 14)

                    Dim memStream As MemoryStream = _
                            New MemoryStream(tempPixelData)

                    Dim bmp As Bitmap = New Bitmap(memStream)

                    PictureBox1.Image = bmp

                End If

                If (wmfDataPresent) Then
                    ' read imgWmfSize wmf data
                End If

                Dim imgESentinel() As Byte = _
                    {&HE0, &HDA, &H92, &HF8, &H2B, &HC9, &HD7, _
                    &HD7, &H62, &HA8, &H35, &HC0, &H62, &HBB, &HEF, &HD4}

                imgCSentinel = r.ReadBytes(16)

                ' if image sentinel is correct
                If (imgESentinel.ToString() = imgCSentinel.ToString()) Then

                    ' Image data is not corrupted
                End If

            End If

            fs.Close()

        End If
    End Sub

Add the following Imports to the top of Form1.vb code:

VB.NET
Imports System.Drawing
Imports System.IO

Build the Solution (F7), and run the application (F5).

Click Button1 and browse for the DWG file, and open it.

Explanation

Autocad DWG Structure

As the Open Design Alliance discovered, at &HD there is a seeker, that is a 4 byte long absolute address of the beginning of the image data. So we open a file stream and create a binary reader from it to access the DWG file. Then we seek the address &HD, and read the Int32 seek position of the first sentinel for the preview Image data.

A sentinel is just a constant 16 long byte array used to check the file for consistency, or as a marker. We have one for the beginning and another for the end of the image.

So, again we seek the image position imgPos and read the 16 bytes of the first sentinel and compare it.

Then after the sentinel, there comes a header Int32 value with the overall size of the image area. This is followed by a Byte which is a counter of the number of image data formats present (1-3) imgPresent.

We repeat imgPresent times, to get first the image header start position in the file, and header size; second, the BMP data start position and size; and third, the WMF data position and size, if present.

If we find a BMP data present, we seek the start position of the BMP data imgBmpStart, then we allocate a byte array buffer to get the BMP data from the binary reader.

We create a Memory Stream from the BMP data buffer and create a Bitmap from that memory stream. We have to use a trick to do so as will be explained below.

Finally we read the ending sentinel again for checking integrity.

BMP Structure

The BMP structure, as it is interpreted by the Bitmap class constructor when reading from a Stream, seems to be somewhat different from that of the actual data present in the DWG file.

Standard BMP Structure

  1. File Information header: a 14 bytes long header record, with the following structure:

    WORD bfType , It must be set to &H4D42, 
        to indicate that the file is a Bitmap
    DWORD bfSize , Total size of the file
    WORD bfReserved1
    WORD bfReserved2
    DWORD bfOffBits , Offset of the image data from start of the file
  2. Bitmap Information Header
    DWORD biSize , Size of the image header, should be 40.
    LONG biWidth , Width of the image in pixels.
    LONG biHeight , Height, this is also the stride of the bitmap.
    WORD biPlanes , number of bit planes, always 1
    WORD biBitCount , bits per pixel 1, 4, 8 or 24
    DWORD biCompression , 0 for no compression.
    DWORD biSizeImage , Size of image data
    LONG biXPelsPerMeter , X resolution
    LONG biYPelsPerMeter , Y resolution
    DWORD biClrUsed, Number of colours
    DWORD biClrImportant , number of colour indexes that are important. 
        If 0, all indexes are important.
  3. Colour palette
  4. Actual Image data

DWG BMP Structure

Tampering with the data present in the DWG binary file, we found that the File Information Header of the BMP is not present. So when passing the structure as a Memory Stream to the Bitmap constructor, we get an error of "Not a valid parameter passed."

So we came to a solution by creating this header in the first 14 bytes of the tempPixelData buffer. We set the bytes corresponding to the first WORD bfType, to &H4D42. Which is a constant indicating this a BMP file. And we set the bytes corresponding to the final WORD bfOffBits to &H0436, which is a constant for the 256 BMP image data offset.

Then we read the image data, append it to the the tempPixelData buffer array, after the header, creates a Memory Stream from it and pass it to the Bitmap constructor which is able to read the BMP as you can see in the sample image at top of this article.

Points of Interest

That's it! For a complete BMP structure, see How to Load a Bitmap by Mark Bernard.

History

  • Nov. 18, 2007 -- Original posting
  • Nov. 22, 2007 -- Image is fully interpreted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer CIMEX S.A.
Cuba Cuba
Rodolfo Ortega is a Cuban Computer Scientist. He works as IT Auditor for the CIMEX S.A. subsidiary in Holguin, Cuba. He lives and works in Holguin, in the eastern part of the island of Cuba.

You can contact him at rodolfom[]cimex.com.cu for any personal message: Ideas on new articles, bibliography about new APIs, questions, are wellcome.

Submit questions related with current article to the article forum.

Comments and Discussions

 
GeneralMy vote of 5 Pin
bullnan16-Aug-21 22:50
bullnan16-Aug-21 22:50 
QuestionQuestion on Older DWG files (R11/12) Pin
Faisal Edakkattil12-Feb-19 19:08
Faisal Edakkattil12-Feb-19 19:08 
Questionhow to compile it to dll ? Pin
k4noe20-Jun-13 18:58
k4noe20-Jun-13 18:58 
Is there posibility that it can compiled as dll file ?
QuestionQ Pin
zero.zero19-Dec-12 20:49
zero.zero19-Dec-12 20:49 
AnswerRe: Q Pin
rmortega7717-Jan-13 14:02
rmortega7717-Jan-13 14:02 
GeneralDFX Pin
Mostafa Elsadany3-Sep-10 15:24
Mostafa Elsadany3-Sep-10 15:24 
GeneralRe: DFX Pin
wout de zeeuw2-Jul-12 23:49
wout de zeeuw2-Jul-12 23:49 
Generalgood job Pin
Mostafa Elsadany26-Aug-10 19:41
Mostafa Elsadany26-Aug-10 19:41 
Generaldwg Pin
Mostafa Elsadany18-Aug-10 17:13
Mostafa Elsadany18-Aug-10 17:13 
QuestionBMP Size Question [modified] Pin
Ozan Müyesseroğlu2-Jun-10 23:33
Ozan Müyesseroğlu2-Jun-10 23:33 
Generalit worked! Pin
jp2code9-Sep-09 8:28
professionaljp2code9-Sep-09 8:28 
Questionerror of add "''Bitmap Information Header " Pin
Hello_alex22-Sep-08 16:51
Hello_alex22-Sep-08 16:51 
GeneralInterested in control to view wmf dwg Pin
sanong18-Nov-07 15:05
sanong18-Nov-07 15:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.