Jump to content

Put the DWG file image in a GUI


Recommended Posts

Hi,

I'm trying to retrieve the image (icon) from a DWG file (AutoCAD), but I can't find a way to do it.

Attached is a DWG file of a door. This image can be seen in the Windows file explorer if you display the icons. (However, I think you need compatible software to display the image, otherwise only the file format icon appears.

Here's how it looks in the file explorer, I'd like to retrieve this image in a GUI as you can do with a classic image file.

Is this possible, and if so, how do I go about it?

Thank you for your help.

Eric

Door.png

Door.dwg

Link to comment
Share on other sites

Normally when the preview is visible in Windows Explorer that's because the program itself installed a Shell Extension to enable Explorer to do so.

My program SMF can extract images created by a Shell Extension, give it a try.

Search a directory with DWG files, open the SMF search report, switch to thumbnails view (e.g. by pressing F4), select the files (multi-select with shift or ctrl), right click > "Export Thumbnails to".

Edited by KaFu
Link to comment
Share on other sites

11 minutes ago, erix said:

Hi Andreik,

Too bad, I'd understood that a PNG image was embedded in the dwg file and I'd hoped that it was possible to extract this image.

https://blog.jtbworld.com/2020/09/autocad-thumbsizedwg-thumbnail-preview.html

Obviously not.
I don't feel able to use another application to do this. Never mind, I'll do without the preview. 😒

😉

Eric

You can extract the PNG from that file but you have to understand first the header of a PNG file to get the binary size. Afterwards you can extract the PNG from that file.

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Hi Kafu,

I tried your first method with SMF, but it extracts the file format icon and not the thumbnail.

Hi UEZ, unfortunately, I realize that I don't have the skills to do this. I've opened the dwg file I attached to my first message with notepad and I can't see what to do with the data.
I did find an IHDR that would correspond to the location of the PNG data, but then I see lots of symbols that I don't understand. This is way beyond my skills.

Kafu, I've tried Bitmaprip and it seems to work. It extracts several images, some of which are unreadable, but there's one image with the thumbnail.
Maybe there's something to be done with it.
Well, I would have preferred a UDF so as not to depend on an application, but I can't have everything.

By the way, if someone much more competent than me feels like starting to create this type of UDF, it could be useful to other users.
Well, I say that, but I know it's not as simple as that.

I don't know if it's even possible

Thank you for your help

Link to comment
Share on other sites

This works fine for me.

#include <Binary.au3>
; https://www.autoitscript.com/forum/topic/131037-binary-udf/?do=findComment&comment=1339527


_Extract_PNG(@ScriptDir & "\Door.dwg")
_Extract_PNG(@ScriptDir & "\test.png")

Func _Extract_PNG($sFile_Input, $sFile_Output = "")
    If Not $sFile_Output Then $sFile_Output = $sFile_Input & "_extracted.png"

    Local $hFile = FileOpen($sFile_Input, 16)
    If $hFile = -1 Then Return SetError(1)

    Local $bContent = FileRead($hFile)
    FileClose($hFile)

    ; https://www.w3.org/TR/png/#5PNG-file-signature
    Local $i_Position_PNG_Header = _BinaryInBin($bContent, Binary("0x89504E470D0A1A0A"))

    If $i_Position_PNG_Header Then

        ; https://www.w3.org/TR/png/#11IEND
        ; https://stackoverflow.com/questions/51719833/whats-wrong-with-my-png-idat-chunk
        ; 49454E44  // IEND start - 4 bytes
        ; AE426082  // CRC of IEND chunk, 4 bytes
        Local $i_Position_PNG_IEND_Trailer = _BinaryInBin($bContent, Binary("0x49454E44AE426082"))

        If $i_Position_PNG_IEND_Trailer And ($i_Position_PNG_Header < $i_Position_PNG_IEND_Trailer) Then
            ConsoleWrite("+ PNG data found" & @CRLF)

            If FileExists($sFile_Output) Then
                ConsoleWrite("! Output file '" & $sFile_Output & "' existed, PNG extraction skipped " & @CRLF)
                Return SetError(2)
            EndIf
            $hFile = FileOpen($sFile_Output, 2 + 16)
            If $hFile = -1 Then Return SetError(3)

            FileWrite($hFile, BinaryMid($bContent, $i_Position_PNG_Header, $i_Position_PNG_IEND_Trailer - $i_Position_PNG_Header + 8)) ; +8 to include trailing PNG_IEND and CRC of IEND chunk
            FileClose($hFile)
            Return 1
        EndIf

    EndIf

    ConsoleWrite("- No PNG data found" & @CRLF)
    Return 0

EndFunc   ;==>_Extract_PNG

Edit: Added +8 to binarymid length to include trailing IEND and CRC of IEND chunk and some error checking.

Edited by KaFu
Link to comment
Share on other sites

@KaFu was faster.

 

Here my hack:

;Coded by UEZ build 2023-08-04
#include <WinAPIFiles.au3>
#include <WinAPIHObj.au3>

Global $sFile = FileOpenDialog("Select a file", "", "All (*.*)", $FD_FILEMUSTEXIST), $sSavePath = "c:\Temp\"
If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "No file has been selected", 10)

