Skip to content

Only first embedded Excel object is openable in generated Word document #621

@a77goddess

Description

@a77goddess

Describe the bug

When using docxtpl.DocxTemplate to generate a Word document containing multiple embedded Excel files through sub-documents, only the first embedded Excel object can be opened normally.
All others appear visually correct but cannot be opened (no response when double-clicked), and the context menu lacks the “Worksheet Object” option.

To Reproduce

import os
import openpyxl
from docxtpl import DocxTemplate


class ExcelOperator(object):
    def __init__(self, excel_file_path):
        self.excel_file_path = excel_file_path
        self.headers = ["Column1", "Column2", "Column3", "Column4", "Column5"]

    def _create_excel(self):
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.append(self.headers)
        return wb, ws

    def write_excel(self, data, sheet_name="DataSheet"):
        work_book, work_sheet = self._create_excel()
        work_sheet.title = sheet_name
        for item in data:
            work_sheet.append(item)
        work_book.save(self.excel_file_path)


class WordOperator(object):
    def __init__(self, main_tpl_file_path, sub_tpl_file_path, output_file_path):
        self.current_path = os.path.dirname(os.path.abspath(__file__))
        self.main_tpl_file_path = main_tpl_file_path
        self.sub_tpl_file_path = sub_tpl_file_path
        self.output_file_path = output_file_path

    def _build_sub_excel_docx(self, index, item_data):
        # Generate Excel data file
        excel_file_path = os.path.join(self.current_path, f"output/0.tmp_excel_{index}.xlsx")
        excel_operator = ExcelOperator(excel_file_path)
        excel_operator.write_excel(item_data["data"], item_data["name"])
        sub_docx_file_path = os.path.join(self.current_path, f"output/0.tmp_sub_doc_{index}.docx")
        # Load sub template file and replace the embedded Excel file with the newly generated Excel data file
        sub_docx = DocxTemplate(self.sub_tpl_file_path)
        sub_docx.replace_zipname('word/embeddings/Microsoft_Excel_Worksheet.xlsx', excel_file_path)
        sub_docx.save(sub_docx_file_path)
        return sub_docx_file_path

    def write_word(self, data_list):
        # Create main template file
        main_ctx = {
            "sub_docs": []
        }
        main_docx = DocxTemplate(self.main_tpl_file_path)
        # Cycle through data list to create sub documents
        for index, item in enumerate(data_list):
            # build sub document with embedded Excel
            sub_docx_file_path = self._build_sub_excel_docx(index, item)
            # load sub document into main document context
            sub_doc_tpl = main_docx.new_subdoc(sub_docx_file_path)
            main_ctx["sub_docs"].append(sub_doc_tpl)
        # Render and save main document
        main_docx.render(main_ctx)
        main_docx.save(self.output_file_path)


if __name__ == "__main__":
    data_list = [
        {"name": "No.1", "data": [["1-1", "1-2", "1-3", "1-4", "1-5"]]},
        {"name": "No.2", "data": [["2-1", "2-2", "2-3", "2-4", "2-5"]]},
        {"name": "No.3", "data": [["3-1", "3-2", "3-3", "3-4", "3-5"]]},
        {"name": "No.4", "data": [["4-1", "4-2", "4-3", "4-4", "4-5"]]},
        {"name": "No.5", "data": [["5-1", "5-2", "5-3", "5-4", "5-5"]]},
        {"name": "No.6", "data": [["6-1", "6-2", "6-3", "6-4", "6-5"]]},
        {"name": "No.7", "data": [["7-1", "7-2", "7-3", "7-4", "7-5"]]}
    ]
    current_path = os.path.dirname(os.path.abspath(__file__))
    # Generate Word document with embedded Excel files
    main_template_file_path = os.path.join(current_path, f"templates/tpl_demo_doc_main_v3.docx")
    sub_template_file_path = os.path.join(current_path, f"templates/tpl_demo_doc_sub_excel.docx")
    result_file_path = os.path.join(current_path, f"output/demo_doc_with_excel.docx")
    word_operator = WordOperator(main_template_file_path, sub_template_file_path, result_file_path)
    word_operator.write_word(data_list)

Expected behavior

All embedded Excel objects should behave like the first one — openable by double-click and recognized as valid “Worksheet Object” embeddings.

Screenshots

Image

Additional context

  • The generated main document contains 7 embedded Excel icons (verified inside the .docx archive at word/embeddings/):
    Microsoft_Excel_Worksheet1.xlsx
    Microsoft_Excel_Worksheet2.xlsx
    Microsoft_Excel_Worksheet3.xlsx
    Microsoft_Excel_Worksheet4.xlsx
    Microsoft_Excel_Worksheet5.xlsx
    Microsoft_Excel_Worksheet6.xlsx
    Microsoft_Excel_Worksheet7.xlsx
  • All 7 .xlsx files in the word/embeddings/ folder are valid and open normally if extracted.
  • However:
    • Only the first embedded Excel object inside the final .docx opens correctly when double-clicked.
    • The rest cannot be opened and show no context menu entry for “Worksheet Object”.
  • See the compressed package for code and running result files docx_demo_v2.tar.gz

docx_demo_v2.tar.gz

Environment

Python 3.11.3
docxtpl 0.20.1
python-docx 1.2.0
openpyxl 3.1.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions