Jump to content

How to Copy MetaData (Exif And more) from a Photo (Jpeg) to Another Photo (Jpeg) ?


Go to solution Solved by KaFu,

Recommended Posts

#include <GDIPlus.au3>
#include <WinAPIHObj.au3>



Local $sfile = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)");


_Clone_MetaData($sfile)

Func _Clone_MetaData($sfile, $sMasterfile = @ScriptDir & "\IMG_Base.JPG")
    Local $hImage1, $hImage2, $hGraphic

    If Not FileExists($sfile) Then Return 0
    If Not FileExists($sMasterfile) Then Return 0

    ; Initialize GDI+ library
    _GDIPlus_Startup()

    $hImage2 = _GDIPlus_ImageLoadFromFile($sfile)
    $hImage1 = _GDIPlus_ImageLoadFromFile($sMasterfile)

    Local $aDim = _GDIPlus_ImageGetDimension ( $hImage1 )
    Local $iXUR = $aDim[0]
    Local $iYDR = $aDim[1]

    ; Draw one image in another
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage1)


    _GDIPlus_GraphicsSetInterpolationMode ( $hGraphic, 7 )
    _GDIPlus_DrawImagePoints($hGraphic, $hImage2, 0, 0, $iXUR, 0, 0, $iYDR)


    ; Save resultant image
    Local $sNewFile = FileSaveDialog("Choose a filename.", @ScriptDir, "JPEG files (*.jpg;*.jpeg)", 2, "IMG_Cloned.JPG"
    _GDIPlus_ImageSaveToFile($hImage1, $sNewFile)

    ; Clean up resources
    _GDIPlus_ImageDispose($hImage1)
    _GDIPlus_ImageDispose($hImage2)


    ; Shut down GDI+ library
    _GDIPlus_Shutdown()

    If FileExists($sNewFile) Then ShellExecute($sNewFile)
EndFunc   ;==>_Clone_MetaData

i use this sample for clone metadata, i need a better solution 

Link to post
Share on other sites
2 hours ago, Parsix said:

i use this sample for clone metadata, i need a better solution

Possibly, the use of the well-known command line tool  exiftool  (by Phil Harvey) would be a suitable solution.

exiftool -TagsFromFile fromImage.jpg toImage.jpg

Additional info on -TagsFromFile, see for example : https://exiftool.org/forum/index.php?topic=10189.0

 

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Link to post
Share on other sites
1 minute ago, Musashi said:

Possibly, the use of the well-known command line tool  exiftool  (by Phil Harvey) would be a suitable solution.

exiftool -TagsFromFile fromImage.jpg toImage.jpg

Additional info on -TagsFromFile, see for example : https://exiftool.org/forum/index.php?topic=10189.0

 

thanks, do you have a solution without exiftool (any external app or dll) ?

Link to post
Share on other sites
35 minutes ago, Parsix said:

thanks, do you have a solution without exiftool (any external app or dll) ?

Unfortunately, no external app or dll I have worked with so far. I use exiftool only for removing metadata from images, but I know, that the tool has many more powerful features. The command line variant does not even need to be installed.

By the way : what difficulties do you expect regarding the use of exiftool ?

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Link to post
Share on other sites

I'm looking to use the Autoit programming language talent instead of the side plugin command line

 

Accurate copy and reproduction of metadata in the new file

Link to post
Share on other sites
  • Solution
Posted (edited)

I use something like this in my program BIC (as a fallback for the prerfered ExifTool method).

Although I too prefer ExifTool over this, as GDI JPG processing is not lossless.

I process the files, save them as PNG, use an external tool called jpge.exe to encode a jpg from the png and then set the Exif infos with ExifTool.

; jpg with GDI always bad
; https://www.autoitscript.com/forum/topic/187573-perplexed-by-jpg-conversion-result/
; "The problem (after reading your edits) is probably due to the fact that GDI+ uses 4:1:1 subsampling for JPG files in it's default Encoder."
;  The issue is that GDI+ enables chroma subsampling which modifies the color values accordingly.

#include <GDIPlus.au3>
#include <WinAPIHObj.au3>

; Initialize GDI+ library
_GDIPlus_Startup()

Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)") ;
If @error Then Exit

