title: Python Build Script for VBA Add-In file
body: <p>I have written a python script that will serve as a "build script" for a macro-enabled PowerPoint file that I am supporting.  </p>
  
  <p>The script creates a new, empty PowerPoint presentation, imports all of the VBA modules, saves the file and converts it to a ZIP archive in order to insert th e the RibbonUI configurations (<em>ribbon_xml.xml</em> file and the <em>mylogo.jpg</em> file). </p>
  
  <p>All of this is working more or less as expected -- until I try to use the output file (manually rename from .zip to .pptm and open it in PowerPoint).  </p>
  
  <p><strong>Error</strong> The code exits cleanly but the output archive (copy.zip) when converted to a PPTM file does not open cleanly.</p>
  
  <p>I receive the warning that something is wrong with the configuration and PowerPoint will attempt to repair the file. </p>
  
  <p>PowerPoint, true to its nature of course does not indicate what the problem is, only that it has found "unreadable content" and that such content has "been removed"...The only thing I can see after comparing some files which I created manually, is that the XML attribute for the CustomUI appears to use some sort of GUID as part of its id attribute</p>
  
  <p><strong>Current workaround:</strong> the function <em>build_ribbon</em> could be done manually using the CustomUI Editor tool and would take about 3 minutes to reliably produce the PPTM output.</p>
  
  <p>So this is not particularly a "Python" question, since it's a question about the implementation of the CustomUI XML / ribbon XML interface.</p>
  
  <p>Full code:</p>
  
  <pre><code>import win32com.client
  import os
  import zipfile
  import uuid
  
  #### PARAMETERS
  vba_source_control_path = r"C:\Repos\MyAddIn\VBA\ChartBuilder_PPT\Modules"
  output_path = r"C:\debug\output.pptm"
  ribbon_xml_path = r"C:\Repos\MyAddIn\Ribbon XML\ribbon_xml.xml"
  ribbon_logo_path = r"C:\Repos\MyAddIn\Ribbon XML\mylogo.jpg"
  
  def build_addin(pres, path):
      """
      This procedure does the following:
          1. adds all of the VBComponents to the working PPTM file
  
      The .PPTM file is used for local development &amp; debugging and
      is only usually packaged as a PPAM for Testing and Distribution
      """
  
      for fn in [fn for fn in os.listdir(path) if not(fn.endswith(".frx"))]:
          pres.VBProject.VBComponents.Import(path + "\\" + fn)
  
      # Clean up old files, if any
      if os.path.isfile(output_path):
          os.remove(output_path)
      if os.path.isfile(output_path.replace(".pptm", ".zip")):
          os.remove(output_path.replace(".pptm", ".zip"))
  
      # Save the new file with VBProject components
      pres.SaveAs(output_path)
  
      pres.Close()
  
  def build_ribbon_zip():
  
      """
          build_ribbon_zip handles manipulation of the .ZIP contents and places the
          necessary components within the PPTM ZIP archive structure
          2. converts the PPTM to a .ZIP
          3. Adds the CustomUI XML and logo.jpg to the .ZIP directory
          4. converts the .ZIP to a PPTM      
      """
  
      id = '&lt;Relationship Id='
      schema = 'http://schemas.openxmlformats.org/officeDocument/2006/'
      _path = output_path.replace(".pptm", ".zip")
      copy_path = r"C:\debug\copy.zip"
  
      # Convert to ZIP archive
      os.rename(output_path, _path)
      zip = zipfile.ZipFile(_path, 'a')
      copy = zipfile.ZipFile(copy_path, 'w')
  
      guid = str(uuid.uuid4()).replace('-', '')[:16]
  
      for itm in [itm for itm in zip.infolist() if itm.filename != r'_rels/.rels']:
          buffer = zip.read(itm.filename)
          copy.writestr(itm, buffer)
  
      # Append the Logo file to the .zip and create the archive
      copy.write(ribbon_logo_path, r'\CustomUI\images\jdplogo.jpg')
  
      # append the CustomUI xml part to the .zip and create the archive
      copy.write(ribbon_xml_path, r'\CustomUI\customUI14.xml')
  
      # append the .rels file to CustomUI\_rels
      rels_xml = r'&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;'
      rels_xml += r'&lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;'
      rels_xml += r'&lt;Relationship Id="jdplogo" Type="'+schema+'relationships/image" Target="images/jdplogo.jpg"/&gt;'
      rels_xml += r'&lt;/Relationships&gt;'
  
      copy.writestr(r'CustomUI\_rels\customUI14.xml.rels', rels_xml.encode('utf-8'))
  
      # get the existing _rels/.rels XML content and append the UI:
      rels_xml = zip.read(r'_rels/.rels').rstrip()[:-16]
      rels_xml += id + r'"R'+guid+'" Type="http://schemas.microsoft.com/office/2007/relationships/ui/extensibility"'
      rels_xml += r'Target="customUI/customUI14.xml"/&gt;&lt;/Relationships&gt;'
  
      rels_xml = rels_xml.replace(os.linesep, '')
      # this file-like object is read-only, and the writestr method will create another .rels file...
  
      copy.writestr(r'_rels/.rels', rels_xml.encode('utf-8'))
  
      zip.close()
      copy.close()
  
  if __name__ == "__main__":
      """
      Procedure to create a new PowerPoint Presentation and insert the Code Modules from source control
      """
      ppApp = win32com.client.Dispatch("PowerPoint.Application")
  
      pres = ppApp.Presentations.Add(False)
  
      pres.Slides.AddSlide(1, pres.SlideMaster.CustomLayouts(1))
  
      build_addin(pres, vba_source_control_path)
  
      ppApp.Quit()
  
      build_ribbon_zip()
  </code></pre>