Malicious Office (OOXML) — malware analysis report

Static analysis result for SHA-256 e79fb702799f8f90…

MALICIOUS

Office (OOXML)

303.1 KB Created: 2007-05-23 18:22:18 UTC Authoring application: Microsoft Office PowerPoint 16.0000 First seen: 2021-07-10
MD5: 975df88f2cb656e195be747ba6e65254 SHA-1: 844d5b11ae65ea235e0f704f3a4cfafbafe45aef SHA-256: e79fb702799f8f90ca11c6e86a5309381ebeae738bdb3633262641149c82f1de
178 Risk Score

Malware Insights

MITRE ATT&CK
T1059.005 Visual Basic T1059.003 Windows Command Shell T1204.002 Malicious File

The OOXML file contains VBA macros, including Auto_Open and Auto_Close functions, which are known to be used for malicious purposes. The presence of Shell() and CreateObject() calls, along with a reference to cmd.exe, strongly indicates that the macros are designed to execute arbitrary commands. The primary goal is likely to download and execute a second-stage payload, although the specific URL or command is not directly observable in the provided script excerpt. The script itself appears to be related to adding custom menu items for LaTeX equation insertion, but the malicious functionality is triggered by the Shell() call.

Heuristics 7

  • VBA project inside OOXML medium 5 related findings OOXML_VBA
    Document contains a VBA project — VBA macros present
  • Potential Shell call in VBA critical OLE_VBA_SHELL
    Potential Shell call in VBA
    Matched line in script
        On Error GoTo ShellError
        Shell """" & GetEditorPath() & """ """ & TempPath & FilePrefix & ".tex""", vbNormalFocus
  • CreateObject call high OLE_VBA_CREATEOBJ
    CreateObject call
    Matched line in script
        Const ForReading = 1, ForWriting = 2, ForAppending = 3
        Set fs = CreateObject("Scripting.FileSystemObject")
        If fs.FileExists(TempPath & FilePrefix & ".png") Then
  • cmd.exe reference in VBA high OLE_VBA_CMD
    cmd.exe reference in VBA
    Matched line in script
                ' Output Bounding Box to file and read back in the appropriate information
                RetValConv& = Execute("cmd /C """ & gs_command & """ -q -dBATCH -dNOPAUSE -sDEVICE=bbox " & FilePrefix & ".pdf 2> " & FilePrefix & ".bbx", TempPath, debugMode, TimeOutTime)
                If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".bbx")) Then
  • Auto_Open macro low OLE_VBA_AUTO
    Auto_Open macro
    Matched line in script
    Sub Auto_Open()
        ' Runs when the add-in is loaded
  • Auto_Close macro low OLE_VBA_AUTOCLOSE
    Auto_Close macro
    Matched line in script
    Sub Auto_Close()
        LatexForm.UnInitializeApp
  • Embedded URL info EMBEDDED_URL
    One or more URLs were extracted from the document. The URL itself is not a detection — see the per-URL labels for which channel (macro, JS, link annotation, document body, ...) reached each URL.
    URL http://www.vbaexpress.com/forum/showthread.php?47687-Addpicture-adds-the-picture-to-a-placeholder-rather-as-a-new-shape In document text (OOXML body / shared strings)
    • http://www.vbaexpress.com/forum/showthread.php?37561-Delete-empty-placeholdersIn document text (OOXML body / shared strings)
    • http://www.kbalertz.com/kb_145679.aspxIn document text (OOXML body / shared strings)
    • http://www.jonathanleroux.org/software/iguanatex/In document text (OOXML body / shared strings)
    • http://www.math.sci.hokudai.ac.jp/~abenori/soft/bin/TeX2img_2.0.2.zipIn document text (OOXML body / shared strings)
    • https://github.com/abenori/TeX2imgIn document text (OOXML body / shared strings)
    • http://www.texstudio.org/In document text (OOXML body / shared strings)
    • http://�www.vba��In document text (OOXML body / shared strings)
    • http://schemas.microsoft.com/office/2009/07/customuiIn document text (OOXML body / shared strings)
    • http://www.mvps.org/access/api/api0004.htmIn document text (OOXML body / shared strings)
    • http://www.mrexcel.com/forum/excel-questions/485489-resize-userform.htmlIn document text (OOXML body / shared strings)
    • http://www.ghostscript.com/download/gsdnld.htmlIn document text (OOXML body / shared strings)
    • http://www.imagemagick.org/script/binary-releases.phpIn document text (OOXML body / shared strings)
    • https://social.msdn.microsoft.com/Forums/en-US/7d584120-a929-4e7c-9ec2-9998ac639bea/mouse-scroll-in-userform-listbox-in-excel-2010?forum=isvvbaIn document text (OOXML body / shared strings)
    • http://creativecommons.org/licenses/by-sa/3.0/In document text (OOXML body / shared strings)

Extracted artifacts 2

Files carved from inside the sample during analysis.

FilenameKindSourceSize
macros.bas vba-macro oletools.olevba.extract_macros (decoded VBA source from OOXML) 172057 bytes
SHA-256: 36c897e663510e63c4537ff840f850105f36efc1061bc3dfe977d66e61fd4bdb
Preview script
First 1,000 lines of the extracted script
Attribute VB_Name = "LatexForm"
Attribute VB_Base = "0{EE134C5A-2F6C-4F0D-AEE2-DCB9B5DC0A20}{D10DD706-AD5A-423A-964A-C96E0275606B}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = False
Dim RegPath As String
    
Dim LaTexEngineList As Variant
Dim LaTexEngineDisplayList As Variant
Dim UsePDFList As Variant

'Dim NumberOfTemplates As Long
Dim TemplateSortedListString As String
Dim TemplateSortedList() As String
Dim TemplateNameSortedListString As String

Dim FormHeightWidthSet As Boolean

Dim theAppEventHandler As New AppEventHandler

Sub InitializeApp()
    Set theAppEventHandler.App = Application
    
    AddMenuItem "New Latex display...", "NewLatexEquation", 18 '226
    AddMenuItem "Edit Latex display...", "EditLatexEquation", 37
    AddMenuItem "Regenerate selection...", "RegenerateSelectedDisplaysNoChange", 19
    AddMenuItem "Vectorize selection...", "ConvertToEMF", 153
    AddMenuItem "Rasterize selection...", "ConvertToPNG", 931
    AddMenuItem "Settings...", "LoadSetTempForm", 548
    AddMenuItem "Insert vector file...", "RibbonInsertVectorGraphicsFile", 23
    
End Sub

Sub AddMenuItem(itemText As String, itemCommand As String, itemFaceId As Long)
    ' Check if we have already added the menu item
    Dim initialized As Boolean
    Dim bef As Integer
    initialized = False
    bef = 1
    Dim Menu As CommandBars
    Set Menu = Application.CommandBars
    For i = 1 To Menu("Insert").Controls.count
        With Menu("Insert").Controls(i)
            If .Caption = itemText Then
                initialized = True
                Exit For
            ElseIf InStr(.Caption, "Dia&gram") Then
                bef = i
            End If
        End With
    Next
    
    ' Create the menu choice.
    If Not initialized Then
        Dim NewControl As CommandBarControl
        Set NewControl = Menu("Insert").Controls.Add _
                              (Type:=msoControlButton, _
                               before:=bef, _
                               Id:=itemFaceId)
        NewControl.Caption = itemText
        NewControl.OnAction = itemCommand
        NewControl.Style = msoButton
    End If
End Sub

Sub UnInitializeApp()
    
    RemoveMenuItem "New Latex display..."
    RemoveMenuItem "Edit Latex display..."
    RemoveMenuItem "Regenerate selection..."
    RemoveMenuItem "Vectorize selection..."
    RemoveMenuItem "Rasterize selection..."
    RemoveMenuItem "Settings..."
    RemoveMenuItem "Insert vector file..."
    ' Clean up older versions
    RemoveMenuItem "Regenerate selected displays..."
    RemoveMenuItem "Convert to EMF..."
    RemoveMenuItem "Convert to PNG..."

    
End Sub

Sub RemoveMenuItem(itemText As String)
    Dim Menu As CommandBars
    Set Menu = Application.CommandBars
    For i = 1 To Menu("Insert").Controls.count
        If Menu("Insert").Controls(i).Caption = itemText Then
            Menu("Insert").Controls(i).Delete
            Exit For
        End If
    Next
    

End Sub


Private Sub ButtonCancel_Click()
    Unload LatexForm
    ' LatexForm.Hide
End Sub


Private Function IsPathWritable(TempPath As String) As Boolean
    FilePrefix = GetFilePrefix()
    
    Dim FName As String
    Dim FHdl As Integer
    FName = TempPath & FilePrefix & ".tmp"
    On Error GoTo TempFolderNotWritable
    FHdl = FreeFile()
    Open FName For Output Access Write As FHdl
    Print #FHdl, "TESTWRITE"
    Close FHdl
    IsPathWritable = True
    Kill FName
    
    On Error GoTo 0
    
    Exit Function

TempFolderNotWritable:
    IsPathWritable = False
End Function

Private Sub WriteLaTeX2File(TempPath As String, FilePrefix As String)
    Const ForReading = 1, ForWriting = 2, ForAppending = 3
    Set fs = CreateObject("Scripting.FileSystemObject")
    If fs.FileExists(TempPath & FilePrefix & ".png") Then
        fs.DeleteFile TempPath + FilePrefix + "*.*" 'Make sure we don't keep old files
    End If
    RegPath = "Software\IguanaTex"
    Dim UseUTF8 As Boolean
    UseUTF8 = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "UseUTF8", True)
    
    If UseUTF8 = False Then
        Set f = fs.CreateTextFile(TempPath + FilePrefix + ".tex", True)
        f.Write TextBox1.Text
        f.Close
    Else
        Dim BinaryStream As Object
        Set BinaryStream = CreateObject("ADODB.stream")
        BinaryStream.Type = 1
        BinaryStream.Open
        Dim adodbStream  As Object
        Set adodbStream = CreateObject("ADODB.Stream")
        With adodbStream
            .Type = 2 'Stream type
            .Charset = "utf-8"
            .Open
            .WriteText TextBox1.Text
            '.SaveToFile TempPath & FilePrefix & ".tex", 2 'Save binary data To disk; problem: this includes a BOM
            ' Workaround to avoid BOM in file:
            .Position = 3 'skip BOM
            .CopyTo BinaryStream
            .Flush
            .Close
        End With
        BinaryStream.SaveToFile TempPath & FilePrefix & ".tex", 2 'Save binary data To disk
        BinaryStream.Flush
        BinaryStream.Close
    End If
    Set fs = Nothing
End Sub

Sub ButtonRun_Click()
    Dim TempPath As String
    'TempPath = GetTempPath()
    If Right(TextBoxTempFolder.Text, 1) <> "\" Then
        TextBoxTempFolder.Text = TextBoxTempFolder.Text & "\"
    End If
    TempPath = TextBoxTempFolder.Text
    
    If Left(TempPath, 1) = "." Then
        Dim sPath As String
        sPath = ActivePresentation.path
        If Len(sPath) > 0 Then
            If Right(sPath, 1) <> "\" Then
                sPath = sPath & "\"
            End If
            TempPath = sPath & TempPath
        Else
            MsgBox "You need to have saved your presentation once to use a relative path."
            Exit Sub
        End If
    End If
    
    Dim FilePrefix As String
    FilePrefix = GetFilePrefix()
    
    Dim debugMode As Boolean
    debugMode = checkboxDebug.Value
    
    ' Read settings
    RegPath = "Software\IguanaTex"
    LATEXENGINEID = ComboBoxLaTexEngine.ListIndex
    tex2pdf_command = LaTexEngineList(LATEXENGINEID)
    Dim TeXExePath As String
    TeXExePath = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "TeXExePath", "")
    TeXExeExt = ""
    If TeXExePath <> "" Then TeXExeExt = ".exe"
    Dim UsePDF As Boolean
    UsePDF = UsePDFList(LATEXENGINEID)
    
    Dim UseEMF As Boolean
    BitmapVector = ComboBoxBitmapVector.ListIndex
    If BitmapVector = 0 Then
        UseEMF = False
    Else
        UseEMF = True
    End If
    'UseEMF = CheckBoxEMF.Value
    Dim OutputType As String
    
    Dim TimeOutTimeString As String
    Dim TimeOutTime As Long
    TimeOutTimeString = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "TimeOutTime", "20") ' Wait 20 seconds for the processes to complete
    TimeOutTime = val(TimeOutTimeString) * 1000
    
    Dim OutputDpiString As String
    OutputDpiString = TextBoxLocalDPI.Text
    Dim OutputDpi As Long
    OutputDpi = val(OutputDpiString)
    
    ' Read current dpi in: this will be used when rescaling
    dpi = 96 'lDotsPerInch ' I'm not convinced that this is the right thing to do, so for now I stop trying to take dpi into account
    default_screen_dpi = 96
    Dim VectorScalingX As Single, VectorScalingY As Single, BitmapScalingX As Single, BitmapScalingY As Single
    VectorScalingX = dpi / default_screen_dpi * val(GetRegistryValue(HKEY_CURRENT_USER, RegPath, "VectorScalingX", "1"))
    VectorScalingY = dpi / default_screen_dpi * val(GetRegistryValue(HKEY_CURRENT_USER, RegPath, "VectorScalingY", "1"))
    BitmapScalingX = val(GetRegistryValue(HKEY_CURRENT_USER, RegPath, "BitmapScalingX", "1"))
    BitmapScalingY = val(GetRegistryValue(HKEY_CURRENT_USER, RegPath, "BitmapScalingY", "1"))
        
    ' Test if path writable
    If Not IsPathWritable(TempPath) Then
        MsgBox "The temporary folder " & TempPath & " appears not to be writable."
        Exit Sub
    End If
    
    
    ' Write latex to a temp file
    Call WriteLaTeX2File(TempPath, FilePrefix)
    
    
    ' Run latex
    Const ForReading = 1, ForWriting = 2, ForAppending = 3
    Set fs = CreateObject("Scripting.FileSystemObject")
    Dim LogFile As Object
    FrameProcess.Visible = True
    
    If UseEMF = True Then ' Use TeX2img to generate an EMF file
        tex2img_command = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "TeX2img Command", "%USERPROFILE%\Downloads\TeX2img\TeX2imgc.exe")
        LabelProcess.Caption = "LaTeX to EMF..."
        FrameProcess.Repaint
        RetVal& = Execute("""" & tex2img_command & """ --latex " + tex2pdf_command + " --preview- """ + FilePrefix + ".tex"" """ + FilePrefix + ".emf""", TempPath, debugMode, TimeOutTime)
        If (RetVal& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".emf")) Then
            ' Error in Latex code
            ' Read log file and show it to the user
            If fs.FileExists(TempPath & FilePrefix & ".log") Then
                Set LogFile = fs.OpenTextFile(TempPath + FilePrefix + ".log", ForReading)
                LogFileViewer.TextBox1.Text = LogFile.ReadAll
                LogFile.Close
                LogFileViewer.TextBox1.ScrollBars = fmScrollBarsBoth
                LogFileViewer.Show 1
            Else
                MsgBox "TeX2img did not return in " & TimeOutTimeString & " seconds and may have hung." _
                & vbNewLine & "You should have run TeX2img once outside IguanaTex to make sure its path are set correctly." _
                & vbNewLine & "Please make sure your code compiles outside IguanaTex."
            End If
            FrameProcess.Visible = False
            Exit Sub
        End If
        FinalFilename = FilePrefix & ".emf"
        OutputType = "EMF"
    Else
        If UsePDF = True Then ' pdf to png route
            gs_command = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "GS Command", "C:\Program Files (x86)\gs\gs9.15\bin\gswin32c.exe")
            IMconv = GetRegistryValue(HKEY_CURRENT_USER, RegPath, "IMconv", "C:\Program Files\ImageMagick\convert.exe")
            
            If tex2pdf_command = "platex" Then
                OutputExt = ".dvi"
                LabelProcess.Caption = "LaTeX to DVI..."
            Else
                OutputExt = ".pdf"
                LabelProcess.Caption = "LaTeX to PDF..."
            End If
            FrameProcess.Repaint
            
            RetVal& = Execute("""" & TeXExePath & tex2pdf_command & TeXExeExt & """ -shell-escape -interaction=batchmode """ + FilePrefix + ".tex""", TempPath, debugMode, TimeOutTime)
            
            If (RetVal& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & OutputExt)) Then
                ' Error in Latex code
                ' Read log file and show it to the user
                If fs.FileExists(TempPath & FilePrefix & ".log") Then
                    Set LogFile = fs.OpenTextFile(TempPath + FilePrefix + ".log", ForReading)
                    LogFileViewer.TextBox1.Text = LogFile.ReadAll
                    LogFile.Close
                    LogFileViewer.TextBox1.ScrollBars = fmScrollBarsBoth
                    LogFileViewer.Show 1
                Else
                    MsgBox tex2pdf_command & " did not return in " & TimeOutTimeString & " seconds and may have hung." _
                    & vbNewLine & "Please make sure your code compiles outside IguanaTex." _
                    & vbNewLine & "You may also try generating in Debug mode, as it will let you know if any font/package is missing"
                End If
                FrameProcess.Visible = False
                Exit Sub
            End If
            
            If tex2pdf_command = "platex" Then
                LabelProcess.Caption = "DVI to PDF..."
                FrameProcess.Repaint
                ' platex actually outputs a DVI file, which we need to convert to PDF (we could go the EPS route, but this blends easier with IguanaTex's existing code)
                RetValConv& = Execute("""" & TeXExePath & "dvipdfmx" & TeXExeExt & """ -o """ + FilePrefix + ".pdf"" """ & FilePrefix & ".dvi""", TempPath, debugMode, TimeOutTime)
                If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".pdf")) Then
                    ' Error in DVI to PDF conversion
                    MsgBox "Error while using dvipdm to convert from DVI to PDF."
                    FrameProcess.Visible = False
                    Exit Sub
                End If
            End If
            
            LabelProcess.Caption = "PDF to PNG..."
            FrameProcess.Repaint
            ' Output Bounding Box to file and read back in the appropriate information
            RetValConv& = Execute("cmd /C """ & gs_command & """ -q -dBATCH -dNOPAUSE -sDEVICE=bbox " & FilePrefix & ".pdf 2> " & FilePrefix & ".bbx", TempPath, debugMode, TimeOutTime)
            If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".bbx")) Then
                ' Error in bounding box computation
                MsgBox "Error while using Ghostscript to compute the bounding box. Is your path correct?"
                FrameProcess.Visible = False
                Exit Sub
            End If
            Dim BBString As String
            BBString = BoundingBoxString(TempPath + FilePrefix + ".bbx")
            
            ' Convert PDF to PNG
            If checkboxTransp.Value = True Then
                PdfPngDevice = "-sDEVICE=pngalpha"
            Else
                PdfPngDevice = "-sDEVICE=png16m"
            End If
            RetValConv& = Execute("""" & gs_command & """ -q -dBATCH -dNOPAUSE " & PdfPngDevice & " -r" & OutputDpiString & " -sOutputFile=""" & FilePrefix & "_tmp.png""" & BBString & " -f """ & TempPath & FilePrefix & ".pdf""", TempPath, debugMode, TimeOutTime)
            If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & "_tmp.png")) Then
                ' Error in PDF to PNG conversion
                MsgBox "Error while using Ghostscript to convert from PDF to PNG. Is your path correct?"
                FrameProcess.Visible = False
                Exit Sub
            End If
            ' Unfortunately, the resulting file has a metadata DPI of OutputDpi (=1200), not the default screen one (usually 96),
            ' so there is a discrepancy with the dvipng output, which is always 96 (independent of the screen, actually).
            ' The only workaround I have found so far is to use Imagemagick's convert to change the DPI (but not the pixel size!)
            ' Execute """" & IMconv & """ -units PixelsPerInch """ & FilePrefix & "_tmp.png"" -density " & CStr(dpi) & " """ & FilePrefix & ".png""", TempPath, debugMode
            RetValConv& = Execute("""" & IMconv & """ -units PixelsPerInch """ & FilePrefix & "_tmp.png"" -density " & CStr(default_screen_dpi) & " """ & FilePrefix & ".png""", TempPath, debugMode, TimeOutTime)
            If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".png")) Then
                ' Error in PDF to PNG conversion
                MsgBox "Error while using ImageMagick to change the PNG DPI. Is your path correct?" _
                & vbNewLine & "The full path is needed to avoid conflict with Windows's built-in convert.exe."
                FrameProcess.Visible = False
                Exit Sub
            End If
            
            ' 'I considered using ImageMagick's convert, but it's extremely slow, and uses ghostscript in the backend anyway
            'PdfPngSwitches = "-density 1200 -trim -transparent white -antialias +repage"
            'Execute IMconv & " " & PdfPngSwitches & " """ & FilePrefix & ".pdf"" """ & FilePrefix & ".png""", TempPath, debugMode
            
        Else
        ' dvi to png route
            LabelProcess.Caption = "LaTeX to DVI..."
            FrameProcess.Repaint
            RetVal& = Execute("""" & TeXExePath & "pdflatex" & TeXExeExt & """ -shell-escape -output-format dvi -interaction=batchmode """ + FilePrefix + ".tex""", TempPath, debugMode, TimeOutTime)
            If (RetVal& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".dvi")) Then
                ' Error in Latex code
                ' Read log file and show it to the user
                If fs.FileExists(TempPath & FilePrefix & ".log") Then
                    Set LogFile = fs.OpenTextFile(TempPath + FilePrefix + ".log", ForReading)
                    LogFileViewer.TextBox1.Text = LogFile.ReadAll
                    LogFile.Close
                    LogFileViewer.TextBox1.ScrollBars = fmScrollBarsBoth
                    LogFileViewer.Show 1
                Else
                    MsgBox "latex did not return in " & TimeOutTimeString & " seconds and may have hung." _
                    & vbNewLine & "Please make sure your code compiles outside IguanaTex." _
                    & vbNewLine & "You may also try generating in Debug mode, as it will let you know if any font/package is missing."
                End If
                FrameProcess.Visible = False
                Exit Sub
            End If
            LabelProcess.Caption = "DVI to PNG..."
            FrameProcess.Repaint
            DviPngSwitches = "-q -D " & OutputDpiString & " -T tight"  ' monitor is 96 dpi or higher; we use OutputDpi (=1200 by default) dpi to get a crisper display, and rescale later on for new displays to match the point size
            If checkboxTransp.Value = True Then
                DviPngSwitches = DviPngSwitches & " -bg Transparent"
            End If
            ' If the user created a .png by using the standalone class with convert, we use that, else we use dvipng
            If Not fs.FileExists(TempPath & FilePrefix & ".png") Then
                RetValConv& = Execute("""" & TeXExePath & "dvipng" & TeXExeExt & """ " & DviPngSwitches & " -o """ & FilePrefix & ".png"" """ & FilePrefix & ".dvi""", TempPath, debugMode, TimeOutTime)
                If (RetValConv& <> 0 Or Not fs.FileExists(TempPath & FilePrefix & ".png")) Then
                    MsgBox "dvipng failed, or did not return in " & TimeOutTimeString & " seconds and may have hung." _
                    & vbNewLine & "You may want to try compiling using the PDF->PNG option." _
                    & vbNewLine & "You may also try generating in Debug mode, as it will let you know if any font is missing."
                    FrameProcess.Visible = False
                    Exit Sub
                End If
            End If
        End If
        OutputType = "PNG"
        FinalFilename = FilePrefix & ".png"
    End If
    ' Latex run successful.
    
    
    ' Now we prepare the insertion of the image
    LabelProcess.Caption = "Insert image..."
    FrameProcess.Repaint
    
    ' If we are in Edit mode, store parameters of old image
    Dim PosX As Single
    Dim PosY As Single
    Dim Sel As Selection
    Set Sel = Application.ActiveWindow.Selection
    Dim oldshape As Shape
    Dim oldshapeIsEMF As Boolean
    Dim s As Shape
    IsInGroup = False
    If ButtonRun.Caption = "ReGenerate" Then
        If Sel.ShapeRange.Type = msoGroup And Sel.HasChildShapeRange Then
            ' Old image is part of a group
            Set oldshape = Sel.ChildShapeRange(1)
            IsInGroup = True
            Dim arr() As Variant ' gather all shapes to be regrouped later on
            j = 0
            For Each s In Sel.ShapeRange.GroupItems
                If s.name <> oldshape.name Then
                    j = j + 1
                    ReDim Preserve arr(1 To j)
                    arr(j) = s.name
                End If
            Next
            
            ' Store the group's animation and Zorder info in a dummy object tmpGroup
            Dim oldGroup As Shape
            Set oldGroup = Sel.ShapeRange(1)
            Dim tmpGroup As Shape
            Set tmpGroup = ActiveWindow.Selection.SlideRange.Shapes.AddShape(msoShapeDiamond, 1, 1, 1, 1)
            MoveAnimation oldGroup, tmpGroup
            MatchZOrder oldGroup, tmpGroup
            
            ' Tag all elements in the group with their hierarchy level and their name or group name
            Dim MaxGroupLevel As Long
            MaxGroupLevel = TagGroupHierarchy(arr, oldshape.name)
            
        Else
            Set oldshape = Sel.ShapeRange(1)
        End If
        PosX = oldshape.Left
        PosY = oldshape.Top
        oldshapeIsEMF = False
        If oldshape.Tags.Item("BitmapVector") <> "" Then
            If oldshape.Tags.Item("BitmapVector") = 1 Then
                oldshapeIsEMF = True
            End If
        End If
    Else
        PosX = 200
        PosY = 200
    End If
            
    ' Get scaling factors
    Dim isTexpoint As Boolean
    Dim tScaleWidth As Single, tScaleHeight As Single
    MagicScalingFactorEMF = 1 ' 1 / 100 ' Magical scaling factor for EMF
    MagicScalingFactorPNG = default_screen_dpi / OutputDpi
    If UseEMF Then
        MagicScalingFactor = MagicScalingFactorEMF
    Else
        MagicScalingFactor = MagicScalingFactorPNG
    End If
    
    If ButtonRun.Caption <> "ReGenerate" Or CheckBoxReset.Value Then
        PointSize = val(textboxSize.Text)
        tScaleWidth = PointSize / 10 * MagicScalingFactor  ' 1/10 is for the default LaTeX point size (10 pt)
        tScaleHeight = tScaleWidth
    Else
        ' Handle the case of Texpoint displays
        isTexpoint = False
        Dim OldDpi As Long
        OldDpi = OutputDpi
        With oldshape.Tags
            If .Item("TEXPOINTSCALING") <> "" Then
                isTexpoint = True
                tScaleWidth = val(.Item("TEXPOINTSCALING")) * MagicScalingFactor
                tScaleHeight = tScaleWidth
            End If
            If .Item("OUTPUTDPI") <> "" Then
                OldDpi = val(.Item("OUTPUTDPI"))
            End If
        End With
        If Not isTexpoint Then ' modifying a normal display, either PNG or EMF
            HeightOld = oldshape.Height
            WidthOld = oldshape.Width
            tScaleHeight = 1
            tScaleWidth = 1
            If oldshapeIsEMF = False Then ' this deals with displays from very old versions of IguanaTex that lack proper size tags
                oldshape.ScaleHeight 1#, msoTrue
                oldshape.ScaleWidth 1#, msoTrue
                tScaleHeight = HeightOld / oldshape.Height * 960 / OutputDpi ' 0.8=960/1200 is there to preserve scaling of displays created with old versions of IguanaTex
                tScaleWidth = WidthOld / oldshape.Width * 960 / OutputDpi
            End If
            With oldshape.Tags
                If .Item("ORIGINALHEIGHT") <> "" Then
                    tmpHeight = val(.Item("ORIGINALHEIGHT"))
                    tScaleHeight = HeightOld / tmpHeight * OldDpi / OutputDpi
                End If
                If .Item("ORIGINALWIDTH") <> "" Then
                    tmpWidth = val(.Item("ORIGINALWIDTH"))
                    tScaleWidth = WidthOld / tmpWidth * OldDpi / OutputDpi
                End If
            End With
            If UseEMF = True And oldshapeIsEMF = False Then
                tScaleHeight = tScaleHeight * MagicScalingFactorEMF / MagicScalingFactorPNG
                tScaleWidth = tScaleWidth * MagicScalingFactorEMF / MagicScalingFactorPNG
            ElseIf UseEMF = False And oldshapeIsEMF = True Then
                tScaleHeight = tScaleHeight / MagicScalingFactorEMF * MagicScalingFactorPNG
                tScaleWidth = tScaleWidth / MagicScalingFactorEMF * MagicScalingFactorPNG
            End If
        End If
    End If
    
    
    ' Insert image and rescale it
    Dim newShape As Shape
    Set newShape = AddDisplayShape(TempPath + FinalFilename, PosX, PosY)
    
    If UseEMF Then
        ' Rescale the EMF picture before converting into PPT object
        Set newShape = ConvertEMF(newShape, VectorScalingX * tScaleWidth, VectorScalingY * tScaleHeight)
        ' Tag shape and its components with their "original" sizes,
        ' which we get by dividing their current height/width by the scaling factors applied above
        newShape.Tags.Add "ORIGINALHEIGHT", newShape.Height / tScaleHeight
        newShape.Tags.Add "ORIGINALWIDTH", newShape.Width / tScaleWidth
        If newShape.Type = msoGroup Then
            For Each s In newShape.GroupItems
                s.Tags.Add "ORIGINALHEIGHT", s.Height / tScaleHeight
                s.Tags.Add "ORIGINALWIDTH", s.Width / tScaleWidth
            Next
        End If

    Else
        ' Resize to the true size of the png file and adjust using the manual scaling factors set in Main Settings
        With newShape
            .ScaleHeight 1#, msoTrue
            .ScaleWidth 1#, msoTrue
            .LockAspectRatio = msoFalse
            .ScaleHeight BitmapScalingY, msoFalse
            .ScaleWidth BitmapScalingX, msoFalse
            .Tags.Add "OUTPUTDPI", OutputDpi ' Stores this display's resolution
            ' Add tags storing the original height and width, used next time to keep resizing ratio.
            .Tags.Add "ORIGINALHEIGHT", newShape.Height
            .Tags.Add "ORIGINALWIDTH", newShape.Width
            ' Apply scaling factors
            .ScaleHeight tScaleHeight, msoFalse
            .ScaleWidth tScaleWidth, msoFalse
            .LockAspectRatio = msoTrue
        End With
    End If
        
    If ButtonRun.Caption = "ReGenerate" Then ' We are editing+resetting size of an old display, we keep rotation
        newShape.Rotation = oldshape.Rotation
        If Not CheckBoxReset.Value Then
            newShape.LockAspectRatio = oldshape.LockAspectRatio ' Unlock aspect ratio if old display had it unlocked
        End If
    End If
    
    ' Add tags
    Call AddTagsToShape(newShape)
    If UseEMF = True And newShape.Type = msoGroup Then
        Set s = newShape.GroupItems(1)
        Call AddTagsToShape(s) 'only left most for now, to make things simple
        For Each s In newShape.GroupItems
        '    Call AddTagsToShape(s)
            s.Tags.Add "EMFchild", True
        Next
    End If
    
    ' Copy animation settings and formatting from old image, then delete it
    If ButtonRun.Caption = "ReGenerate" Then
        Dim TransferDesign As Boolean
        TransferDesign = True
        If UseEMF <> oldshapeIsEMF Or CheckBoxResetFormat.Value Then
            TransferDesign = False
        End If
        If IsInGroup Then
            ' Transfer format to new shape
            MatchZOrder oldshape, newShape
            If TransferDesign Then
                oldshape.PickUp
                newShape.Apply
            End If
            ' Handle the case of shape within EMF group.
            Dim DeleteLowestLayer As Boolean
            DeleteLowestLayer = False
            If oldshape.Tags.Item("EMFchild") <> "" Then
                DeleteLowestLayer = True
            End If
            oldshape.Delete
            
            Dim newGroup As Shape
            ' Get current slide, it will be used to group ranges
            Dim sld As Slide
            Dim SlideIndex As Long
            SlideIndex = ActiveWindow.View.Slide.SlideIndex
            Set sld = ActivePresentation.Slides(SlideIndex)

            ' Group all non-modified elements from old group, plus modified element
            j = j + 1
            ReDim Preserve arr(1 To j)
            arr(j) = newShape.name
            If DeleteLowestLayer Then
                Dim arr_remain() As Variant
                j_remain = 0
                For Each n In arr
                    Set s = ActiveWindow.Selection.SlideRange.Shapes(n)
                    ThisShapeLevel = 0
                    For i_tag = 1 To s.Tags.count
                        If (s.Tags.name(i_tag) = "LAYER") Then
                            ThisShapeLevel = val(s.Tags.Value(i_tag))
                        End If
                    Next
                    If ThisShapeLevel = 1 Then
                        s.Delete
                    Else
                        j_remain = j_remain + 1
                        ReDim Preserve arr_remain(1 To j_remain)
                        arr_remain(j_remain) = s.name
                    End If
                Next
                newShape.Tags.Add "LAYER", 2
                arr = arr_remain
            Else
                newShape.Tags.Add "LAYER", 1
            End If
            newShape.Tags.Add "SELECTIONNAME", newShape.name
            
            ' Hierarchically re-group elements
            For Level = 1 To MaxGroupLevel
                Dim CurrentLevelArr() As Variant
                j_current = 0
                For Each n In arr
                    ThisShapeLevel = 0
                    Dim ThisShapeSelectionName As String
                    ThisShapeSelectionName = ""
                    On Error Resume Next
                    With ActiveWindow.Selection.SlideRange.Shapes(n).Tags
                        For i_tag = 1 To .count
                            If (.name(i_tag) = "LAYER") Then
                                ThisShapeLevel = val(.Value(i_tag))
                            End If
                            If (.name(i_tag) = "SELECTIONNAME") Then
                                ThisShapeSelectionName = .Value(i_tag)
                            End If
                        Next
                    End With
                    
                    
                    If ThisShapeLevel = Level Then
                        If j_current > 0 Then
                            If Not IsInArray(CurrentLevelArr, ThisShapeSelectionName) Then
                                j_current = j_current + 1
                                ReDim Preserve CurrentLevelArr(1 To j_current)
                                CurrentLevelArr(j_current) = ThisShapeSelectionName
                            End If
                        Else
                            j_current = j_current + 1
                            ReDim Preserve CurrentLevelArr(1 To j_current)
                            CurrentLevelArr(j_current) = ThisShapeSelectionName
                        End If
                    End If
                Next
                
                If j_current > 1 Then
                    Set newGroup = sld.Shapes.Range(CurrentLevelArr).Group
                    j = j + 1
                    ReDim Preserve arr(1 To j)
                    arr(j) = newGroup.name
                    newGroup.Tags.Add "SELECTIONNAME", newGroup.name
                    newGroup.Tags.Add "LAYER", Level + 1
                End If
                
            Next
            
            ' Delete the tags to avoid conflict with future runs
            For Each n In arr
                On Error Resume Next
                    ActiveWindow.Selection.SlideRange.Shapes(n).Tags.Delete ("SELECTIONNAME")
                    ActiveWindow.Selection.SlideRange.Shapes(n).Tags.Delete ("LAYER")
            Next
            
            ' Use temporary group to retrieve the group's original animation and Zorder
            MoveAnimation tmpGroup, newGroup
            MatchZOrder tmpGroup, newGroup
            tmpGroup.Delete
        Else
            MoveAnimation oldshape, newShape
            MatchZOrder oldshape, newShape
            If TransferDesign Then
                If oldshapeIsEMF And oldshape.Type = msoGroup Then
                    
                    ' First transfer group format to temporary shape
                    ' (we use a duplicate of the old EMF shape)
                    Dim tmpGroupEMF As Shape
                    Set tmpGroupEMF = oldshape.Duplicate(1)
                    'TransferGroupFormat oldshape, tmpGroupEMF
                    
                    ' Transfer shape formatting
                    ' First need to delete all but one shape in the group to unlock the format pickup
                    Dim tmpshp As Shape
                    Set tmpshp = oldshape.GroupItems(1)
                    For j = oldshape.GroupItems.count To 2 Step -1
                        ' Delete backwards because Powerpoint renumbers
                        ' We could also always delete .GroupItems(2) ...
                        oldshape.GroupItems(j).Delete
                    Next
                    tmpshp.PickUp
                    
                    ' Transfer shape formatting to each shape within the group
                    For Each s In newShape.GroupItems
                        s.Apply
                    Next
                    tmpshp.Delete
                    
                    ' Now we can transfer the group formatting from the temporary shape
                    TransferGroupFormat tmpGroupEMF, newShape
                    tmpGroupEMF.Delete
                Else
                    oldshape.PickUp
                    newShape.Apply
                    oldshape.Delete
                End If
            Else
                oldshape.Delete
            End If
        End If
    End If
    
    
    ' Select the new shape
    newShape.Select
    
    
    ' Delete temp files if not in debug mode
    If debugMode = False Then fs.DeleteFile TempPath + FilePrefix + "*.*"
    
    
    
    FrameProcess.Visible = False
    Unload LatexForm
