From 686b719bff93413513b6c84e3be5881aef663c20 Mon Sep 17 00:00:00 2001 From: Viktor Andreev Date: Wed, 8 Oct 2025 20:12:59 +0600 Subject: [PATCH] add pivot cache records conversion --- .../Format/Logic/Biff_records/SxBool.cpp | 7 ++ .../Format/Logic/Biff_records/SxBool.h | 3 +- .../Format/Logic/Biff_records/SxErr.cpp | 5 ++ .../XlsFile/Format/Logic/Biff_records/SxErr.h | 3 +- .../XlsFile/Format/Logic/Biff_unions/DBB.cpp | 5 +- .../Format/Logic/Biff_unions/SXOPER.cpp | 27 +++++++- .../XlsFile/Format/Logic/Biff_unions/SXOPER.h | 1 + OOXML/XlsxFormat/Pivot/PivotCacheRecords.h | 1 + OOXML/XlsxFormat/Pivot/Pivots.cpp | 67 ++++++++++++++++++- OOXML/XlsxFormat/Xlsx.cpp | 28 +++++++- 10 files changed, 138 insertions(+), 9 deletions(-) diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.cpp index 0fbb2187cf..5bb78bcca7 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.cpp @@ -55,5 +55,12 @@ void SxBool::readFields(CFRecord& record) val = (flags != 0); } + +void SxBool::writeFields(CFRecord& record) +{ + unsigned short flags = val; + record << flags; +} + } // namespace XLS diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.h b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.h index 1e86d58f4c..fd3955c8e6 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.h +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxBool.h @@ -47,10 +47,11 @@ public: BaseObjectPtr clone(); void readFields(CFRecord& record); + void writeFields(CFRecord& record); static const ElementType type = typeSxBool; - bool val; + bool val = false; }; } // namespace XLS diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.cpp index 40d5aa8e98..53ca79e4fa 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.cpp @@ -54,5 +54,10 @@ void SxErr::readFields(CFRecord& record) record >> wbe; } +void SxErr::writeFields(CFRecord& record) +{ + record << wbe; +} + } // namespace XLS diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.h b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.h index e1b0a7dc3c..85f1c8c809 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.h +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxErr.h @@ -47,10 +47,11 @@ public: BaseObjectPtr clone(); void readFields(CFRecord& record); + void writeFields(CFRecord& record); static const ElementType type = typeSxErr; - unsigned short wbe; + unsigned short wbe = 0; }; } // namespace XLS diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/DBB.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/DBB.cpp index 9a8e979b08..0d72d06216 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/DBB.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/DBB.cpp @@ -85,9 +85,8 @@ const bool DBB::loadContent(BinProcessor& proc) const bool DBB::saveContent(BinProcessor& proc) { - if(m_SXDBB == nullptr) - return false; - proc.mandatory(*m_SXDBB); + if(m_SXDBB != nullptr) + proc.mandatory(*m_SXDBB); for(auto i : m_arSXOPER) if(i != nullptr) proc.mandatory(*i); diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.cpp index 0ee015e8de..e8d0a2c06b 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.cpp @@ -130,7 +130,6 @@ const bool SXOPER::loadContent(BinProcessor& proc) m_element = elements_.back(); elements_.pop_back(); - return true; } const bool SXOPER::saveContent(BinProcessor& proc) @@ -141,12 +140,38 @@ const bool SXOPER::saveContent(BinProcessor& proc) number.num.data.value = std::stod(value); proc.mandatory(number); } + else if (bBool) + { + SxBool boolVal; + boolVal.val = value == L"1"; + proc.mandatory(boolVal); + } else if(bString) { SXString string; string.segment = value; proc.mandatory(string); } + else if(bErr) + { + SxErr err; + if(value == L"NULL!") + err.wbe = 0; + else if(value == L"#DIV/0!") + err.wbe = 0x07; + else if(value == L"#VALUE!") + err.wbe = 0x0F; + else if(value == L"#REF!") + err.wbe = 0x17; + else if(value == L"#NAME?") + err.wbe = 0x1D; + else if(value == L"#NUM!") + err.wbe = 0x24; + else if(value == L"#N/A") + err.wbe = 0x2A; + proc.mandatory(err); + + } else if(bDate) { //todo date parsing diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.h b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.h index 06da17ca5a..293e64e0ed 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.h +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.h @@ -63,6 +63,7 @@ public: bool bEmpty = false; bool bInteger = false; bool bBool = false; + bool bErr = false; std::wstring value = L""; std::wstring node = L""; diff --git a/OOXML/XlsxFormat/Pivot/PivotCacheRecords.h b/OOXML/XlsxFormat/Pivot/PivotCacheRecords.h index 35295a4f97..a5c716e292 100644 --- a/OOXML/XlsxFormat/Pivot/PivotCacheRecords.h +++ b/OOXML/XlsxFormat/Pivot/PivotCacheRecords.h @@ -70,6 +70,7 @@ namespace OOX void fromBin(XLS::BaseObjectPtr& obj); void fromBin(XLS::StreamCacheReaderPtr& reader); XLS::BaseObjectPtr toBin(); + XLS::BaseObjectPtr toXLS(); void toBin(XLS::StreamCacheWriterPtr& writer); virtual EElementType getType () const { diff --git a/OOXML/XlsxFormat/Pivot/Pivots.cpp b/OOXML/XlsxFormat/Pivot/Pivots.cpp index f779242148..13cfd2d1c1 100644 --- a/OOXML/XlsxFormat/Pivot/Pivots.cpp +++ b/OOXML/XlsxFormat/Pivot/Pivots.cpp @@ -155,6 +155,8 @@ #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/PIVOTLI.h" #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/PIVOTCACHE.h" #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/FDB.h" +#include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/DBB.h" +#include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/SXOPER.h" #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxView.h" #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_records/Sxvd.h" #include "../../../MsBinaryFile/XlsFile/Format/Logic/Biff_records/SxIvd.h" @@ -7875,7 +7877,6 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" { auto ptrPCDIDT(new XLSB::PCDIDT); - if(i->getType() == et_x_PivotBooleanValue) { auto boolValue = static_cast(i); @@ -7934,6 +7935,70 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" } return objectPtr; } + XLS::BaseObjectPtr CPivotCacheRecord::toXLS() + { + auto ptr = new XLS::DBB; + for(auto i:m_arrItems) + { + auto operPtr = new XLS::SXOPER; + if(i->getType() == et_x_PivotBooleanValue) + { + auto boolValue = static_cast(i); + if(boolValue->m_oValue.IsInit()) + operPtr->value = std::to_wstring(boolValue->m_oValue.get()); + operPtr->bBool = true; + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + + else if(i->getType() == et_x_PivotErrorValue) + { + auto errorValue = static_cast(i); + if(errorValue->m_oValue.IsInit()) + operPtr->value = errorValue->m_oValue.get(); + operPtr->bErr = true; + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + + else if(i->getType() == et_x_PivotNoValue) + { + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + + else if(i->getType() == et_x_PivotNumericValue) + { + auto numValue = static_cast(i); + if(numValue->m_oValue.IsInit()) + operPtr->value = std::to_wstring(numValue->m_oValue.get()); + operPtr->bNumber = true; + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + + else if(i->getType() == et_x_PivotCharacterValue) + { + auto charValue = static_cast(i); + if(charValue->m_oValue.IsInit()) + operPtr->value = charValue->m_oValue.get(); + operPtr->bString = true; + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + + else if(i->getType() == et_x_PivotDateTimeValue) + { + auto dataValue = static_cast(i); + if(dataValue->m_oValue.IsInit()) + operPtr->value = dataValue->m_oValue->GetValue(); + operPtr->bDate = true; + ptr->m_arSXOPER.push_back(XLS::BaseObjectPtr(operPtr)); + continue; + } + } + return XLS::BaseObjectPtr(ptr); + } void CPivotCacheRecord::toBin(XLS::StreamCacheWriterPtr& writer) { { diff --git a/OOXML/XlsxFormat/Xlsx.cpp b/OOXML/XlsxFormat/Xlsx.cpp index 66ef354769..5b506d45a1 100644 --- a/OOXML/XlsxFormat/Xlsx.cpp +++ b/OOXML/XlsxFormat/Xlsx.cpp @@ -33,6 +33,7 @@ #include "../DocxFormat/App.h" #include "../DocxFormat/Core.h" +#include "../DocxFormat/FileTypes.h" #include "Workbook/Workbook.h" #include "SharedStrings/SharedStrings.h" @@ -57,10 +58,12 @@ #include "ComplexTypes_Spreadsheet.h" #include "../../DesktopEditor/common/SystemUtils.h" +#include "../Binary/XlsbFormat/FileTypes_SpreadsheetBin.h" #include "../../MsBinaryFile/XlsFile/Converter/xls_writer.h" #include "../../MsBinaryFile/XlsFile/Format/Logic/WorkbookStreamObject.h" #include "../../MsBinaryFile/XlsFile/Format/Logic/GlobalsSubstream.h" +#include "../../MsBinaryFile/XlsFile/Format/Logic/Biff_unions/PIVOTCACHE.h" OOX::Spreadsheet::CXlsx::CXlsx() : OOX::IFileContainer(dynamic_cast(this)) { @@ -203,7 +206,6 @@ bool OOX::Spreadsheet::CXlsx::WriteXLS(const CPath& oFilePath) workbookStream->m_GlobalsSubstream = m_pWorkbook->toXLS(); auto CastedGlobalsStram = static_cast(workbookStream->m_GlobalsSubstream.get()); CastedGlobalsStram->global_info_ = writer.globalInfoPtr; - //todo substreams conversion for(auto i : m_arWorksheets) workbookStream->m_arWorksheetSubstream.push_back(i->toXLS()); if(m_pSharedStrings != nullptr) @@ -222,8 +224,30 @@ bool OOX::Spreadsheet::CXlsx::WriteXLS(const CPath& oFilePath) continue; auto cacheFilePtr = m_pWorkbook->Find(cacheHeader->m_oRid->GetValue()); auto CachePtr = static_cast(cacheFilePtr.GetPointer()); + auto XLSBinCache = CachePtr->m_oPivotCashDefinition->toXLS(cacheHeader->m_oCacheId->GetValue()); + auto castedCache = static_cast(XLSBinCache.get()); + auto cacheRecordsPtr = CachePtr->Find(OOX::SpreadsheetBin::FileTypes::PivotCacheRecordsBin); + if(!(cacheRecordsPtr->type() == OOX::FileTypes::Unknown)) + { + auto castedRecords = static_cast(cacheRecordsPtr.GetPointer()); + if(!castedRecords->m_oPivotCacheRecords.IsInit() && castedRecords->m_pData!= nullptr) + { + castedRecords->m_oPivotCacheRecords.Init(); + XmlUtils::CXmlLiteReader reader; + reader.FromStringA((char*)castedRecords->m_pData, castedRecords->m_nDataLength); + reader.ReadNextNode(); + castedRecords->m_oPivotCacheRecords->fromXML(reader); + } + if(castedRecords->m_oPivotCacheRecords.IsInit()) + for(auto CacheRecord : castedRecords->m_oPivotCacheRecords->m_arrItems) + { + castedCache->m_arDBB.push_back(CacheRecord->toXLS()); + } + } + + if(CachePtr->m_oPivotCashDefinition.IsInit()) - writer.WritePivotCache(CachePtr->m_oPivotCashDefinition->toXLS(cacheHeader->m_oCacheId->GetValue()), cacheHeader->m_oCacheId->GetValue()); + writer.WritePivotCache(XLSBinCache, cacheHeader->m_oCacheId->GetValue()); } } return true;