4

サポートしているマクロを有効にした PowerPoint ファイルの "ビルド スクリプト" として機能する Python スクリプトを作成しました。

このスクリプトは、新しい空の PowerPoint プレゼンテーションを作成し、すべての VBA モジュールをインポートし、ファイルを保存して ZIP アーカイブに変換し、RibbonUI 構成 ( ribbon_xml.xmlファイルとmylogo.jpgファイル) を挿入します。

出力ファイルを使用しようとするまでは (.zip から .pptm に手動で名前を変更し、PowerPoint で開きます)。

エラーコードは正常に終了しますが、PPTM ファイルに変換されたときの出力アーカイブ (copy.zip) は正常に開きません。

構成に問題があるという警告が表示され、PowerPoint がファイルの修復を試みます。

もちろん、その性質上、問題が何であるかを示しているわけではありません。「読み取れないコンテンツ」が見つかり、そのようなコンテンツが「削除された」ということだけです...私が作成したいくつかのファイルを比較した後に確認できる唯一のこと手動で、CustomUI の XML 属性が id 属性の一部としてある種の GUID を使用しているように見えることです。

現在の回避策:関数build_ribbonは、CustomUI エディター ツールを使用して手動で行うことができ、PPTM 出力を確実に生成するには約 3 分かかります。

CustomUI XML / リボン XML インターフェイスの実装に関する質問であるため、これは特に「Python」に関する質問ではありません。

完全なコード:

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()
4

1 に答える 1

2

一部の参照が出力にないため、PowerPoint が異常になります。これを次のように解決しました:

def build_addin(pres, path):
    """
    This procedure does the following:
        1. adds all of the VBComponents to the working PPTM file
        2. adds required project references
    The .PPTM file is used for local development & debugging and
    is only usually packaged as a PPAM for Testing and Distribution
    """

    version = str(int(float(pres.Application.version)))

    # import the VB Components
    for fn in [fn for fn in os.listdir(path) if not(fn.endswith(".frx"))]:
        pres.VBProject.VBComponents.Import(path + "\\" + fn)

    # add the required project references
    pres.VBProject.References.AddFromFile(r'C:\Program Files (x86)\Microsoft Office\Office'+version+'\EXCEL.EXE')
    # MSForms TreeView Control
    pres.VBProject.References.AddFromFile(r'C:\Windows\SysWOW64\MSCOMCTL.OCX')
    # MSXML2
    pres.VBProject.References.AddFromFile(r'C:\Windows\System32\msxml6.dll')
    # ADODB
    pres.VBProject.References.AddFromFile(r'C:\Program Files (x86)\Common Files\System\ado\msado15.dll')
    # VBE Extensibility

また、不正な形式の可能性のある XML を見つけてbuild_ribbon修正しましたが、PowerPoint はファイルを最初に (1 回) 開いたときにファイルを「修復」する必要があるため、まだ 100% ではありませんが、その後は期待どおりに動作しているようです。

カスタム ロゴがリボンに表示されていないことに気付きました。「判読できないコンテンツ」は、リボン コントロールの 1 つに読み込まれている JPG 画像ファイルに関連している可能性があります。OpenXMLDeveloper のこのフォーラムから:

この種の問題は、次の領域のいずれかに問題がある場合に発生します。

  1. 関係 ID が部品と一致しません
  2. content_types.xml ファイルのエラー
  3. 一部のエラー (document.xml またはその他の部分)
  4. スライド マスター - スライド レイアウト/スライド レイアウト - スライド間のリンクが一致しません

[Content_Types].xml ファイルに .jpg ファイル拡張子の要素が含まれていないことを再確認します。

ElementTree のインポート ステートメントを追加します。

import xml.etree.ElementTree as ET

そして、次のように変更build_ribbon_zipします。

def build_ribbon_zip():

    """
        build_ribbon_zip handles manipulation of the .ZIP contents and places the
        necessary components within the PPTM ZIP archive structure
        3. converts the PPTM to a .ZIP
        4. Adds the CustomUI XML to the .ZIP directory
        5. converts the .ZIP to a PPTM

    """
    bom = u'\ufeff'
    _path=output_path.replace('.pptm', '.zip')
    copy_path=r'C:\debug\copy.zip'

    # Convert to ZIP archive
    os.rename(output_path, _path)
    z=zipfile.ZipFile(_path, 'a', zipfile.ZIP_DEFLATED)
    copy=zipfile.ZipFile(copy_path, 'w', zipfile.ZIP_DEFLATED)

    guid=str(uuid.uuid4()).replace('-', '')[:16]

    """
        the .rels files are written directly from XML string built in procedure
        the [Content_Types].xml file needs to include additional parameter for the 'jpg' extension
    """
    for itm in [itm for itm in z.infolist() if itm.filename != r'_rels/.rels']:
        buffer = z.read(itm.filename)
        if itm.filename == "[Content_Types].xml":
            # Modify the [Content_Types].xml file to include the jpg reference
            # <Default Extension="jpg" ContentType="image/.jpg" />
            # copy the XML from the original zip archive, this file has not been copied in the above loop
            root = ET.fromstring(buffer)

            ET.SubElement(root, '{http://schemas.openxmlformats.org/package/2006/content-types}Default', {'Extension': 'jpg', 'ContentType': 'image/.jpg'})

            copy.writestr(itm, ET.tostring(root).encode('utf-8'))

            # Append the Logo file to the .zip and create the archive
            copy.write(ribbon_logo_path, r'\customUI\images\jdplogo.jpg')

        else:
            copy.writestr(itm, buffer)

    # append the CustomUI xml part to the .zip and create the archive
    copy.write(ribbon_xml_path, r'\customUI\customUI14.xml')

    # create the string & append the .rels to CustomUI\_rels
    rels_xml = """<?xml version="1.0" encoding="utf-8"?>
        <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
        <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="images/jdplogo.jpg" Id="jdplogo" />
    </Relationships>"""

    copy.writestr(r'customUI\_rels\customUI14.xml.rels', rels_xml.encode('utf-8'))

    # get the existing _rels/.rels XML content and copy to the copied archiveI:

    rels_xml = r'<?xml version="1.0" encoding="utf-8" ?>'
    rels_xml += r'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
    rels_xml += r'<Relationship Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/'
    rels_xml += r'core-properties" '
    rels_xml += r'Target="docProps/core.xml" Id="rId3" />'
    rels_xml += r'<Relationship Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail" '
    rels_xml += r'Target="docProps/thumbnail.jpeg" Id="rId2" />'
    rels_xml += r'<Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" '
    rels_xml += r'Target="ppt/presentation.xml" Id="rId1" />'
    rels_xml += r'<Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" '
    rels_xml += r'Target="docProps/app.xml" Id="rId4" /><Relationship '
    rels_xml += r'Type="http://schemas.microsoft.com/office/2007/relationships/ui/extensibility" '
    rels_xml += r'Target="/customUI/customUI14.xml" Id="R'+guid+'" /></Relationships>'

    copy.writestr(r'_rels\.rels', rels_xml.encode('utf-8'))

    z.close()
    copy.close()

    os.remove(_path)
    os.rename(copy_path, output_path)
于 2015-07-28T04:25:08.593 に答える