troldal / OpenXLSX

A C++ library for reading, writing, creating and modifying Microsoft Excel® (.xlsx) files.
BSD 3-Clause "New" or "Revised" License
1.37k stars 333 forks source link

Can't be saved correctly xlsm-file #65

Closed nobdt1 closed 2 years ago

nobdt1 commented 3 years ago

set cell value and save, but it show error "removedPart /xl/vbaProject.bin。 (Visual Basic for Applications (VBA))" when open xlsm via office2019

sisco0 commented 3 years ago

Are you using only english characters, or characters like á,é?

nobdt1 commented 3 years ago

My .xlsm file has Chinese and English characters (.xlsm contains VBA code and forms, also contains Chinese and English characters) Opening .xlsm file in Excel 2019 after OpenXLSX has been modified and saved will prompt "There is something wrong with .xlsm. Do you try to recover?" If you select Yes, it will prompt "Deleted parts: part /xl/vbaproject.bin" Then I found that the VBA code and forms I had written in .xlsm were missing I tried the following

Note: The .xlsm file before modification, that is, the file edited through Excel 2019, is called the "source file". The files modified through OpenXLSX are called "modified files"

  1. Change the suffix of "source file" to ".zip" and unzip it to a directory
  2. Change the suffix of "modified Files" to ".zip" and unzip it to a directory
  3. Compare the two directories from steps 1 and 2 and find that all .xml files are different; The "source file" XML node has extra encoding="UTF-8" standalone="yes", so I made the following changes to the source code.
    
    library/external/pugixml/pugixml.cpp | 4 ++--
    1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/external/pugixml/pugixml.cpp b/library/external/pugixml/pugixml.cpp index e90e525..f049cd9 100644 --- a/library/external/pugixml/pugixml.cpp +++ b/library/external/pugixml/pugixml.cpp @@ -7116,9 +7116,9 @@ namespace pugi }

     if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) {
  1. Compare "source Files" and "modified Files" again and find the following two files are different

    \xl\vbaProject.bin
    \xl\worksheets\sheet6.xml
  2. I try to restore the \xl\vbaproject.bin file and repackage it into a ZIP with the suffix XLSM and open it with Excel 2019. It seems that everything is fine

To sum up, I suspect that the contents of \xl\vbaproject.bin were messed up when I saved the .xlsm file through OpenXLSX, but I can't find the relevant code.

sisco0 commented 3 years ago

For sure, chinese characters needed that encoding, which were not compatible with the current OpenXLSX implementation.

kinbei commented 3 years ago

I had the same problem, The error message "removedPart /xl/vbaproject.bin." because the '/xl/vbaproject.bin' is treated as the .xml file. You can solve it through the following patch.

 library/sources/XLDocument.cpp | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/library/sources/XLDocument.cpp b/library/sources/XLDocument.cpp
index 4c6cdf3..71edb46 100644
--- a/library/sources/XLDocument.cpp
+++ b/library/sources/XLDocument.cpp
@@ -493,17 +493,20 @@ void XLDocument::open(const std::string& fileName)

     // ===== Add remaining spreadsheet elements to the vector of XLXmlData objects.
     for (auto& item : m_contentTypes.getContentItems()) {
-        if (item.path().substr(0, 4) == "/xl/" && !(item.path() == "/xl/workbook.xml"))
-            m_data.emplace_back(/* parentDoc */ this,
-                                /* xmlPath   */ item.path().substr(1),
-                                /* xmlID     */ m_wbkRelationships.relationshipByTarget(item.path().substr(4)).id(),
-                                /* xmlType   */ item.type());
-
-        else
-            m_data.emplace_back(/* parentDoc */ this,
-                                /* xmlPath   */ item.path().substr(1),
-                                /* xmlID     */ m_docRelationships.relationshipByTarget(item.path().substr(1)).id(),
-                                /* xmlType   */ item.type());
+
+        if (item.path().substr(item.path().length() - 4, 4) == ".xml") {
+            if (item.path().substr(0, 4) == "/xl/" && !(item.path() == "/xl/workbook.xml"))
+                m_data.emplace_back(/* parentDoc */ this,
+                                    /* xmlPath   */ item.path().substr(1),
+                                    /* xmlID     */ m_wbkRelationships.relationshipByTarget(item.path().substr(4)).id(),
+                                    /* xmlType   */ item.type());
+
+            else
+                m_data.emplace_back(/* parentDoc */ this,
+                                    /* xmlPath   */ item.path().substr(1),
+                                    /* xmlID     */ m_docRelationships.relationshipByTarget(item.path().substr(1)).id(),
+                                    /* xmlType   */ item.type());
+        }
     }

     // ===== Open the workbook and document property items
nobdt1 commented 3 years ago

it's work. thanks