I have written a python script that will serve as a "build script" for a macro-enabled PowerPoint file that I am supporting.
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 (ribbon_xml.xml file and the mylogo.jpg file).
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).
Error The code exits cleanly but the output archive (copy.zip) when converted to a PPTM file does not open cleanly.
I receive the warning that something is wrong with the configuration and PowerPoint will attempt to repair the file.
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
Current workaround: the function build_ribbon could be done manually using the CustomUI Editor tool and would take about 3 minutes to reliably produce the PPTM output.
So this is not particularly a "Python" question, since it's a question about the implementation of the CustomUI XML / ribbon XML interface.
Full 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 & 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 = '<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'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
rels_xml += r'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
rels_xml += r'<Relationship Id="jdplogo" Type="'+schema+'relationships/image" Target="images/jdplogo.jpg"/>'
rels_xml += r'</Relationships>'
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"/></Relationships>'
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()