Exit Sub
   
End Sub

Private Sub AddTagsToShape(vSh As Shape)
    With vSh.Tags
        .Add "LATEXADDIN", TextBox1.Text
        .Add "IguanaTexSize", val(textboxSize.Text)
        .Add "IGUANATEXCURSOR", TextBox1.SelStart
        .Add "TRANSPARENCY", checkboxTransp.Value
        .Add "FILENAME", TextBoxFile.Text
        .Add "LATEXENGINEID", ComboBoxLaTexEngine.ListIndex
        .Add "TEMPFOLDER", TextBoxTempFolder.Text
        .Add "LATEXFORMHEIGHT", LatexForm.Height
        .Add "LATEXFORMWIDTH", LatexForm.Width
        .Add "LATEXFORMWRAP", TextBox1.WordWrap
        .Add "BitmapVector", ComboBoxBitmapVector.ListIndex
    End With
End Sub



Private Function ConvertEMF(inSh As Shape, ScalingX As Single, ScalingY As Single, _
                            Optional FileType As String = "emf", Optional ConvertLines As Boolean = True) As Shape
    With inSh
        .ScaleHeight 1#, msoTrue
        .ScaleWidth 1#, msoTrue
        .LockAspectRatio = msoFalse
        .ScaleHeight ScalingY, msoTrue
        .ScaleWidth ScalingX, msoTrue
        .LockAspectRatio = msoTrue
    End With
    
    Dim newShape As Shape
    ' Get current slide, it will be used to group ranges
    Dim sld As Slide
    Dim SlideIndex As Long
    SlideIndex = ActiveWindow.View.Slide.SlideIndex
    Set sld = ActivePresentation.Slides(SlideIndex)

    ' Convert EMF image to object
    Dim Shr As ShapeRange
    Set Shr = inSh.Ungroup
    If FileType = "emf" Then
        Set Shr = Shr.Ungroup
        ' Clean up
        Shr.Item(1).Delete
        Shr.Item(2).Delete
        If Shr(3).GroupItems.count > 2 Then
            Set newShape = Shr(3)
        Else ' only a single freeform, so not a group
            Set newShape = Shr(3).GroupItems(2)
        End If
        Shr(3).GroupItems(1).Delete
    ElseIf FileType = "eps" Then
        Shr.GroupItems(1).Delete
        Shr.GroupItems(1).Delete
        Set newShape = Shr.Ungroup.Group
    End If
    
    
    If newShape.Type = msoGroup Then
    
        Dim arr_group() As Variant
        arr_group = GetAllShapesInGroup(newShape)
        Call FullyUngroupShape(newShape)
        Set newShape = sld.Shapes.Range(arr_group).Group
        
        Dim emf_arr() As Variant ' gather all shapes to be regrouped later on
        j_emf = 0
        Dim delete_arr() As Variant ' gather all shapes to be deleted later on
        j_delete = 0
        Dim s As Shape
        For Each s In newShape.GroupItems
            j_emf = j_emf + 1
            ReDim Preserve emf_arr(1 To j_emf)
            If s.Type = msoLine Then
                If ConvertLines And (s.Height > 0 Or s.Width > 0) Then
                    emf_arr(j_emf) = LineToFreeform(s).name
                    j_delete = j_delete + 1
                    ReDim Preserve delete_arr(1 To j_delete)
                    delete_arr(j_delete) = s.name
                Else
                    emf_arr(j_emf) = s.name
                End If
            Else
                emf_arr(j_emf) = s.name
                If s.Fill.Visible = msoTrue Then
                s.Line.Visible = msoFalse
                Else
                s.Line.Visible = msoTrue
                
                End If
            End If
        Next
        newShape.Ungroup
        If j_delete > 0 Then
            sld.Shapes.Range(delete_arr).Delete
        End If
        Set newShape = sld.Shapes.Range(emf_arr).Group
    
    Else
        If newShape.Type = msoLine Then
            newShapeName = LineToFreeform(newShape).name
            newShape.Delete
            Set newShape = sld.Shapes(newShapeName)
        Else
            newShape.Line.Visible = msoFalse
        End If
    End If
    newShape.LockAspectRatio = msoTrue
    Set ConvertEMF = newShape