Local $sfile_output = StringReplace($sfile_input, ".", "_Out.")

_Clone_MetaData($sfile_input, $sfile_output)

If FileExists($sfile_output) Then ShellExecute($sfile_output)

; Shut down GDI+ library
_GDIPlus_Shutdown()


Func _Clone_MetaData($sfile_input, $sfile_output)

    If Not FileExists($sfile_input) Then Return 0

    Local $a_DateTimeOriginal = FileGetTime($sfile_input)
    Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5]

    Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input)

    Local $aDim = _GDIPlus_ImageGetDimension($hImage)
    Local $a_Local_Input_EXIF_Dimensions[2]
    $a_Local_Input_EXIF_Dimensions[0] = $aDim[0]
    $a_Local_Input_EXIF_Dimensions[1] = $aDim[1]

    Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage)

    Local $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $aDim[0], $aDim[1], $GDIP_PXF32ARGB)

    If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties)

    Local $sString = "Code ripped from BIC, https://funk.eu"
    Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0131, $sString, 2, StringLen($sString) + 1)         ; Software > 2=ASCII

    If IsArray($a_Local_Input_EXIF_Dimensions) Then
        $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0100, $a_Local_Input_EXIF_Dimensions[0], 4, -1)       ; ImageWidth > 4=UINT
    EndIf

    If IsArray($a_Local_Input_EXIF_Dimensions) Then
        $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0101, $a_Local_Input_EXIF_Dimensions[1], 4, -1)       ; ImageHeight > 4=UINT
    EndIf

    $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC

    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0132, $sString, 2, StringLen($sString) + 1)         ; 0x0132 ModifyDate > 2=ASCII

    $sString = "Custom User Comment"
    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9286, $sString, 2, StringLen($sString) + 1)         ; 0x9286 UserComment > 2=ASCII

    If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1)         ; 0x9003 DateTimeOriginal > 2=ASCII

    _GDIPlus_ImageSaveToFile($hClone, $sfile_output)

    ; Clean up resources
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_BitmapDispose($hClone)

EndFunc   ;==>_Clone_MetaData





Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties)
    For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1
        Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]]
        cGDIPlus_ImageSetPropertyItem($hClone, $aInput)
    Next
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Array

Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength)
    ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
    #cs
        0x0131 > Software
        0x010e > ImageDescription
        0x9286 > UserComment
        0x0132 > ModifyDate
        0x9003 > DateTimeOriginal   string  ExifIFD     (date/time when original image was taken)
    #ce
    Local $a_PropertyValue = [$s_PropertyItem_Value]
    Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue]
    Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem)
    Return SetError(@error, @extended, $iRes)
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Ex

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values) <> $iqValues Then
                Return SetError(405, 0, False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    ; _ConsoleWrite($iId & @TAB & $iType & @CRLF)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=2; no property items found
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

 

Edited by KaFu
Link to post
Share on other sites
Posted (edited)
7 hours ago, KaFu said:

I use something like this in my program BIC (as a fallback for the prerfered ExifTool method).

Although I too prefer ExifTool over this, as GDI JPG processing is not lossless.

I process the files, save them as PNG, use an external tool called jpge.exe to encode a jpg from the png and then set the Exif infos with ExifTool.

; jpg with GDI always bad
; https://www.autoitscript.com/forum/topic/187573-perplexed-by-jpg-conversion-result/
; "The problem (after reading your edits) is probably due to the fact that GDI+ uses 4:1:1 subsampling for JPG files in it's default Encoder."
;  The issue is that GDI+ enables chroma subsampling which modifies the color values accordingly.

#include <GDIPlus.au3>
#include <WinAPIHObj.au3>

; Initialize GDI+ library
_GDIPlus_Startup()

Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)") ;
If @error Then Exit

Local $sfile_output = StringReplace($sfile_input, ".", "_Out.")

_Clone_MetaData($sfile_input, $sfile_output)