ExtractPNGFromFile($sFile, $sSavePath)

Func ExtractPNGFromFile($sFileIn, $sPathOut = @ScriptDir, $bOverwrite = True)
    Local $iSize = FileGetSize($sFile), $nBytes
    Local $tBuffer = DllStructCreate("byte bin[" & $iSize & "]")
    Local Const $hFile = _WinAPI_CreateFile($sFile, 2, 2)
    If @error Or Not $hFile Then Return SetError(1, 0, 0)
    _WinAPI_ReadFile($hFile, $tBuffer, $iSize, $nBytes)
    _WinAPI_CloseHandle($hFile)

    Local $sBin = Binary($tBuffer.bin), $iPosS, $iPosE, $iPosN, $p = 1, $iStart, $iEnd, $c = 1, $cc, $sFilename
    If StringRight($sPathOut, 1) <> "\" Then $sPathOut = $sPathOut & "\"

    Do
        $iPosS = StringInStr($sBin, "89504E470D0A1A0A", 0, 1, $p)
        If $iPosS Then
            $iPosN = StringInStr($sBin, "89504E470D0A1A0A", 0, 1, $iPosS + 16)
            $iStart = BitShift($iPosS, 1)
            $iPosE = StringInStr($sBin, "49454E44AE426082", 0, 1, $iPosS + 16)
            ConsoleWrite($c & ": " & Hex(BitShift($iPosS, 1), 8) & ", " & Hex(BitShift($iPosN, 1), 8) & ", " & Hex(BitShift($iPosE, 1), 8) & @CRLF)
            If $iPosN And $iPosE > $iPosN Then
                $p = $iPosN
            Else
                If $iPosE Then
                    $sFilename = $sPathOut & "PNG_extracted" & StringFormat("%02i", $c) & ".png"
                    If Not $bOverwrite Then
                        $cc = $c
                        While FileExists($sFilename)
                            $sFilename = $sPathOut & "PNG_extracted" & StringFormat("%02i", $cc) & ".png"
                            $cc += 1
                        WEnd
                    EndIf
                    SavePNG($sFilename, $tBuffer.bin, $iStart, BitShift($iPosE, 1) + 8)
                    $p = $iPosE + 16
                    $c += 1
                Else
                    ExitLoop
                EndIf
            EndIf
        EndIf
    Until Not $iPosS
    $sBin = 0
    ConsoleWrite("Saved " & $c - 1 & " PNG files" & @CRLF)
EndFunc

Func SavePNG($sName, $tBin, $s, $e, $bOverwrite = True)
    Local $l = $e - $s, $nBytes
    Local $tBuffer = DllStructCreate("byte bin[" & $l & "]")
    $tBuffer.bin = BinaryMid($tBin, $s, $l)
    Local $hFile = _WinAPI_CreateFile($sName, $bOverwrite ? 1 : 0)
    If @error Or Not $hFile Then Return SetError(1, 0, 0)
    _WinAPI_WriteFile($hFile, $tBuffer, $l, $nBytes)
    _WinAPI_CloseHandle($hFile)
    ConsoleWrite($nBytes & " bytes written (found at address " & Hex($s - 1, 8) & ")" & @CRLF)
EndFunc

My code finds 3 PNGs in Door.dwg but 2 are corrupted. Finds now only 1 PNG image.

Btw, it should find more than 1 PNG image if the file should contain several PNGs.

Edited by UEZ
Code update

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

I don't know why there are several pngs in the dwg file, some of which don't seem to work, but never mind, the first one in the list is the one for the thumbnail I'm interested in.

In any case you are very strong, congratulations. 😉

@KaFu, it works very well.

@UEZ, I don't know why but I always get this result 

Saved 0 PNG files

For the past 10 years, images in dwg files have been in png format, previously they were in BMP format.

To be able to extract both types, is it possible to locate and write the file for a BMP format?

Many thanks

Link to comment
Share on other sites

2 hours ago, erix said:

I don't know why there are several pngs in the dwg file, some of which don't seem to work, but never mind, the first one in the list is the one for the thumbnail I'm interested in.

In any case you are very strong, congratulations. 😉

@KaFu, it works very well.