End Function

Private Sub FullyUngroupShape(newShape As Shape)
    Dim Shr As ShapeRange
    Dim s As Shape
    If newShape.Type = msoGroup Then
        Set Shr = newShape.Ungroup
        For i = 1 To Shr.count
            Set s = Shr.Item(i)
            If s.Type = msoGroup Then
                Call FullyUngroupShape(s)
            End If
        Next
    End If
End Sub

Private Function GetAllShapesInGroup(newShape As Shape) As Variant
    Dim arr() As Variant
    Dim j As Long
    Dim s As Shape
    For Each s In newShape.GroupItems
            j = j + 1
            ReDim Preserve arr(1 To j)
            arr(j) = s.name
    Next
    GetAllShapesInGroup = arr
End Function

Private Function LineToFreeform(s As Shape) As Shape
    t = s.Line.Weight
    Dim ApplyTransform As Boolean
    ApplyTransform = True
    
    Dim bHflip As Boolean
    Dim bVflip As Boolean
    Dim nBegin As Long
    Dim nEnd As Long
    Dim aC(1 To 4, 1 To 2) As Double
    
    With s
        aC(1, 1) = .Left:           aC(1, 2) = .Top
        aC(2, 1) = .Left + .Width:  aC(2, 2) = .Top
        aC(3, 1) = .Left:           aC(3, 2) = .Top + .Height
        aC(4, 1) = .Left + .Width:  aC(4, 2) = .Top + .Height
    
        bHflip = .HorizontalFlip
        bVflip = .VerticalFlip
    End With
    
    If bHflip = bVflip Then
        If bVflip = False Then
            ' down to right -- South-East
            nBegin = 1: nEnd = 4
        Else
            ' up to left -- North-West
            nBegin = 4: nEnd = 1
        End If
    ElseIf bHflip = False Then
        ' up to right -- North-East
        nBegin = 3: nEnd = 2
    Else
        ' down to left -- South-West
        nBegin = 2: nEnd = 3
    End If
    xs = aC(nBegin, 1)
    ys = aC(nBegin, 2)
    xe = aC(nEnd, 1)
    ye = aC(nEnd, 2)
    
    ' Get unit vector in orthogonal direction
    xd = xe - xs
    yd = ye - ys
    
    s_length = Sqr(xd * xd + yd * yd)
    If s_length > 0 Then
    n_x = -yd / s_length
    n_y = xd / s_length
    Else
    n_x = 0
    n_y = 0
    End If
    
    x1 = xs + n_x * t / 2
    y1 = ys + n_y * t / 2
    x2 = xe + n_x * t / 2
    y2 = ye + n_y * t / 2
    x3 = xe - n_x * t / 2
    y3 = ye - n_y * t / 2
    x4 = xs - n_x * t / 2
    y4 = ys - n_y * t / 2
        
    'End If
    
    
    If ApplyTransform Then
        Dim builder As FreeformBuilder
        Set builder = ActiveWindow.Selection.SlideRange(1).Shapes.BuildFreeform(msoEditingCorner, x1, y1)
        builder.AddNodes msoSegmentLine, msoEditingAuto, x2, y2
        builder.AddNodes msoSegmentLine, msoEditingAuto, x3, y3
        builder.AddNodes msoSegmentLine, msoEditingAuto, x4, y4
        builder.AddNodes msoSegmentLine, msoEditingAuto, x1, y1
        Dim oSh As Shape
        Set oSh = builder.ConvertToShape
        oSh.Fill.ForeColor = s.Line.ForeColor
        oSh.Fill.Visible = msoTrue
        oSh.Line.Visible = msoFalse
        oSh.Rotation = s.Rotation
        Set LineToFreeform = oSh
    Else
        Set LineToFreeform = s
    End If
End Function

Private Function IsInArray(arr As Variant, valueToCheck As String) As Boolean
    IsInArray = False
    For Each n In arr
        If n = valueToCheck Then
            IsInArray = True
            Exit For
        End If
    Next

End Function

Private Function TagGroupHierarchy(arr As Variant, TargetName As String) As Long
    ' Arr is the list of names of (leaf) elements in this group
    ' TargetName is the display which is being modified. We're going down the branch containing it.
    Dim Sel As Selection
    ActiveWindow.Selection.SlideRange.Shapes(TargetName).Select
    Set Sel = Application.ActiveWindow.Selection
    
…
vbaProject_00.bin vba-project OOXML VBA project: ppt/vbaProject.bin 472576 bytes
SHA-256: 4411e4180f5afd7b5f788804d90c5b41c3243909275cf35d67aa8ef4febcc424