If FileExists($sfile_output) Then ShellExecute($sfile_output)

; Shut down GDI+ library
_GDIPlus_Shutdown()


Func _Clone_MetaData($sfile_input, $sfile_output)

    If Not FileExists($sfile_input) Then Return 0

    Local $a_DateTimeOriginal = FileGetTime($sfile_input)
    Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5]

    Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input)

    Local $aDim = _GDIPlus_ImageGetDimension($hImage)
    Local $a_Local_Input_EXIF_Dimensions[2]
    $a_Local_Input_EXIF_Dimensions[0] = $aDim[0]
    $a_Local_Input_EXIF_Dimensions[1] = $aDim[1]

    Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage)

    Local $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $aDim[0], $aDim[1], $GDIP_PXF32ARGB)

    If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties)

    Local $sString = "Code ripped from BIC, https://funk.eu"
    Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0131, $sString, 2, StringLen($sString) + 1)         ; Software > 2=ASCII

    If IsArray($a_Local_Input_EXIF_Dimensions) Then
        $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0100, $a_Local_Input_EXIF_Dimensions[0], 4, -1)       ; ImageWidth > 4=UINT
    EndIf

    If IsArray($a_Local_Input_EXIF_Dimensions) Then
        $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0101, $a_Local_Input_EXIF_Dimensions[1], 4, -1)       ; ImageHeight > 4=UINT
    EndIf

    $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC

    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0132, $sString, 2, StringLen($sString) + 1)         ; 0x0132 ModifyDate > 2=ASCII

    $sString = "Custom User Comment"
    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9286, $sString, 2, StringLen($sString) + 1)         ; 0x9286 UserComment > 2=ASCII

    If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1)         ; 0x9003 DateTimeOriginal > 2=ASCII

    _GDIPlus_ImageSaveToFile($hClone, $sfile_output)

    ; Clean up resources
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_BitmapDispose($hClone)

EndFunc   ;==>_Clone_MetaData





Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties)
    For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1
        Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]]
        cGDIPlus_ImageSetPropertyItem($hClone, $aInput)
    Next
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Array

Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength)
    ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
    #cs
        0x0131 > Software
        0x010e > ImageDescription
        0x9286 > UserComment
        0x0132 > ModifyDate
        0x9003 > DateTimeOriginal   string  ExifIFD     (date/time when original image was taken)
    #ce
    Local $a_PropertyValue = [$s_PropertyItem_Value]
    Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue]
    Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem)
    Return SetError(@error, @extended, $iRes)
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Ex

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values) <> $iqValues Then
                Return SetError(405, 0, False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    ; _ConsoleWrite($iId & @TAB & $iType & @CRLF)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=2; no property items found
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

 

Very nice

can be resized with keep quality and metadat?

Edited by Parsix
Link to post
Share on other sites

As the picture is re-encoded, JPG is not lossless and you can not determine the initial quality setting of a JPG, the quality will always be reduced.

#include <GDIPlus.au3>
#include <WinAPIHObj.au3>
#include <Array.au3>

; Initialize GDI+ library
_GDIPlus_Startup()

Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)")
If @error Then Exit

Local $sfile_output = StringReplace($sfile_input, ".", "_Out.")

_Clone_MetaData($sfile_input, $sfile_output, Default, 500)

If FileExists($sfile_output) Then ShellExecute($sfile_output)

; Shut down GDI+ library
_GDIPlus_Shutdown()