@UEZ, I don't know why but I always get this result 

Saved 0 PNG files

For the past 10 years, images in dwg files have been in png format, previously they were in BMP format.

To be able to extract both types, is it possible to locate and write the file for a BMP format?

Many thanks

Mea culpa, the updated code had a bug - should be fixed now. Now it finds only 1 image in your dqg file. And yes, it is possible to search for a BMP header, too.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Checked for that too while researching PNG extraction 😉.

#include <Binary.au3>
; https://www.autoitscript.com/forum/topic/131037-binary-udf/?do=findComment&comment=1339527

_Extract_BMP(@ScriptDir & "\test.bmp")

Func _Extract_BMP($sFile_Input, $sFile_Output = "")
    If Not $sFile_Output Then $sFile_Output = $sFile_Input & "_extracted.bmp"

    Local $hFile = FileOpen($sFile_Input, 16)
    If $hFile = -1 Then Return SetError(1)

    Local $bContent = FileRead($hFile)
    FileClose($hFile)

    Local $i_BMP_Position_Header = _BinaryInBin($bContent, Binary("0x424D"))
    Local $i_BMP_Total_Size = Int(BinaryMid($bContent, $i_BMP_Position_Header + 2, 4))

    If $i_BMP_Position_Header And $i_BMP_Total_Size Then
        ConsoleWrite("+ BMP data found" & @CRLF)

        If FileExists($sFile_Output) Then
            ConsoleWrite("! Output file '" & $sFile_Output & "' existed, BMP extraction skipped " & @CRLF)
            Return SetError(2)
        EndIf
        $hFile = FileOpen($sFile_Output, 2 + 16)
        If $hFile = -1 Then Return SetError(3)

        FileWrite($hFile, BinaryMid($bContent, $i_BMP_Position_Header, $i_BMP_Total_Size))
        FileClose($hFile)
        Return 1
    EndIf

    ConsoleWrite("- No BMP data found" & @CRLF)
    Return 0

EndFunc   ;==>_Extract_BMP

Edit: And for JPG too while I'm at it.

#include <Binary.au3>
; https://www.autoitscript.com/forum/topic/131037-binary-udf/?do=findComment&comment=1339527

_Extract_JPG(@ScriptDir & "\test.jpg")

Func _Extract_JPG($sFile_Input, $sFile_Output = "")
    If Not $sFile_Output Then $sFile_Output = $sFile_Input & "_extracted.jpg"

    Local $hFile = FileOpen($sFile_Input, 16)
    If $hFile = -1 Then Return SetError(1)

    Local $bContent = FileRead($hFile)
    FileClose($hFile)

    Local $i_JPG_Position_Header = _BinaryInBin($bContent, Binary("0xFFD8"))

    If $i_JPG_Position_Header Then

        Local $i_JPG_Position_End = _BinaryInBin($bContent, Binary("0xFFD9"))

        If $i_JPG_Position_End And ($i_JPG_Position_Header < $i_JPG_Position_End) Then
            ConsoleWrite("+ JPG data found" & @CRLF)

            If FileExists($sFile_Output) Then
                ConsoleWrite("! Output file '" & $sFile_Output & "' existed, JPG extraction skipped " & @CRLF)
                Return SetError(2)
            EndIf
            $hFile = FileOpen($sFile_Output, 2 + 16)
            If $hFile = -1 Then Return SetError(3)

            FileWrite($hFile, BinaryMid($bContent, $i_JPG_Position_Header, $i_JPG_Position_End - $i_JPG_Position_Header + 2))
            FileClose($hFile)
            Return 1
        EndIf

    EndIf

    ConsoleWrite("- No JPG data found" & @CRLF)
    Return 0

EndFunc   ;==>_Extract_JPG

 

Edited by KaFu
Link to comment
Share on other sites

@KaFu finding BMP might be a little more complicated -> https://en.wikipedia.org/wiki/BMP_file_format Searching only for "BM" might be not accurate and the probability of BM in a file should also be high, so that wrong bitmaps are saved when the size somehow fits.

 

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

You're right, "BM" is very generic.

But I can't see a more specific pattern in the BMP file structure.

Btw your approach is smarter, to look for multiple picture files, mine only looks for the first one, was too lazy to iterate 😏.

Maybe the only was is to iterate and extract all possible solutions, and manually verify if a picture is valid or corrupt?

Edit:

Maybe additionally check if the leading values of the DIB Header (Width, Height, Planes, Bits per Pixel, etc.) contain plausible values?

Edited by KaFu
Link to comment
Share on other sites

Maybe something like this here to be more accurate?

;BM = 424D, BA = 4241, CI = 4349, CP = 4350, IC = 4943, PT = 5054
$bin = "0x1234424D460000000000000036000000" ;shorten file content
$aResult = StringRegExp($bin, ".*(424D|4241|4349|4350|4943|5054)[[:xdigit:]]{8}(00000000).+", 1)
If Not @error Then ConsoleWrite("BMP found" & @CRLF)

 

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

I've tested your code, but it doesn't detect the image in BMP format.

Maybe it's not organized like a classic BMP. I should have sent you an older dwg to test. This has now been done as an attachment.

While searching, I came across this article:

https://www.codeproject.com/Articles/21356/VBDwgImageExtractor


Which then sent me to this documentation explaining the dwg specification and I came across page 93 which seems to give the hexadecimal codes to look for and in testing a few, they are well recognized. However, I didn't understand how to find the header or the data.

https://static.opendesign.com/files/guestdownloads/OpenDesign_Specification_for_.dwg_files.pdf

According to this document, the preview can also be stored in WMF format, but this is rarer.

I'd be interested to know. I'm aware that I'm interested in something that's not really at my level, but I'd like to get as far as I can. 😉

Thank you for your help.

Door2.dwg

Link to comment
Share on other sites

Interesting challenge 😉. The BMP does not contain a file information header (leading 14 bytes), but there's an anchor pattern to look for.

; #include <Array.au3>
#include <Binary.au3>
; https://www.autoitscript.com/forum/topic/131037-binary-udf/?do=findComment&comment=1339527

_Extract_BMP_from_Old_DWG_Format(@ScriptDir & "\Door2_OLD_BMP_FORMAT.dwg")

Func _Extract_BMP_from_Old_DWG_Format($sFile_Input)
    Local $hFile = FileOpen($sFile_Input, 16)
    If $hFile = -1 Then Return SetError(1)

    Local $bContent = FileRead($hFile)
    FileClose($hFile)

    Local $i_BMP_Read_Position = 0

    Local $i_BMP_Position_Header = _BinaryInBin($bContent, Binary("0x1F256D07D43628289D57CA3F9D44102B")) ; 16
    ConsoleWrite("$i_BMP_Position_Header= " & $i_BMP_Position_Header & @CRLF)
    $i_BMP_Read_Position = $i_BMP_Position_Header + 16

    Local $i_BMP_Total_Size = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
    ConsoleWrite("$i_BMP_Total_Size= " & $i_BMP_Total_Size & @CRLF)
    $i_BMP_Read_Position += 4

    Local $i_BMP_Counter = Int(BinaryMid($bContent, $i_BMP_Read_Position, 1))
    ConsoleWrite("$i_BMP_Counter= " & $i_BMP_Counter & @CRLF)
    $i_BMP_Read_Position += 1

    If $i_BMP_Counter Then
        Local $a_BMP_Data[$i_BMP_Counter][8]
        #cs
            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
        #ce
        For $i = 0 To UBound($a_BMP_Data) - 1
            ConsoleWrite($i & @CRLF)

            ; ' Get image type
            ; Dim imgCode As Byte = r.ReadByte()
            Local $i_BMP_Image_Type = Int(BinaryMid($bContent, $i_BMP_Read_Position, 1))
            $i_BMP_Read_Position += 1

            Switch $i_BMP_Image_Type
                Case 1 ; Header data
                    $a_BMP_Data[$i][0] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4
                    $a_BMP_Data[$i][1] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4

                Case 2 ; BMP data
                    $a_BMP_Data[$i][2] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4
                    $a_BMP_Data[$i][3] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4

                    $a_BMP_Data[$i][4] = True

                Case 3 ; WMF data
                    $a_BMP_Data[$i][5] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4
                    $a_BMP_Data[$i][6] = Int(BinaryMid($bContent, $i_BMP_Read_Position, 4))
                    $i_BMP_Read_Position += 4

                    $a_BMP_Data[$i][7] = True

            EndSwitch

        Next

        ; _ArrayDisplay($a_BMP_Data)

        Local $iEnum = 0
        For $i = 0 To UBound($a_BMP_Data) - 1
            
            If Not $a_BMP_Data[$i][2] Then ContinueLoop ; no BMP data
            $iEnum += 1

            $hFile = FileOpen($sFile_Input & "_" & $iEnum & "_extracted.bmp", 2 + 16)
            If $hFile = -1 Then Return SetError(3)

            FileWrite($hFile, Binary("0x424d000000000000000036040000")) ; BMP Header

            FileWrite($hFile, BinaryMid($bContent, $a_BMP_Data[$i][2] + 1, $a_BMP_Data[$i][3]))

            FileClose($hFile)

        Next

    EndIf

    Exit

EndFunc   ;==>_Extract_BMP_from_Old_DWG_Format

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...