Func _Clone_MetaData($sfile_input, $sfile_output, $iWidth = Default, $iHeight = Default, $i_JPG_Quality = 95)

    If Not FileExists($sfile_input) Then Return 0

    Local $a_DateTimeOriginal = FileGetTime($sfile_input)
    Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5]

    Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input)

    Local $aDim = _GDIPlus_ImageGetDimension($hImage)

    Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $i_Local_Input_EXIF_Properties_Orientation = 1
    For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1
        If $a_Local_Input_EXIF_Properties[$i][0] = 0x0112 Then ; Orientation
            $i_Local_Input_EXIF_Properties_Orientation = $a_Local_Input_EXIF_Properties[$i][3]
            $i_Local_Input_EXIF_Properties_Orientation = $i_Local_Input_EXIF_Properties_Orientation[0]
            ExitLoop
        EndIf
    Next
    Switch $i_Local_Input_EXIF_Properties_Orientation
        Case 5 To 9
            ConsoleWrite("! Exif orientation applied" & @CRLF)
            Local $iBuffer = $aDim[0]
            $aDim[0] = $aDim[1]
            $aDim[1] = $iBuffer
    EndSwitch

    Local $iRatio = $aDim[0] / $aDim[1]

    If $iWidth <> Default Or $iHeight <> Default Then
        If $iWidth = Default Then
            $iWidth = $iHeight / $iRatio
        EndIf

        If $iHeight = Default Then
            $iHeight = $iWidth * $iRatio
        EndIf

        Local $hBitmap_Scaled = _GDIPlus_ImageResize($hImage, $iWidth, $iHeight)

        ConsoleWrite($aDim[0] & @TAB & $aDim[1] & @TAB & $aDim[0] / $aDim[1] & @TAB & $iWidth & @TAB & $iHeight & @TAB & $hBitmap_Scaled & @CRLF)

        _GDIPlus_ImageDispose($hImage)
        $hImage = $hBitmap_Scaled

    EndIf

    If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hImage, $a_Local_Input_EXIF_Properties)

    Local $sString = "Code ripped from BIC, https://funk.eu"
    Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0131, $sString, 2, StringLen($sString) + 1)         ; Software > 2=ASCII

    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0100, $iWidth, 4, -1)       ; ImageWidth > 4=UINT

    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0101, $iHeight, 4, -1)       ; ImageHeight > 4=UINT

    $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC

    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0132, $sString, 2, StringLen($sString) + 1)         ; 0x0132 ModifyDate > 2=ASCII

    $sString = "Original Image Dimensions = " & $aDim[0] & "x" & $aDim[1]
    $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x9286, $sString, 2, StringLen($sString) + 1)         ; 0x9286 UserComment > 2=ASCII

    If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1)         ; 0x9003 DateTimeOriginal > 2=ASCII

    Local $sCLSID = _GDIPlus_EncodersGetCLSID("JPG")
    Local $tParams = _GDIPlus_ParamInit(1)
    Local $tData = DllStructCreate("int Quality")
    DllStructSetData($tData, "Quality", $i_JPG_Quality)
    Local $pData = DllStructGetPtr($tData)
    _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData)
    Local $pParams = DllStructGetPtr($tParams)
    _GDIPlus_ImageSaveToFileEx($hImage, $sfile_output, $sCLSID, $pParams)

    ; Clean up resources
    _GDIPlus_ImageDispose($hImage)

EndFunc   ;==>_Clone_MetaData





Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties)
    For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1
        Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]]
        cGDIPlus_ImageSetPropertyItem($hClone, $aInput)
    Next
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Array

Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength)
    ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
    #cs
        0x0131 > Software
        0x010e > ImageDescription
        0x9286 > UserComment
        0x0132 > ModifyDate
        0x9003 > DateTimeOriginal   string  ExifIFD     (date/time when original image was taken)
    #ce
    Local $a_PropertyValue = [$s_PropertyItem_Value]
    Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue]
    Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem)
    Return SetError(@error, @extended, $iRes)
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem_Ex

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values) <> $iqValues Then
                Return SetError(405, 0, False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    ; _ConsoleWrite($iId & @TAB & $iType & @CRLF)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=2; no property items found
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    ;--------- should be moved to GDIPlusConstants.au3
    Local Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
            $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
            $GDIP_PROPERTYTAGTYPESRATIONAL = 10
    ;-------------------------------------------------

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

 

Link to post
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
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By jloyzaga
      I've got this project to check a url's metadata and compare it against what a file says it should be. I have done a search on this site for metadata and get a long list of specific calls to get specific metadata items but I'm wondering if there is a way to get the full list of metadata for a url?
       
      Even where to search would be a great help
      Thanks
       
      Joe
×
×
  • Create New...