diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt index 5d63db0f..4b93a2b2 100644 --- a/Examples/CMakeLists.txt +++ b/Examples/CMakeLists.txt @@ -11,59 +11,76 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") add_subdirectory(external/nowide EXCLUDE_FROM_ALL) #======================================================================================================================= -# Define Demo1 target +# Define Demo01 target #======================================================================================================================= -add_executable(Demo1 Demo1.cpp) -target_link_libraries(Demo1 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo01 Demo01.cpp) +target_link_libraries(Demo01 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo1A target +# Define Demo01A target #======================================================================================================================= if(${OPENXLSX_ENABLE_LIBZIP}) find_package(LIBZIP MODULE REQUIRED) - add_executable(Demo1A Demo1A.cpp) - target_link_libraries(Demo1A PRIVATE OpenXLSX::OpenXLSX libzip::zip) + add_executable(Demo01A Demo01A.cpp) + target_link_libraries(Demo01A PRIVATE OpenXLSX::OpenXLSX libzip::zip) endif() #======================================================================================================================= -# Define Demo2 target +# Define Demo02 target #======================================================================================================================= -add_executable(Demo2 Demo2.cpp) -target_link_libraries(Demo2 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo02 Demo02.cpp) +target_link_libraries(Demo02 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo3 target +# Define Demo03 target #======================================================================================================================= -add_executable(Demo3 Demo3.cpp) -target_link_libraries(Demo3 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo03 Demo03.cpp) +target_link_libraries(Demo03 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo4 target +# Define Demo04 target #======================================================================================================================= -add_executable(Demo4 Demo4.cpp) -target_link_libraries(Demo4 PRIVATE OpenXLSX::OpenXLSX nowide::nowide) +add_executable(Demo04 Demo04.cpp) +target_link_libraries(Demo04 PRIVATE OpenXLSX::OpenXLSX nowide::nowide) #======================================================================================================================= -# Define Demo9 target +# Define Demo09 target #======================================================================================================================= -add_executable(Demo5 Demo5.cpp) -target_link_libraries(Demo5 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo05 Demo05.cpp) +target_link_libraries(Demo05 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo6 target +# Define Demo06 target #======================================================================================================================= -add_executable(Demo6 Demo6.cpp) -target_link_libraries(Demo6 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo06 Demo06.cpp) +target_link_libraries(Demo06 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo7 target +# Define Demo07 target #======================================================================================================================= -add_executable(Demo7 Demo7.cpp) -target_link_libraries(Demo7 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo07 Demo07.cpp) +target_link_libraries(Demo07 PRIVATE OpenXLSX::OpenXLSX) #======================================================================================================================= -# Define Demo8 target +# Define Demo08 target #======================================================================================================================= -add_executable(Demo8 Demo8.cpp) -target_link_libraries(Demo8 PRIVATE OpenXLSX::OpenXLSX) +add_executable(Demo08 Demo08.cpp) +target_link_libraries(Demo08 PRIVATE OpenXLSX::OpenXLSX) +#======================================================================================================================= +# Define Demo09 target +#======================================================================================================================= +add_executable(Demo09 Demo09.cpp) +target_link_libraries(Demo09 PRIVATE OpenXLSX::OpenXLSX) + +#======================================================================================================================= +# Define Demo10 target +#======================================================================================================================= +add_executable(Demo10 Demo10.cpp) +target_link_libraries(Demo10 PRIVATE OpenXLSX::OpenXLSX) + +#======================================================================================================================= +# Define Demo11 target +#======================================================================================================================= +add_executable(Demo11 Demo11.cpp) +target_link_libraries(Demo11 PRIVATE OpenXLSX::OpenXLSX) \ No newline at end of file diff --git a/Examples/Demo1.cpp b/Examples/Demo01.cpp similarity index 100% rename from Examples/Demo1.cpp rename to Examples/Demo01.cpp diff --git a/Examples/Demo1A.cpp b/Examples/Demo01A.cpp similarity index 100% rename from Examples/Demo1A.cpp rename to Examples/Demo01A.cpp diff --git a/Examples/Demo2.cpp b/Examples/Demo02.cpp similarity index 100% rename from Examples/Demo2.cpp rename to Examples/Demo02.cpp diff --git a/Examples/Demo3.cpp b/Examples/Demo03.cpp similarity index 94% rename from Examples/Demo3.cpp rename to Examples/Demo03.cpp index c3878a9a..f891ab24 100644 --- a/Examples/Demo3.cpp +++ b/Examples/Demo03.cpp @@ -78,10 +78,10 @@ int main() // The color of each sheet tab can be set using the 'setColor' method for a // sheet, and passing an XLColor object as an argument. - wbk.sheet("Sheet2").setColor(XLColor(0, 0, 0)); - wbk.sheet("Sheet3").setColor(XLColor(255, 0, 0)); - wbk.sheet("Sheet4").setColor(XLColor(0, 255, 0)); - wbk.sheet("Sheet5").setColor(XLColor(0, 0, 255)); + wbk.sheet("Sheet2").setTabColor(XLColor(0, 0, 0)); + wbk.sheet("Sheet3").setTabColor(XLColor(255, 0, 0)); + wbk.sheet("Sheet4").setTabColor(XLColor(0, 255, 0)); + wbk.sheet("Sheet5").setTabColor(XLColor(0, 0, 255)); doc.save(); diff --git a/Examples/Demo4.cpp b/Examples/Demo04.cpp similarity index 100% rename from Examples/Demo4.cpp rename to Examples/Demo04.cpp diff --git a/Examples/Demo5.cpp b/Examples/Demo05.cpp similarity index 100% rename from Examples/Demo5.cpp rename to Examples/Demo05.cpp diff --git a/Examples/Demo6.cpp b/Examples/Demo06.cpp similarity index 100% rename from Examples/Demo6.cpp rename to Examples/Demo06.cpp diff --git a/Examples/Demo7.cpp b/Examples/Demo07.cpp similarity index 100% rename from Examples/Demo7.cpp rename to Examples/Demo07.cpp diff --git a/Examples/Demo8.cpp b/Examples/Demo08.cpp similarity index 100% rename from Examples/Demo8.cpp rename to Examples/Demo08.cpp diff --git a/Examples/Demo09.cpp b/Examples/Demo09.cpp new file mode 100644 index 00000000..c0707670 --- /dev/null +++ b/Examples/Demo09.cpp @@ -0,0 +1,65 @@ +#include + +using namespace std; +using namespace OpenXLSX; + +int main() { + cout << "********************************************************************************\n"; + cout << "DEMO PROGRAM #09: Named Range\n"; + cout << "********************************************************************************\n"; + + // This example illustrate the use of the named range object. This could be particulary + // usefull when programming without GUI, to set up name in the Excel file, so that + // data could be safetly retrived and written by the code in dedicated location in the Excel file + // Unlike other implementation, named range is accessible within workbook object. Sheet limited + // maed range are as well accessed via the wrokbook. + + // First, create a new document and access the 1st sheet. + cout << "\nGenerating spreadsheet ..." << endl; + XLDocument doc; + doc.create("./Demo09.xlsx"); + auto wks = doc.workbook().sheet(1).get(); + string sheetName = wks.name(); + + // Define a namedrange "New name" + // At this stage the range shall be continuous + // a third argument could be provided the index of the spreasheet where + // the named range will be defined. Absolute marks ($) will be added. + string fullRef = sheetName + "!A5:E10"; + XLNamedRange myRange = doc.workbook().addNamedRange("MyNamedRange",fullRef /*, localSheetId*/); + + // Save the sheet... + cout << "Saving spreadsheet ..." << endl; + doc.save(); + doc.close(); + + // ...and reopen it (just to make sure that it is a valid .xlsx file) + cout << "Re-opening spreadsheet ..." << endl << endl; + doc.open("./Demo09.xlsx"); + wks = doc.workbook().worksheet(sheetName); + + // The NamedRange object is a range, it can be iterated + cout << "Filling the named range object ..." << endl; + XLNamedRange rng = doc.workbook().namedRange("MyNamedRange"); + int i = 0; + for (auto& cell : rng){ + cell.value() = i; + i++; + } + + // It can also be accessed via [] + cout << "Value of the 10th cell of the named range:" << endl; + cout << rng[10].value() << endl; + + // for convenience, in particular in case of mono cell named range, + // a fonction is provided: firstCell() + cout << "first Cell Value: " << rng.firstCell().value()<< endl; + + // Finally named range could be removed + //doc.workbook().deleteNamedRange("MyNamedRange"); + + doc.save(); + doc.close(); + + return 0; +} \ No newline at end of file diff --git a/Examples/Demo10.cpp b/Examples/Demo10.cpp new file mode 100644 index 00000000..4dbaca06 --- /dev/null +++ b/Examples/Demo10.cpp @@ -0,0 +1,159 @@ +#include + +#include + +using namespace std; +using namespace OpenXLSX; + +int main() { + cout << "********************************************************************************\n"; + cout << "DEMO PROGRAM #10: Tables first part\n"; + cout << "********************************************************************************\n"; + + // This example illustrate the use of the named range object. This could be particulary + // usefull when programming without GUI, to set up name in the Excel file, so that + // data could be safetly retrived and written by the code in dedicated location in the Excel file + // Unlike other implementation, named range is accessible within workbook object. Sheet limited + // maed range are as well accessed via the wrokbook. + + // First, create a new document and access the 1st sheet. + cout << "\nGenerating spreadsheet ..." << endl; + XLDocument doc; + doc.create("./Demo10.xlsx"); + auto wks = doc.workbook().sheet(1).get(); + string sheetName = wks.name(); + + // Fill the column header and create the table + // If the table header is not set, default name will be setup + // Also columns name shall be unique, a increment marks is added if required + cout << "Creating table ..." << endl; + wks.cell("B2").value() = "#"; + wks.cell("C2").value() = "One"; + wks.cell("D2").value() = "Col"; + wks.cell("E2").value() = "With"; + wks.cell("F2").value() = "Table"; + + XLTable myTable = doc.workbook().addTable(sheetName,"MyTable","B2:F18"); + //XLTable myTable = doc.workbook().addTable(sheetName,"MyTable","B2:F4"); + + // Save the sheet... + cout << "Saving spreadsheet ..." << endl; + doc.save(); + doc.close(); + + // ...and reopen it (just to make sure that it is a valid .xlsx file) + cout << "Re-opening spreadsheet ..." << endl << endl; + doc.open("./Demo10.xlsx"); + wks = doc.workbook().worksheet(sheetName); + + // We can retrieve the headers name: + XLTable tbl = doc.workbook().table("MyTable"); + vector headers = tbl.columnNames(); + + cout << "Table "<< tbl.name() << " header names:" << endl; + int i = 0; + for(const auto& h : headers){ + cout << i++ << " - " << h << endl; + } + + // We can also access different items + auto ncol = tbl.columnIndex("Col"); // Retrieve the colum index by the name + auto sht = tbl.getWorksheet(); // Retrieve and object worksheet + auto rang = tbl.tableRange(); // whole table range including total & headers + auto bodyRange = tbl.dataBodyRange(); // Only data body table range (excl. total & headers) + auto nRow = tbl.rowsCount(); // Only data body table range (excl. total & headers) + // And iterate throught the table rows + auto nCol = tbl.columnsCount(); // Only data body table range (excl. total & headers) + + int j = 0; + for(const auto& row : tbl.tableRows()){ + row[tbl.columnIndex("#")].value() = j; + row[1].value() = "Data" + to_string(j); + row[2].value() = "Col" + to_string(j); + row[3].value() = 4 * j; + row[tbl.columnIndex("Table")].value() = (float)j * 2.0f / 3.0f; + j++; + } + + // loop could also be done on columns + for(auto& col : tbl.columns()) + for (auto& cell : col.bodyRange()) + cout << cell.value() << " - "; + + cout << endl; + + // Also show the total with selected function + tbl.autofilter().hideArrows(); + //tbl.setHeaderVisible(false); + + // Total formulas + tbl.setTotalVisible(true); + //tbl.column("Table").setTotalsRowFunction(""); + //tbl.column("Table").setTotalsRowFunction("count"); + tbl.column("Table").totalsRowFormula() ="sum"; + string totaFormula = tbl.column("Table").totalsRowFormula(); + cout << "total Formula in the table column : " << totaFormula << endl; + + tbl.column("#").totalsRowLabel() ="Demo Total"; + string totaLabel = tbl.column("#").totalsRowLabel(); + cout << "total Label in the table column : " << totaLabel << endl; + + // To clear, a empty string could be sent, or the method + // clearTotalsRowFormula could be called + tbl.column("Table").totalsRowFormula() =""; + tbl.setTotalVisible(false); + + // Columns formulas could be setup, check and cleared, using either + // empty string or calling clearColumnFormula + tbl.column("With").columnFormula() = "MyTable[[#This Row],['#]]*2"; + string columFormula = tbl.column("With").columnFormula(); + cout << "Column Formula : " << columFormula << endl; + + + //Inserting columns + auto& newCol = tbl.insertColumn("newCol",2); + newCol.columnFormula() = "MyTable[[#This Row],['#]]+2"; + cout << "Inserted Column : " << newCol.name() << endl; + + auto& appendCol = tbl.appendColumn("newCol"); // test the auto increment + appendCol.columnFormula() = "MyTable[[#This Row],[One]]&\"akira\"&MyTable[[#This Row],[newCol]]"; + cout << "Append Column : " << newCol.name() << endl; + + // Deleting colum + tbl.deleteColumn("newCol"); + + //Inserting and deleting rows + auto newRow = tbl.insertRow(2); + tbl.deleteRow(3); + + tbl.setHeaderVisible(false); + tbl.setHeaderVisible(true); + + + +/* + // Table style basics + cout << "Table Style : " << tbl.tableStyle().style() << endl; + tbl.tableStyle().setStyle("TableStyleDark7"); + + cout << "Column stripes : " << tbl.tableStyle().columnStripes() << endl; + cout << "row stripes : " << tbl.tableStyle().rowStripes() << endl; + + tbl.tableStyle().showRowStripes(false); + tbl.tableStyle().showColumnStripes(true); + + cout << "1st Column highlighted : " << tbl.tableStyle().firstColumnHighlighted() << endl; + cout << "last Column highlighted : " << tbl.tableStyle().lastColumnHighlighted() << endl; + + tbl.tableStyle().showFirstColumnHighlighted(true); + tbl.tableStyle().showLastColumnHighlighted(true); +*/ + + auto tablelist = doc.workbook().tableNames(); + doc.workbook().deleteTable("MyTable"); + + doc.save(); + doc.close(); + + return 0; +} \ No newline at end of file diff --git a/Examples/Demo11.cpp b/Examples/Demo11.cpp new file mode 100644 index 00000000..d7ddcc07 --- /dev/null +++ b/Examples/Demo11.cpp @@ -0,0 +1,117 @@ +#include +#include +#include + +#ifdef WIN32 +#include +#include +#endif + +using namespace std; +using namespace OpenXLSX; + +void printType(XLNumberFormat fmt) +{ +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + switch (fmt.type()) { + case XLNumberFormat::XLNumberType::kCurrency: + cout << "\nkCurrency " + fmt.currencySymbol(); + break; + case XLNumberFormat::XLNumberType::kDate: + cout << "\nkDate "; + break; + case XLNumberFormat::XLNumberType::kPercent: + cout << "\nkPercent "; + break; + case XLNumberFormat::XLNumberType::kUnkown: + cout << "\nkUnkown "; + break; + } +} + + +void testNumbers(const XLWorksheet& wks) +{ + // check if the value is a number before using XLNumberFormat + + cout << "\n"; + auto doNumFmt = [&](const XLCell& cell) { + const auto& val = cell.value(); + if (XLValueType::Integer == val.type() || XLValueType::Float == val.type()) { + const auto fmt = cell.style().numberFormat(); + printType(fmt); + cout << " Format string = " << cell.style().formatString(); + } + else + cout << "\nNot a number"; + }; + + doNumFmt(wks.cell("A1")); + doNumFmt(wks.cell("A2")); + doNumFmt(wks.cell("A3")); + doNumFmt(wks.cell("A4")); + doNumFmt(wks.cell("A5")); + doNumFmt(wks.cell("A6")); + doNumFmt(wks.cell("A7")); + doNumFmt(wks.cell("A8")); + doNumFmt(wks.cell("A9")); + cout << "\n"; +} + +void testFonts(const XLWorksheet& wks) +{ + cout << "\n"; + auto doFonstTest = [&](const XLCell& cell) { + const auto& val = cell.value(); + const auto& style = cell.style(); + const auto& font = style.font(); + cout << "value= " << val.get() << ", name= " << font.name() << ", size= " << font.size() + << ", color= " << font.color().hex() << ", isBold " << font.bold() << ", isUnderline " << (font.underline() == 2) + << ", double underline " << (font.underline() == -4119) << ", isStrike " << font.strikethrough() << ", isItalic " + << font.italic() << '\n'; + }; + + doFonstTest(wks.cell("A1")); + doFonstTest(wks.cell("A2")); + doFonstTest(wks.cell("A3")); + doFonstTest(wks.cell("A4")); + doFonstTest(wks.cell("A5")); + doFonstTest(wks.cell("A6")); + cout << "\n"; +} + +int main() +{ + cout << "********************************************************************************\n"; + cout << "DEMO PROGRAM #11: Basic Usage\n"; + cout << "********************************************************************************\n"; +/* + auto& current_path = std::filesystem::current_path(); + auto& parent_path = current_path.parent_path(); + std::filesystem::path foundPath; + + while (parent_path.string().size() > 3) { + if (parent_path.filename().string().compare("OpenXLSX") == 0) foundPath = parent_path; + parent_path = parent_path.parent_path(); + } + foundPath += "\\Tests\\NumberType.xlsx"; + if (!std::filesystem::exists(foundPath)) { + cout << "File not found"; + return 0; + } + */ + std::string testFile = ".\\Tests\\NumberType.xlsx"; + + XLDocument doc; + doc.open(testFile); + + testNumbers(doc.workbook().worksheet("Sheet1")); + testFonts(doc.workbook().worksheet("Sheet2")); + + doc.close(); + + return 0; +} \ No newline at end of file diff --git a/OpenXLSX/CMakeLists.txt b/OpenXLSX/CMakeLists.txt index 6577cd7b..6d0a8530 100644 --- a/OpenXLSX/CMakeLists.txt +++ b/OpenXLSX/CMakeLists.txt @@ -82,6 +82,7 @@ endif () # List of project source files #======================================================================================================================= set(OPENXLSX_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/sources/XLAutofilter.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLCell.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLCellIterator.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLCellRange.cpp @@ -93,12 +94,18 @@ set(OPENXLSX_SOURCES ${CMAKE_CURRENT_LIST_DIR}/sources/XLDateTime.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLDocument.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLFormula.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLNamedRange.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLProperties.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLRelationships.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLRow.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLRowData.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLSharedStrings.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLSheet.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLStyles.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLTable.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLTableColumn.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLTableRows.cpp + ${CMAKE_CURRENT_LIST_DIR}/sources/XLTableStyle.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLWorkbook.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLXmlData.cpp ${CMAKE_CURRENT_LIST_DIR}/sources/XLXmlFile.cpp diff --git a/OpenXLSX/OpenXLSX.hpp b/OpenXLSX/OpenXLSX.hpp index 77cb1bf7..e46d8a01 100644 --- a/OpenXLSX/OpenXLSX.hpp +++ b/OpenXLSX/OpenXLSX.hpp @@ -55,8 +55,13 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include "headers/XLDocument.hpp" #include "headers/XLException.hpp" #include "headers/XLFormula.hpp" +#include "headers/XLNamedRange.hpp" #include "headers/XLRow.hpp" #include "headers/XLSheet.hpp" +#include "headers/XLStyles.hpp" +#include "headers/XLTable.hpp" +#include "headers/XLTableRows.hpp" +#include "headers/XLTableStyle.hpp" #include "headers/XLWorkbook.hpp" #include "headers/XLZipArchive.hpp" diff --git a/OpenXLSX/headers/XLAutofilter.hpp b/OpenXLSX/headers/XLAutofilter.hpp new file mode 100644 index 00000000..efbdfd06 --- /dev/null +++ b/OpenXLSX/headers/XLAutofilter.hpp @@ -0,0 +1,112 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLAUTOFILTER_HPP +#define OPENXLSX_XLAUTOFILTER_HPP + +#pragma warning(push) +//#pragma warning(disable : 4251) +//#pragma warning(disable : 4275) + +// ===== External Includes ===== // +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLXmlParser.hpp" + +namespace OpenXLSX +{ + // ========== Forward declaration ============ + class XLXmlData; + + + class OPENXLSX_EXPORT XLAutofilter + { + public: + /** + * @brief The constructor. + * @param dataNode XMLNode of the column + */ + XLAutofilter(const XMLNode& dataNode, XLXmlData* parent); + + //XLTableColumn(const XLTableColumn&) = delete; + //XLTableColumn& operator=(const XLTableColumn&) = delete; + ~XLAutofilter(); + + /** + * @brief + * @return the ref on which the filter apply + */ + std::string ref() const; + + /** + * @brief + * @param name set ref to be applied + */ + void setRef(const std::string& ref) const; + + /** + * @brief + * @param indexCol col index to be hidden (first index is 0) + * @param hide true by default + */ + void hideArrow(uint16_t indexCol, bool hide = true) const; + + /** + * @brief + * @param hide true by default + */ + void hideArrows(bool hide = true) const; + + private: + std::unique_ptr m_dataNode; + XLXmlData* m_parent; + }; +} // namespace OpenXLSX + +//#pragma warning(pop) +#endif // OPENXLSX_XLAUTOFILTER_HPP diff --git a/OpenXLSX/headers/XLCell.hpp b/OpenXLSX/headers/XLCell.hpp index 355e8fd4..ffc996a5 100644 --- a/OpenXLSX/headers/XLCell.hpp +++ b/OpenXLSX/headers/XLCell.hpp @@ -57,13 +57,16 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include "XLCellReference.hpp" #include "XLCellValue.hpp" #include "XLFormula.hpp" -#include "XLSharedStrings.hpp" + + // ========== CLASS AND ENUM TYPE DEFINITIONS ========== // namespace OpenXLSX { class XLCellRange; - class XLSharedStrings; + class XLWorksheet; + class XLStyles; + class XLStyle; /** * @brief An implementation class encapsulating the properties and behaviours of a spreadsheet cell. @@ -73,6 +76,7 @@ namespace OpenXLSX friend class XLCellIterator; friend class XLCellValueProxy; friend class XLRowDataIterator; + friend class XLStyles; friend bool operator==(const XLCell& lhs, const XLCell& rhs); friend bool operator!=(const XLCell& lhs, const XLCell& rhs); @@ -89,7 +93,7 @@ namespace OpenXLSX * @param cellNode * @param sharedStrings */ - XLCell(const XMLNode& cellNode, const XLSharedStrings& sharedStrings); + XLCell(const XMLNode& cellNode, const XLWorksheet* wks); /** * @brief Copy constructor @@ -181,6 +185,18 @@ namespace OpenXLSX * @param newFormula */ + /** + * @brief + * @param + */ + const XLStyles& styles() const; + + /** + * @brief + * @param + */ + const XLStyle style() const; + private: /** @@ -192,8 +208,8 @@ namespace OpenXLSX static bool isEqual(const XLCell& lhs, const XLCell& rhs); //---------- Private Member Variables ---------- // - std::unique_ptr m_cellNode; /**< A pointer to the root XMLNode for the cell. */ - XLSharedStrings m_sharedStrings; /**< */ + std::shared_ptr m_cellNode; /**< A pointer to the root XMLNode for the cell. */ + const XLWorksheet* m_worksheet; XLCellValueProxy m_valueProxy; /**< */ XLFormulaProxy m_formulaProxy; /**< */ }; diff --git a/OpenXLSX/headers/XLCellIterator.hpp b/OpenXLSX/headers/XLCellIterator.hpp index a5fe97c3..779a4393 100644 --- a/OpenXLSX/headers/XLCellIterator.hpp +++ b/OpenXLSX/headers/XLCellIterator.hpp @@ -60,6 +60,8 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. namespace OpenXLSX { + class XLWorksheet; + class OPENXLSX_EXPORT XLCellIterator { public: @@ -157,7 +159,7 @@ namespace OpenXLSX XLCellReference m_topLeft; /**< The cell reference of the first cell in the range */ XLCellReference m_bottomRight; /**< The cell reference of the last cell in the range */ XLCell m_currentCell; /**< */ - XLSharedStrings m_sharedStrings; /**< */ + const XLWorksheet* m_worksheet; /**< */ bool m_endReached { false }; /**< */ }; diff --git a/OpenXLSX/headers/XLCellRange.hpp b/OpenXLSX/headers/XLCellRange.hpp index ccfdb66a..d2eb27e0 100644 --- a/OpenXLSX/headers/XLCellRange.hpp +++ b/OpenXLSX/headers/XLCellRange.hpp @@ -15,33 +15,33 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. MM _MM_ - Copyright (c) 2018, Kenneth Troldal Balslev - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the author nor the - names of any contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ + Copyright (c) 2018, Kenneth Troldal Balslev + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ #ifndef OPENXLSX_XLCELLRANGE_HPP #define OPENXLSX_XLCELLRANGE_HPP @@ -52,6 +52,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== External Includes ===== // #include +#include // ===== OpenXLSX Includes ===== // #include "OpenXLSX-Exports.hpp" @@ -62,6 +63,8 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. namespace OpenXLSX { + class XLWorksheet; + /** * @brief This class encapsulates the concept of a cell range, i.e. a square area * (or subset) of cells in a spreadsheet. @@ -85,7 +88,7 @@ namespace OpenXLSX explicit XLCellRange(const XMLNode& dataNode, const XLCellReference& topLeft, const XLCellReference& bottomRight, - const XLSharedStrings& sharedStrings); + const XLWorksheet* wks); /** * @brief Copy constructor [default]. @@ -124,6 +127,27 @@ namespace OpenXLSX */ XLCellRange& operator=(XLCellRange&& other) noexcept; + /** + * @brief + * @param rhs + * @return + */ + bool operator==(const XLCellRange& rhs) const; + + /** + * @brief + * @param rhs + * @return + */ + bool operator!=(const XLCellRange& rhs) const; + + /** + * @brief + * @param index + * @return + */ + XLCell operator[](uint32_t index) const; + /** * @brief Get the number of rows in the range. * @return The number of rows. @@ -148,11 +172,60 @@ namespace OpenXLSX */ XLCellIterator end() const; + /** + * @brief offset the whole range if possible + * @param topLeft XLCoordinates of topleft corner + * @param bottomRight XLCoordinates of bottom right corner + */ + void setRangeCoordinates(const XLCellReference& topLeft, + const XLCellReference& bottomRight); + + /** + * @brief offset the whole range if possible + * @param row integer could be negative + * @param col integer could be negative, default 0 + * @note this method does not move any data on the worksheet, + * It will only affect the range considered by the XLCellRange object + */ + void offset(int row, int col = 0); + /** * @brief */ void clear(); + /** + * @brief get the coordinates of the top left and bottom right + * @return A pair of ref + */ + std::pair rangeCoordinates(); + /** + * @brief get the coordinates of the top left and bottom right + * @return A pair of ref + */ + const std::pair rangeCoordinates() const; + + /** + * @brief Static helper function to get top left and bottom right + * @param ref a ref i.e. A1:C20 + * @return A pair of ref + */ + static std::pair topLeftBottomRight(const std::string& ref); + + /** + * @brief Static helper function to get the num of row of a range + * @param ref a ref i.e. A1:C20 + * @return the num of columns + */ + static uint16_t columnsCount(const std::string& ref); + + /** + * @brief Static helper function to get the num of row of a range + * @param ref a ref i.e. A1:C20 + * @return the num of columns + */ + static uint32_t rowsCount(const std::string& ref); + //---------------------------------------------------------------------------------------------------------------------- // Private Member Variables //---------------------------------------------------------------------------------------------------------------------- @@ -161,7 +234,7 @@ namespace OpenXLSX std::unique_ptr m_dataNode; /**< */ XLCellReference m_topLeft; /**< The cell reference of the first cell in the range */ XLCellReference m_bottomRight; /**< The cell reference of the last cell in the range */ - XLSharedStrings m_sharedStrings; + const XLWorksheet* m_worksheet; }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLCellReference.hpp b/OpenXLSX/headers/XLCellReference.hpp index 98910f61..c7fa073c 100644 --- a/OpenXLSX/headers/XLCellReference.hpp +++ b/OpenXLSX/headers/XLCellReference.hpp @@ -51,6 +51,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #pragma warning(disable : 4275) // ===== External Includes ===== // +#include #include #include @@ -81,6 +82,7 @@ namespace OpenXLSX //---------------------------------------------------------------------------------------------------------------------- public: + /** * @brief Constructor taking a cell address as argument. * @param cellAddress The address of the cell, e.g. 'A1'. @@ -158,6 +160,14 @@ namespace OpenXLSX */ XLCellReference operator--(int); // NOLINT + /** + * @brief Offset for a given x, y. Stops at the limit of the worksheet. + * @param rows the relative number of row to offset + * @param columns the relative number of columns to offset default 0. + * @return return the offseted object + */ + XLCellReference& offset(int rows, int columns = 0); + /** * @brief Get the row number of the XLCellReference. * @return The row. @@ -189,11 +199,23 @@ namespace OpenXLSX */ void setRowAndColumn(uint32_t row, uint16_t column); + /** + * @brief Set the address of the XLCellReference + * @param coord coordinates of the cell to be set(std::pair(row, col). + */ + void setCoordinates(const XLCoordinates& coord); + + /** + * @brief . + * @return the coordinates of the cellref. + */ + XLCoordinates coordinates(); + /** * @brief Get the address of the XLCellReference * @return The address, e.g. 'A1' */ - std::string address() const; + std::string address(bool absolute = false) const; /** * @brief Set the address of the XLCellReference @@ -203,7 +225,7 @@ namespace OpenXLSX void setAddress(const std::string& address); //---------------------------------------------------------------------------------------------------------------------- - // Private Member Functions + // Static Helpers Functions //---------------------------------------------------------------------------------------------------------------------- // private: @@ -243,6 +265,8 @@ namespace OpenXLSX */ static XLCoordinates coordinatesFromAddress(const std::string& address); + static std::string adressFromCoordinates(uint32_t row, uint16_t column, bool absolute = false); + //---------------------------------------------------------------------------------------------------------------------- // Private Member Variables //---------------------------------------------------------------------------------------------------------------------- diff --git a/OpenXLSX/headers/XLCellValue.hpp b/OpenXLSX/headers/XLCellValue.hpp index ba57f5b5..8560d006 100644 --- a/OpenXLSX/headers/XLCellValue.hpp +++ b/OpenXLSX/headers/XLCellValue.hpp @@ -67,6 +67,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. namespace OpenXLSX { //---------- Forward Declarations ----------// + class XLSharedStrings; class XLCellValueProxy; class XLCell; @@ -275,6 +276,52 @@ namespace OpenXLSX return this->get(); } + std::string getAsString() const + { + struct AnyGet { + std::string operator()(bool value) { return value ? "1" : "0"; } + std::string operator()(int64_t value) { return std::to_string(value); } + std::string operator()(double value) { return std::to_string(value); } + std::string operator()(const std::string& value) { return value; } + }; + + return std::visit(AnyGet{}, m_value); + } + + double getAsDouble() const + { + struct AnyGet { + double operator()(bool value) { return value ? 1.0 : 0.0; } + double operator()(int64_t value) { return static_cast(value); } + double operator()(double value) { return value; } + double operator()(const std::string& value) { + double res = 0.0; + try{ res = std::stod(value); + }catch(...) { res = 0.0; } + return res; + } + }; + + return std::visit(AnyGet{}, m_value); + } + + int getAsInteger() const + { + struct AnyGet { + int operator()(bool value) { return value ? 1 : 0; } + int operator()(int64_t value) { return static_cast(value); } + int operator()(double value) { return static_cast(value); } + int operator()(const std::string& value) { + int res = 0; + try{ res = std::stoi(value); + }catch(...) { res = 0; } + return res; + } + }; + + return std::visit(AnyGet{}, m_value); + } + /** * @brief Clears the contents of the XLCellValue object. * @return Returns a reference to the current object. @@ -429,6 +476,21 @@ namespace OpenXLSX return getValue().get(); } + std::string getAsString() const + { + return getValue().getAsString(); + } + + double getAsDouble() const + { + return getValue().getAsDouble(); + } + + int getAsInteger() const + { + return getValue().getAsInteger(); + } + /** * @brief Clear the contents of the cell. * @return A reference to the current object. @@ -482,7 +544,7 @@ namespace OpenXLSX * @param cell Pointer to the parent XLCell object. * @param cellNode Pointer to the corresponding XMLNode object. */ - XLCellValueProxy(XLCell* cell, XMLNode* cellNode); + XLCellValueProxy(XLCell* cell, std::shared_ptr cellNode); /** * @brief Copy constructor @@ -533,10 +595,16 @@ namespace OpenXLSX */ XLCellValue getValue() const; + /** + * @brief Get a pointer to the XLSharedStrings* object of the document. + * @return An XLSharedStrings pointer. + */ + XLSharedStrings* getSharedString() const; + //---------- Private Member Variables ---------- // XLCell* m_cell; /**< Pointer to the owning XLCell object. */ - XMLNode* m_cellNode; /**< Pointer to corresponding XML cell node. */ + std::shared_ptr m_cellNode; /**< Pointer to corresponding XML cell node. */ }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLColor.hpp b/OpenXLSX/headers/XLColor.hpp index 3c584381..766e3639 100644 --- a/OpenXLSX/headers/XLColor.hpp +++ b/OpenXLSX/headers/XLColor.hpp @@ -51,6 +51,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #pragma warning(disable : 4275) // ===== External Includes ===== // +#include #include // ===== OpenXLSX Includes ===== // diff --git a/OpenXLSX/headers/XLCommandQuery.hpp b/OpenXLSX/headers/XLCommandQuery.hpp index 5d503aca..3b1727d7 100644 --- a/OpenXLSX/headers/XLCommandQuery.hpp +++ b/OpenXLSX/headers/XLCommandQuery.hpp @@ -75,7 +75,9 @@ namespace OpenXLSX AddSharedStrings, AddWorksheet, AddChartsheet, + AddTable, DeleteSheet, + DeleteTable, CloneSheet, }; @@ -142,6 +144,10 @@ namespace OpenXLSX QuerySheetRelsID, QuerySheetRelsTarget, QuerySharedStrings, + QuerySheetFromName, + QueryTableCount, + QueryTableFromIndex, + QueryTableFromName, QueryXmlData }; diff --git a/OpenXLSX/headers/XLConstants.hpp b/OpenXLSX/headers/XLConstants.hpp index ec314af3..81b52e22 100644 --- a/OpenXLSX/headers/XLConstants.hpp +++ b/OpenXLSX/headers/XLConstants.hpp @@ -5,10 +5,12 @@ #ifndef OPENXLSX_XLCONSTANTS_HPP #define OPENXLSX_XLCONSTANTS_HPP +#define INCREMENT_STRING "1" namespace OpenXLSX { inline const uint16_t MAX_COLS = 16'384; inline const uint32_t MAX_ROWS = 1'048'576; + } // namespace OpenXLSX #endif // OPENXLSX_XLCONSTANTS_HPP diff --git a/OpenXLSX/headers/XLContentTypes.hpp b/OpenXLSX/headers/XLContentTypes.hpp index f1be5e7c..619cbc8d 100644 --- a/OpenXLSX/headers/XLContentTypes.hpp +++ b/OpenXLSX/headers/XLContentTypes.hpp @@ -15,33 +15,33 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. MM _MM_ - Copyright (c) 2018, Kenneth Troldal Balslev - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the author nor the - names of any contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ + Copyright (c) 2018, Kenneth Troldal Balslev + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ #ifndef OPENXLSX_XLCONTENTTYPES_HPP #define OPENXLSX_XLCONTENTTYPES_HPP @@ -67,28 +67,29 @@ namespace OpenXLSX * @brief */ enum class XLContentType { - Workbook, - WorkbookMacroEnabled, - Worksheet, - Chartsheet, - ExternalLink, - Theme, - Styles, - SharedStrings, - Drawing, + CalculationChain, Chart, + Chartsheet, ChartStyle, ChartColorStyle, + Comments, ControlProperties, - CalculationChain, - VBAProject, CoreProperties, - ExtendedProperties, CustomProperties, - Comments, + Drawing, + ExtendedProperties, + ExternalLink, + SharedStrings, + Styles, Table, + Theme, + Unknown, + VBAProject, VMLDrawing, - Unknown + Workbook, + WorkbookMacroEnabled, + Worksheet, + WorksheetRelations }; /** diff --git a/OpenXLSX/headers/XLDocument.hpp b/OpenXLSX/headers/XLDocument.hpp index 85744381..c1a683ba 100644 --- a/OpenXLSX/headers/XLDocument.hpp +++ b/OpenXLSX/headers/XLDocument.hpp @@ -15,33 +15,33 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. MM _MM_ - Copyright (c) 2018, Kenneth Troldal Balslev - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the author nor the - names of any contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ + Copyright (c) 2018, Kenneth Troldal Balslev + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ #ifndef OPENXLSX_XLDOCUMENT_HPP #define OPENXLSX_XLDOCUMENT_HPP @@ -55,7 +55,6 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include #include #include -#include #include // ===== OpenXLSX Includes ===== // @@ -67,6 +66,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include "XLProperties.hpp" #include "XLRelationships.hpp" #include "XLSharedStrings.hpp" +#include "XLStyles.hpp" #include "XLWorkbook.hpp" #include "XLXmlData.hpp" #include "XLZipArchive.hpp" @@ -112,6 +112,7 @@ namespace OpenXLSX friend class XLXmlFile; friend class XLWorkbook; friend class XLSheet; + friend class XLStyles; friend class XLXmlData; //---------- Public Member Functions @@ -259,6 +260,12 @@ namespace OpenXLSX */ XLQuery execQuery(const XLQuery& query); + /** + * @brief + * @return + */ + const XLStyles& styles() const; + //---------------------------------------------------------------------------------------------------------------------- // Protected Member Functions @@ -277,14 +284,14 @@ namespace OpenXLSX * @param path * @return */ - XLXmlData* getXmlData(const std::string& path); + XLXmlData* getXmlDataByPath(const std::string& path); /** * @brief * @param path * @return */ - const XLXmlData* getXmlData(const std::string& path) const; + const XLXmlData* getXmlDataByPath(const std::string& path) const; /** * @brief @@ -292,7 +299,51 @@ namespace OpenXLSX * @return */ bool hasXmlData(const std::string& path) const; + + /** + * @brief Explore the tree to find the sheet element + * @param sheetName to be found + * @return a pointer to XLXmlData or nullptr if sheetName doesn not exist + */ + XLXmlData* getXmlDataByName(const std::string& name) const; + + /** + * @brief + * @param name + * @return the path to xl/worksheets/_rels/sheet{0}.xml.rels whether it exist or not. + * Return a empty string if sheetName does not exists + */ + std::string getSheetRelsPath(const std::string& sheetName) const; + /** + * @brief determine the available id disponible for filename + * @param type + * @return Return the available id. + */ + uint16_t availableFileID(XLContentType type); + + /** + * @brief determine the available id disponible for Id field + * @param type + * @return Return the available id. + * @note fill the gap if any + */ + uint16_t availableSheetID(); + + /** + * @brief create a new table in the doc + * @param sheetName + * @param tableName + * @param reference + */ + void createTable(const std::string& sheetName, const std::string& tableName, const std::string& reference); + + /** + * @brief delete the corresponding table + * @param tableName the table to be deleted + */ + void deleteTable(const std::string& tableName); + //---------------------------------------------------------------------------------------------------------------------- // Private Member Variables //---------------------------------------------------------------------------------------------------------------------- @@ -301,17 +352,18 @@ namespace OpenXLSX std::string m_filePath {}; /**< The path to the original file*/ std::string m_realPath {}; /**< */ - mutable std::list m_data {}; /**< */ - mutable std::deque m_sharedStringCache {}; /**< */ - mutable XLSharedStrings m_sharedStrings {}; /**< */ + mutable std::list m_data {}; /**< */ + mutable XLSharedStrings m_sharedStrings; /**< The sharedstrings object (one for each doc)*/ XLRelationships m_docRelationships {}; /**< A pointer to the document relationships object*/ XLRelationships m_wbkRelationships {}; /**< A pointer to the document relationships object*/ XLContentTypes m_contentTypes {}; /**< A pointer to the content types object*/ XLAppProperties m_appProperties {}; /**< A pointer to the App properties object */ XLProperties m_coreProperties {}; /**< A pointer to the Core properties object*/ + XLXmlData* m_XmlWorkbook {}; XLWorkbook m_workbook {}; /**< A pointer to the workbook object */ IZipArchive m_archive {}; /**< */ + XLStyles m_styles {}; /**< Styles object >*/ }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLException.hpp b/OpenXLSX/headers/XLException.hpp index dbcad116..f92a2fba 100644 --- a/OpenXLSX/headers/XLException.hpp +++ b/OpenXLSX/headers/XLException.hpp @@ -52,6 +52,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== External Includes ===== // #include +#include // ===== OpenXLSX Includes ===== // #include "OpenXLSX-Exports.hpp" @@ -148,6 +149,15 @@ namespace OpenXLSX inline explicit XLFormulaError(const std::string& err) : XLException(err) {}; }; + class OPENXLSX_EXPORT XLLogError + { + public: + inline explicit XLLogError(const std::string& err) + { + std::cerr << err << std::endl; + }; + }; + } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLFormula.hpp b/OpenXLSX/headers/XLFormula.hpp index 21326fbd..b88664d1 100644 --- a/OpenXLSX/headers/XLFormula.hpp +++ b/OpenXLSX/headers/XLFormula.hpp @@ -55,6 +55,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include #include #include +#include // ===== OpenXLSX Includes ===== // #include "OpenXLSX-Exports.hpp" @@ -108,6 +109,8 @@ namespace OpenXLSX m_formulaString = std::string(formula); else m_formulaString = formula.c_str(); + + checkIfError(); } /** @@ -170,6 +173,7 @@ namespace OpenXLSX void set(T formula) { *this = formula; + checkIfError(); } /** @@ -190,8 +194,23 @@ namespace OpenXLSX */ XLFormula& clear(); + /** + * @brief update the formula considering that the expression + * passed as argument is about to be cancelled + * @param toBeDeleted expression that will be replaced by "#REF!" + * @return Return a reference to the cleared object, which is unchanged if + * it does not contain the param + */ + XLFormula updateDeleting(const std::string& toBeDeleted); + + bool hasError() const; + + private: + void checkIfError(); + private: std::string m_formulaString; /**< A std::string, holding the formula string.*/ + bool m_isError {false}; }; /** @@ -267,6 +286,17 @@ namespace OpenXLSX */ XLFormulaProxy& clear(); + /** + * @brief update the formula considering that the expression + * passed as argument is about to be cancelled + * @param toBeDeleted expression that will be replaced by "#REF!" + * @return Return a reference to the cleared object, which is unchanged if + * it does not contain the param + */ + XLFormula updateDeleting(const std::string& toBeDeleted); + + bool hasError() const; + /** * @brief Conversion operator, for converting the object to a std::string. * @return The formula as a std::string. @@ -285,7 +315,7 @@ namespace OpenXLSX * @param cell Pointer to the associated cell object. * @param cellNode Pointer to the associated cell node object. */ - XLFormulaProxy(XLCell* cell, XMLNode* cellNode); + XLFormulaProxy(XLCell* cell, std::shared_ptr cellNode); /** * @brief Copy constructor. @@ -321,7 +351,7 @@ namespace OpenXLSX //---------- Private Member Variables ---------- // XLCell* m_cell; /**< Pointer to the owning XLCell object. */ - XMLNode* m_cellNode; /**< Pointer to corresponding XML cell node. */ + std::shared_ptr m_cellNode; /**< Pointer to corresponding XML cell node. */ }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLNamedRange.hpp b/OpenXLSX/headers/XLNamedRange.hpp new file mode 100644 index 00000000..bf29647c --- /dev/null +++ b/OpenXLSX/headers/XLNamedRange.hpp @@ -0,0 +1,142 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLNAMEDRANGE_HPP +#define OPENXLSX_XLNAMEDRANGE_HPP + +#pragma warning(push) +#pragma warning(disable : 4251) +#pragma warning(disable : 4275) + +// ===== External Includes ===== // +#include + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLCellRange.hpp" + + +namespace OpenXLSX +{ + /** + * @brief This class derivate from cell range class, + * it just add the method to creating, .... + */ + class OPENXLSX_EXPORT XLNamedRange : public XLCellRange + { + public: + /** + * @brief Constructor + * @param rangeName Name of the range to be created + * @param reference Reference of the cell/range to be named: Sheet1!$I$17:$I$19 + * @param localSheetId Id of the sheet where the name is defined, default is 0 (global) + */ + XLNamedRange(const std::string& name, + const std::string& reference, + uint32_t localSheetId, + const XLCellRange& rng); + /** + * @brief Destructor [default] + * @note This implements the default destructor. + */ + ~XLNamedRange(); + + /** + * @brief The copy constructor + * @param other The made range object to be copied and assigned. + * @return A reference to the new object. + */ + XLNamedRange(const XLNamedRange& other); + + /** + * @brief The copy assignment operator [default] + * @param other The named range to be copied and assigned. + * @return A reference to the new object. + * @throws A std::range_error if the source range and destination range are of different size and shape. + * @note This implements the default copy assignment operator. + */ + XLNamedRange& operator=(const XLNamedRange& other); + + /** + * @brief The move assignment operator + * @param other The named range to be moved and assigned. + * @return A reference to the new object. + * @note This implements the default move assignment operator. + */ + XLNamedRange& operator=(XLNamedRange&& other) noexcept; + + /** + * @brief + * @return The name of the named range. + */ + const std::string& name() const; + + /** + * @brief + * @return The reference of the range. + */ + const std::string& reference() const; + + /** + * @brief + * @return The name of the named range. + */ + uint32_t localSheetId() const; + + /** + * @brief + * @return The first cell of the range. + */ + XLCell firstCell() const; + + private: + uint32_t m_localSheetId; + std::string m_name; + std::string m_reference; + }; +} // namespace OpenXLSX + +#pragma warning(pop) +#endif // OPENXLSX_XLNAMEDRANGE_HPP diff --git a/OpenXLSX/headers/XLRelationships.hpp b/OpenXLSX/headers/XLRelationships.hpp index d0d4b8a4..74b30edf 100644 --- a/OpenXLSX/headers/XLRelationships.hpp +++ b/OpenXLSX/headers/XLRelationships.hpp @@ -83,6 +83,7 @@ namespace OpenXLSX Theme, Styles, Chart, + Table, ChartStyle, ChartColorStyle, Image, @@ -161,6 +162,7 @@ namespace OpenXLSX */ std::string id() const; + private: // ---------- Private Member Variables ---------- // std::unique_ptr m_relationshipNode; /**< An XMLNode object with the relationship item */ }; @@ -269,8 +271,14 @@ namespace OpenXLSX * @return true if the XLRelationshipItem exists; otherwise false. */ bool idExists(const std::string& id) const; + + protected: // ---------- Protected Member Functions ---------- // + /** + * @brief Get the next available id "rId". Find holes in the list + * @return return the rId{0} string. + */ + std::string getAvailableRelsId() const; - // ---------- Protected Member Functions ---------- // }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLRow.hpp b/OpenXLSX/headers/XLRow.hpp index a1647f21..f0104672 100644 --- a/OpenXLSX/headers/XLRow.hpp +++ b/OpenXLSX/headers/XLRow.hpp @@ -58,6 +58,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. namespace OpenXLSX { class XLRowRange; + class XLWorksheet; /** * @brief The XLRow class represent a row in an Excel spreadsheet. Using XLRow objects, various row formatting @@ -86,7 +87,7 @@ namespace OpenXLSX * @param rowNode * @param sharedStrings */ - XLRow(const XMLNode& rowNode, const XLSharedStrings& sharedStrings); + XLRow(const XMLNode& rowNode, const XLWorksheet* wks); /** * @brief Copy Constructor @@ -232,7 +233,7 @@ namespace OpenXLSX //---------- PRIVATE MEMBER VARIABLES ----------// std::unique_ptr m_rowNode; /**< The XMLNode object for the row. */ - XLSharedStrings m_sharedStrings; /**< */ + const XLWorksheet* m_worksheet; /**< */ XLRowDataProxy m_rowDataProxy; /**< */ }; @@ -335,7 +336,7 @@ namespace OpenXLSX uint32_t m_firstRow { 1 }; /**< The cell reference of the first cell in the range */ uint32_t m_lastRow { 1 }; /**< The cell reference of the last cell in the range */ XLRow m_currentRow; /**< */ - XLSharedStrings m_sharedStrings; /**< */ + const XLWorksheet* m_worksheet; /**< */ }; /** @@ -344,6 +345,7 @@ namespace OpenXLSX class OPENXLSX_EXPORT XLRowRange { friend class XLRowIterator; + friend class XLTableRowIterator; //---------------------------------------------------------------------------------------------------------------------- // Public Member Functions @@ -357,7 +359,7 @@ namespace OpenXLSX * @param last * @param sharedStrings */ - explicit XLRowRange(const XMLNode& dataNode, uint32_t first, uint32_t last, const XLSharedStrings& sharedStrings); + explicit XLRowRange(const XMLNode& dataNode, uint32_t first, uint32_t last, const XLWorksheet* wks); /** * @brief @@ -409,14 +411,14 @@ namespace OpenXLSX XLRowIterator end(); //---------------------------------------------------------------------------------------------------------------------- - // Private Member Variables + // Protected Member Variables //---------------------------------------------------------------------------------------------------------------------- - private: + protected: std::unique_ptr m_dataNode; /**< */ uint32_t m_firstRow; /**< The cell reference of the first cell in the range */ uint32_t m_lastRow; /**< The cell reference of the last cell in the range */ - XLSharedStrings m_sharedStrings; /**< */ + const XLWorksheet* m_worksheet; /**< */ }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLRowData.hpp b/OpenXLSX/headers/XLRowData.hpp index 2ef0f12f..1f26cee1 100644 --- a/OpenXLSX/headers/XLRowData.hpp +++ b/OpenXLSX/headers/XLRowData.hpp @@ -194,12 +194,13 @@ namespace OpenXLSX * @param lastColumn The index of the last column. * @param sharedStrings A pointer to the shared strings repository. */ - explicit XLRowDataRange(const XMLNode& rowNode, uint16_t firstColumn, uint16_t lastColumn, const XLSharedStrings& sharedStrings); + explicit XLRowDataRange(const XMLNode& rowNode, uint16_t firstColumn, + uint16_t lastColumn, const XLWorksheet* wks); std::unique_ptr m_rowNode; /**< */ uint16_t m_firstCol { 1 }; /**< The cell reference of the first cell in the range */ uint16_t m_lastCol { 1 }; /**< The cell reference of the last cell in the range */ - XLSharedStrings m_sharedStrings; /**< */ + const XLWorksheet* m_worksheet; /**< */ }; /** @@ -269,7 +270,7 @@ namespace OpenXLSX // ===== If the container value_type is a POD type, use the overloaded operator= on each cell. else { - auto range = XLRowDataRange(*m_rowNode, 1, values.size(), getSharedStrings()); + auto range = XLRowDataRange(*m_rowNode, 1, values.size()/*, getSharedStrings()*/); auto dst = range.begin(); auto src = values.begin(); @@ -364,12 +365,6 @@ namespace OpenXLSX */ std::vector getValues() const; - /** - * @brief Helper function for getting a pointer to the shared strings repository. - * @return A pointer to an XLSharedStrings object. - */ - XLSharedStrings getSharedStrings() const; - /** * @brief Convenience function for erasing the first 'count' numbers of values in the row. * @param count The number of values to erase. diff --git a/OpenXLSX/headers/XLSharedStrings.hpp b/OpenXLSX/headers/XLSharedStrings.hpp index 94914c56..6d28053d 100644 --- a/OpenXLSX/headers/XLSharedStrings.hpp +++ b/OpenXLSX/headers/XLSharedStrings.hpp @@ -50,13 +50,15 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #pragma warning(disable : 4251) #pragma warning(disable : 4275) -#include +#include #include + // ===== OpenXLSX Includes ===== // #include "OpenXLSX-Exports.hpp" #include "XLXmlFile.hpp" + namespace OpenXLSX { /** @@ -80,7 +82,7 @@ namespace OpenXLSX * @brief * @param xmlData */ - explicit XLSharedStrings(XLXmlData* xmlData, std::deque *stringCache); + explicit XLSharedStrings(XLXmlData* xmlData); /** * @brief Destructor @@ -118,7 +120,7 @@ namespace OpenXLSX * @param str * @return */ - int32_t getStringIndex(const std::string& str) const; + uint32_t getStringIndex(const std::string& str) const; /** * @brief @@ -139,7 +141,7 @@ namespace OpenXLSX * @param str The string to append. * @return A long int with the index of the appended string */ - int32_t appendString(const std::string& str); + uint32_t appendString(const std::string& str); /** * @brief Clear the string at the given index. @@ -151,7 +153,8 @@ namespace OpenXLSX void clearString(uint64_t index); private: - std::deque *m_stringCache {}; /** < Each string must have an unchanging memory address; hence the use of std::deque */ + std::vector m_stringShared; /** < Each string must have an unchanging memory address; hence the use of std::deque */ + //std::deque *m_stringCache {}; /** < Each string must have an unchanging memory address; hence the use of std::deque */ }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLSheet.hpp b/OpenXLSX/headers/XLSheet.hpp index 71c41940..e9363ae6 100644 --- a/OpenXLSX/headers/XLSheet.hpp +++ b/OpenXLSX/headers/XLSheet.hpp @@ -194,7 +194,7 @@ namespace OpenXLSX * @brief * @param color */ - void setColor(const XLColor& color) + void setTabColor(const XLColor& color) { static_cast(*this).setColor_impl(color); } @@ -294,6 +294,7 @@ namespace OpenXLSX .setParam("sheetID", relationshipID()) .setParam("cloneName", newName)); } + }; /** @@ -304,6 +305,7 @@ namespace OpenXLSX friend class XLCell; friend class XLRow; friend class XLWorkbook; + friend class XLCellValueProxy; friend class XLSheetBase; //---------------------------------------------------------------------------------------------------------------------- @@ -350,7 +352,7 @@ namespace OpenXLSX * @note The move assignment operator has been explicitly deleted. */ XLWorksheet& operator=(XLWorksheet&& other) = default; - + /** * @brief * @param ref @@ -386,6 +388,12 @@ namespace OpenXLSX * @return A const XLCellRange object with the requested range. */ XLCellRange range(const XLCellReference& topLeft, const XLCellReference& bottomRight) const; +/** + * @brief Get a range with the given coordinates. + * @param topLeft A Excel type range i.e "A3:C12". + * @return A const XLCellRange object with the requested range. + */ + XLCellRange range(const std::string& range) const; /** * @brief @@ -484,6 +492,7 @@ namespace OpenXLSX * @param selected */ void setActive_impl(); + }; /** @@ -640,7 +649,7 @@ namespace OpenXLSX * @brief * @param color */ - void setColor(const XLColor& color); + void setTabColor(const XLColor& color); /** * @brief Method for getting the index of the sheet. diff --git a/OpenXLSX/headers/XLStyles.hpp b/OpenXLSX/headers/XLStyles.hpp new file mode 100644 index 00000000..7824116e --- /dev/null +++ b/OpenXLSX/headers/XLStyles.hpp @@ -0,0 +1,177 @@ +/* + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + Copyright (c) 2018, Kenneth Troldal Balslev + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OPENXLSX_XLSYLES_HPP +#define OPENXLSX_XLSYLES_HPP + +#pragma warning(push) +#pragma warning(disable : 4251) +#pragma warning(disable : 4275) + +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLXmlFile.hpp" +#include "XLColor.hpp" +#include "XLXmlParser.hpp" + + +namespace OpenXLSX +{ + class XLStyle; + class XLNumberFormat; + class XLFont; + class XLCell; + + // ================================================================================ + // XLStyles Class + // ================================================================================ + class OPENXLSX_EXPORT XLStyles : public XLXmlFile + { + friend class XLCell; + friend class XLFont; + friend class XLStyle; + friend XLDocument; + + public: + XLStyles() = default; + ~XLStyles(); + XLStyles(XLXmlData* xmlData); + XLStyle style(const XLCell& cell) const; + std::string formatString(int numFmtId) const; + + private: + void init(const XLXmlData* stylesData); + + private: + std::vector m_VecStyle; + }; + + // ================================================================================ + // XLStyle Class + // ================================================================================ + class OPENXLSX_EXPORT XLStyle + { + friend class XLCell; + friend class XLFont; + friend class XLStyles; + + private: + explicit XLStyle(const XLDocument& doc); + + public: + ~XLStyle() = default; + + XLNumberFormat numberFormat() const; + std::string formatString() const; + int numFmtId() const; + XLFont font() const; + + //alignment is in cellXfs, revise to cache the node to lookup + //applyXXX attributes, alignment and stuff we missed. + //lazy cache may be best + + private: + int m_numFmtId = -1; + int m_fontId = -1; + int m_fillId = -1; + int m_borderId = -1; + int m_xfId = -1; + std::reference_wrapper m_doc; + }; + + // ================================================================================ + // Font Class + // ================================================================================ + class OPENXLSX_EXPORT XLFont + { + friend class XLStyle; + + private: + XLFont(const XLStyle& style, const XMLNode& node); + + public: + ~XLFont(); + XLFont(XLFont const&) = delete; + XLFont& operator=(XLFont const&) = delete; + std::string name() const; + double size() const; + XLColor color() const; + int colorIndex() const; + int underline()const; + bool strikethrough() const; + bool bold() const; + bool italic() const; + bool isValid() const; + + private: + std::reference_wrapper m_style; + std::unique_ptr m_node; + }; + + // ================================================================================ + // XLNumberFormat Class + // ================================================================================ + class OPENXLSX_EXPORT XLNumberFormat + { + public: + enum class XLNumberType { kUnkown, kDate, kPercent, kCurrency }; + + public: + explicit XLNumberFormat(const XLStyle& style); + ~XLNumberFormat() = default; + + XLNumberType type(); + std::string currencySymbol() const; + + private: + XLNumberType tryFindType(); + XLNumberType tryBuiltinType(); + + private: + std::reference_wrapper m_style; + std::string m_currencySymbol; + std::string m_fmtLocal; //TODO: get precision etc. + }; +}// namespace OpenXLSX + +#pragma warning(pop) +#endif // OPENXLSX_XLSYLES_HPP \ No newline at end of file diff --git a/OpenXLSX/headers/XLTable.hpp b/OpenXLSX/headers/XLTable.hpp new file mode 100644 index 00000000..0df16076 --- /dev/null +++ b/OpenXLSX/headers/XLTable.hpp @@ -0,0 +1,342 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLTABLE_HPP +#define OPENXLSX_XLTABLE_HPP + +#pragma warning(push) +//#pragma warning(disable : 4251) +//#pragma warning(disable : 4275) + +// ===== External Includes ===== // +#include +#include +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLAutofilter.hpp" +#include "XLXmlData.hpp" +#include "XLTableColumn.hpp" +#include "XLTableRows.hpp" +#include "XLTableStyle.hpp" +#include "XLSheet.hpp" +namespace OpenXLSX +{ + class OPENXLSX_EXPORT XLTable + { + friend class XLTableColumn; + friend class XLTableColumnTotalProxy; + friend class XLTableColumnFormulaProxy; + friend class XLTableColumnTotalLabelProxy; + friend class XLTableStyle; + + public: + /** + * @brief The constructor. + * @param xmlData from the table file + */ + explicit XLTable(XLXmlData* xmlData); + + /** + * @brief The destructor. + */ + ~XLTable(); + + /** + * @brief + * @return the name of the table + */ + const std::string name() const; + + /** + * @brief + * @return the reference cells of the table (without the worksheet) + */ + const std::string ref() const; + + /** + * @brief + * @return A vector containing the columns names + */ + std::vector columnNames() const; + + /** + * @brief get column index from name + * @param name the name of the requested column index + * @return The index of the column + */ + uint16_t columnIndex(const std::string& name) const; + + /** + * @brief get column name from index + * @param index of the requested column + * @return the name of the column + */ + const std::string columnName(uint16_t index) const; + + /** + * @brief + * @param name the name of the requested column + * @return a pointer to the TableColumn object + */ + XLTableColumn& column(const std::string& name); + + /** + * @brief the column vector to be iterate + * @return a const ref on the vector of columns + */ + const std::vector& columns() const; + + /** + * @brief + * @return A pointer to the worksheet object the table belong to + */ + XLWorksheet* getWorksheet(); + + /** + * @brief + * @return an XLCellRange object of the whole table + * including header and total row if visibles + */ + XLCellRange tableRange(); + + /** + * @brief + * @return a ref to the XLCellRange object of data body range of the table + * exclude header and total row + */ + const XLCellRange& dataBodyRange() const; + + /** + * @brief + * @return a ref to the XLCellRange object of data body range of the table + * exclude header and total row + */ + XLCellRange& dataBodyRange(); + + + /** + * @brief + * @return an XLTableRows iterable object (only data body range) + */ + XLTableRows tableRows(); + + + /** + * @brief check if the header row is displayed + * @return true if header is visible + */ + bool isHeaderVisible() const; + + + /** + * @brief check if the total row is displayed + * @return true if total row is visible + */ + bool isTotalVisible() const; + + /** + * @brief + * @param visible + */ + void setHeaderVisible(bool visible = true); + + /** + * @brief Manage display of the Total Row + * @param visible + */ + void setTotalVisible(bool visible = true); + + /** + * @brief create the autofilter + * @return the autofilter object + * @note it will turn on visible the headers if required + */ + XLAutofilter autofilter(); + + /** + * @brief remove the autofilter + * @note it is called automatically when removing headers + */ + void removeAutofilter(); + + /** + * @brief return the table style obect + */ + XLTableStyle tableStyle(); + + /** + * @brief + * @return number of columns + */ + uint16_t columnsCount() const; + + /** + * @brief + * @return number of data rows, excluding header and total row + */ + uint32_t rowsCount() const; + + /** + * @brief set the name of the table + * @param tableName name of the table + */ + void setName(const std::string& tableName); + + /** + * @brief Insert a new column before index position + * @param columnName name of the new column, will be incremented if already exists + * @param index index of insertion + * @note the column at the right side of the table are not shifted + */ + XLTableColumn& insertColumn(const std::string& columnName, uint16_t index = 0); + + /** + * @brief Append a new column at right side of the table + * @param columnName name of the new column, will be incremented if already exists + * @note the column at the right side of the table are not shifted + */ + XLTableColumn& appendColumn(const std::string& columnName); + + /** + * @brief delete the column + * @param columnName of the column to be deleted + * @note the column at the right side of the table are not shifted + */ + void deleteColumn(const std::string& columnName ); + + /** + * @brief Insert a new row before index position + * @param index index of insertion + * @note existing cell info may be deleted if there is a column formula + * The data below the table are not shifted down + */ + XLCellRange insertRow(uint16_t index = 0); + + /** + * @brief Append a new row at the bottom side of the table + * @return a XLCellRange of the appened row + * @note existing cell info may be deleted if there is a column formula + * The data below the table are not shifted down + */ + XLCellRange appendRow(); + + /** + * @brief delete a row. Although the Excel behavior, only the row + * is impacted, the row belows are not lifted up + * @param index of the row to be deleted + * @note the data below the table are not shifted + */ + void deleteRow(uint32_t index ); + + //---------------------------------------------------------------------------------------------------------------------- + // Protected + //---------------------------------------------------------------------------------------------------------------------- + protected: + + /** + * @brief set the formulas in the worksheet for all the columns + * formulas are read directly in the table.xml file + */ + void setColumnFormulas() const; + + /** + * @brief set the formulas in the worksheet for the all the total row + * the formulas is based on the attribute totalsRowFunction of each col + */ + void setTotalFormulas() const; + + /** + * @brief set theLabels in the worksheet for the all the total row + * the formulas is based on the attribute totalsRowLabel of each col + */ + void setTotalLabels() const; + + /** + * @brief set the headers in the worksheet for all the columns + * headers name are read directly in the table.xml file + */ + void setHeaderLabels() const; + + //---------------------------------------------------------------------------------------------------------------------- + // Private Methods + //---------------------------------------------------------------------------------------------------------------------- + private: + + /** + * @brief Load the columns in the vector member variable + * using the table.xml file. Update the count in the xml file + */ + void updateColumns(); + + /** + * @brief Adjust the ref according to m_dataBodyRange + * and the state of visibility of headers and total + */ + void adjustRef(); + + /** + * @brief create the XML entry for the autofilter and setup its ref + * + */ + void setupAutofilter(); + + //---------------------------------------------------------------------------------------------------------------------- + // Private Member Variables + //---------------------------------------------------------------------------------------------------------------------- + private: + XLXmlData* m_pXmlData; + std::vector m_columns; + XLWorksheet m_sheet; + XLCellRange m_dataBodyRange; // without header and total + // cell range + // sheet + // filter + // sort state + // tablestyle + }; +} // namespace OpenXLSX + +//#pragma warning(pop) +#endif // OPENXLSX_XLTABLE_HPP diff --git a/OpenXLSX/headers/XLTableColumn.hpp b/OpenXLSX/headers/XLTableColumn.hpp new file mode 100644 index 00000000..27e39dcb --- /dev/null +++ b/OpenXLSX/headers/XLTableColumn.hpp @@ -0,0 +1,457 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLTABLECOLUMN_HPP +#define OPENXLSX_XLTABLECOLUMN_HPP + +#pragma warning(push) +//#pragma warning(disable : 4251) +//#pragma warning(disable : 4275) + +// ===== External Includes ===== // +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLXmlParser.hpp" +#include "XLCellRange.hpp" + +namespace OpenXLSX +{ + class XLTable; + + + /** + * @brief The XLTableColumnFormulaProxy class is used for proxy (or placeholder) objects for XLTableColumnd formulas objects. + * @details The XLTableColumnFormulaProxy class is used for proxy (or placeholder) objects forXLTableColumnd formulas objects. + * The purpose is to enable implicit conversion during assignment operations. XLTableColumnd formulas objects + * can not be constructed manually by the user, only through XLTableColumn objects. + */ + class OPENXLSX_EXPORT XLTableColumnProxy + { + friend class XLTableColumn; + + public: + //---------- Public Member Functions ----------// + + /** + * @brief Destructor + */ + ~XLTableColumnProxy(); + + /** + * @brief Copy assignment operator. + * @param other XLCellValueProxy object to be copied. + * @return A reference to the current object. + */ + XLTableColumnProxy& operator=(const XLTableColumnProxy& other); + + /** + * @brief Templated assignment operator + * @tparam T The type of numberValue assigned to the object. + * @param formula The formula to be set + * @return A reference to the current object. + */ + virtual XLTableColumnProxy& operator=(const std::string& formula) = 0; + + void set(const std::string& formula) + { + *this = formula; + } + + std::string get() const + { + return getFormula(); + } + + //XLTableColumnProxy& clear(); + + /** + * @brief Set the cell value to a error state. + * @return A reference to the current object. + */ + XLTableColumnProxy& setError(const std::string & error); + + /** + * @brief Implicitly convert the XLTableColumnProxy object to a string object. + * @return a string corresponding to the formula. + */ + operator std::string() { return getFormula(); }; // NOLINT + + /** + * @brief Implicitly convert the XLTableColumnProxy object to a string object. + * @return a string corresponding to the formula. + */ + operator std::string () const { return getFormula(); }; + + protected: + //---------- Private Member Functions ---------- // + + /** + * @brief Constructor + * @param attr Pointer to the corresponding XML attribute object. + */ + XLTableColumnProxy(const XMLNode& dataNode, + const std::string& attr, const XLTable& table); + + /** + * @brief Copy constructor + * @param other Object to be copied. + */ + XLTableColumnProxy(const XLTableColumnProxy& other); + + /** + * @brief Move constructor + * @param other Object to be moved. + */ + XLTableColumnProxy(XLTableColumnProxy&& other) noexcept; + + /** + * @brief Move assignment operator + * @param other Object to be moved + * @return Reference to moved-to pbject. + */ + XLTableColumnProxy& operator=(XLTableColumnProxy&& other) noexcept; + + /** + * @brief Set the cell to a string value. + * @param formula The value to be set. + */ + virtual void setFormula(const std::string& formula) = 0; + + /** + * @brief Get a copy of the XLCellValue object for the cell. + * @return An XLCellValue object. + */ + virtual std::string getFormula() const = 0; + + //---------- Private Member Variables ---------- // + + std::shared_ptr m_node; /**< Pointer to corresponding XML attribute */ + std::string m_attribute; + const XLTable& m_table; + }; // Class + + class OPENXLSX_EXPORT XLTableColumnTotalProxy : public XLTableColumnProxy + { + friend class XLTableColumn; + + public: + //---------- Public Member Functions ----------// + + /** + * @brief Destructor + */ + ~XLTableColumnTotalProxy()= default; + + /** + * @brief Templated assignment operator + * @param formula The formula to be set + * @return A reference to the current object. + */ + XLTableColumnProxy& operator=(const std::string& formula); + + void clear(); + + private: + //---------- Private Member Functions ---------- // + + /** + * @brief Constructor + * @param attr Pointer to the corresponding XML attribute object. + */ + XLTableColumnTotalProxy(const XMLNode& dataNode, + const std::string& attr, const XLTable& table) + : XLTableColumnProxy(dataNode, attr,table) {}; + + /** + * @brief Set the cell to a string value. + * @param formula The value to be set. + */ + void setFormula(const std::string& formula); + + /** + * @brief Get a copy of the XLCellValue object for the cell. + * @return An XLCellValue object. + */ + std::string getFormula() const; + }; // Class + + class OPENXLSX_EXPORT XLTableColumnFormulaProxy : public XLTableColumnProxy + { + friend class XLTableColumn; + + public: + //---------- Public Member Functions ----------// + + /** + * @brief Destructor + */ + ~XLTableColumnFormulaProxy()= default; + + /** + * @brief Templated assignment operator + * @param formula The formula to be set + * @return A reference to the current object. + */ + XLTableColumnProxy& operator=(const std::string& formula); + + void clear(); + + private: + //---------- Private Member Functions ---------- // + + /** + * @brief Constructor + * @param attr Pointer to the corresponding XML attribute object. + */ + XLTableColumnFormulaProxy(const XMLNode& dataNode, + const std::string& attr, const XLTable& table) + : XLTableColumnProxy(dataNode, attr,table) {}; + + /** + * @brief Set the cell to a string value. + * @param formula The value to be set. + */ + void setFormula(const std::string& formula); + + /** + * @brief Get a copy of the XLCellValue object for the cell. + * @return An XLCellValue object. + */ + std::string getFormula() const; + }; // Class + + class OPENXLSX_EXPORT XLTableColumnTotalLabelProxy : public XLTableColumnProxy + { + friend class XLTableColumn; + + public: + //---------- Public Member Functions ----------// + + /** + * @brief Destructor + */ + ~XLTableColumnTotalLabelProxy()= default; + + /** + * @brief Templated assignment operator + * @param formula The formula to be set + * @return A reference to the current object. + */ + XLTableColumnProxy& operator=(const std::string& formula); + + void clear(); + + private: + //---------- Private Member Functions ---------- // + + /** + * @brief Constructor + * @param attr Pointer to the corresponding XML attribute object. + */ + XLTableColumnTotalLabelProxy(const XMLNode& dataNode, + const std::string& attr, const XLTable& table) + : XLTableColumnProxy(dataNode, attr,table) {}; + + /** + * @brief Set the cell to a string value. + * @param formula The value to be set. + */ + void setFormula(const std::string& formula); + + /** + * @brief Get a copy of the XLCellValue object for the cell. + * @return An XLCellValue object. + */ + std::string getFormula() const; + }; // Class + + + class OPENXLSX_EXPORT XLTableColumn + { + friend class XLTable; + + public: + /** + * @brief The constructor. + * @param dataNode XMLNode of the column + */ + XLTableColumn(const XMLNode& dataNode, const XLTable& table); + + /** + * @brief Copy Constructor + * @note The copy constructor is explicitly deleted + */ + XLTableColumn(const XLTableColumn& other); + + /** + * @brief Move Constructor + * @note The move constructor has been explicitly deleted. + */ + XLTableColumn(XLTableColumn&& other) noexcept; + + //XLTableColumn(const XLTableColumn&) = delete; + //XLTableColumn& operator=(const XLTableColumn&) = delete; + ~XLTableColumn(); + + /** + * @brief Copy assignment operator. + * @note The copy assignment operator is explicitly deleted. + */ + XLTableColumn& operator=(const XLTableColumn& other); + + /** + * @brief Move assignment operator. + * @note The move assignment operator has been explicitly deleted. + */ + XLTableColumn& operator=(XLTableColumn&& other) noexcept; + + + /** + * @brief the name of the column (header) + * @return the column name + */ + std::string name() const; + + /** + * @brief set the name of the column (header) + * @param name set the column name + */ + void setName(const std::string& name) const; + + /** + * @brief the getter setter function + * @return return a XLTableColumnProxy ref which could be implicitely convert to string + */ + XLTableColumnProxy& totalsRowFormula(); + + /** + * @brief the getter setter function + * @return return a XLTableColumnProxy ref which could be implicitely convert to string + */ + const XLTableColumnProxy& totalsRowFormula() const; + + /** + * @brief clear the total row function of this columns and trigger the sheet update + */ + void clearTotalsRowFormula(); + + /** + * @brief the getter setter function + * @return return a XLTableColumnProxy ref which could be implicitely convert to string + */ + XLTableColumnFormulaProxy& columnFormula(); + + /** + * @brief the getter setter function + * @return return a XLTableColumnFormulaProxy ref which could be implicitely convert to string + */ + const XLTableColumnFormulaProxy& columnFormula() const; + + /** + * @brief clear the total row function of this columns and trigger the sheet update + */ + void clearColumnFormula(); + + /** + * @brief the getter setter function + * @return return a XLTableColumnProxy ref which could be implicitely convert to string + */ + XLTableColumnProxy& totalsRowLabel(); + + /** + * @brief the getter setter function + * @return return a XLTableColumnProxy ref which could be implicitely convert to string + */ + const XLTableColumnProxy& totalsRowLabel() const; + + /** + * @brief clear the total row function of this columns and trigger the sheet update + */ + void clearTotalsRowLabel(); + + /** + * @brief get the body range of the cell + * @return an XLCellReference of the body range of the column + */ + XLCellRange bodyRange() const; + + protected: + /** + * @brief get the index of the column, from table xml file + * @return the corresponding index + */ + uint16_t index() const; + + /** + * @brief set the index of the column, in the table xml file + * @param index corresponding index + */ + void setIndex(uint16_t index); + + /** + * @brief check if the formula contain the field about to be deleted + * @param toBeDeleted field about to be deleted + * The field is found excluding inside quote. If found the formula is deleted + */ + void formulaUpdateDeleting(const std::string& toBeDeleted); + private: + std::shared_ptr m_dataNode; + const XLTable& m_table; + XLTableColumnTotalProxy m_proxyTotal; + XLTableColumnFormulaProxy m_proxyColumn; + XLTableColumnTotalLabelProxy m_proxyLabel; + + }; + + + + +} // namespace OpenXLSX + +//#pragma warning(pop) +#endif // OPENXLSX_XLTABLECOLUMN_HPP diff --git a/OpenXLSX/headers/XLTableRows.hpp b/OpenXLSX/headers/XLTableRows.hpp new file mode 100644 index 00000000..9cbcb26e --- /dev/null +++ b/OpenXLSX/headers/XLTableRows.hpp @@ -0,0 +1,251 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLTABLEROWS_HPP +#define OPENXLSX_XLTABLEROWS_HPP + +#pragma warning(push) +//#pragma warning(disable : 4251) +//#pragma warning(disable : 4275) + +// ===== External Includes ===== // + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLCellRange.hpp" +#include "XLRow.hpp" + +namespace OpenXLSX +{ + class XLTableRows; + + /** + * @brief The XLTableRowIterator class is used to iterate througt XLTableRows + */ + class OPENXLSX_EXPORT XLTableRowIterator + { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = XLCellRange; + using pointer = XLCellRange*; + using reference = XLCellRange&; + + /** + * @brief + * @param rowRange + * @param loc + */ + explicit XLTableRowIterator(const XLTableRows& tableRows, + XLIteratorLocation loc); + + /** + * @brief + */ + ~XLTableRowIterator(); + + /** + * @brief + * @param other + */ + XLTableRowIterator(const XLTableRowIterator& other); + + /** + * @brief + * @param other + */ + XLTableRowIterator(XLTableRowIterator&& other) noexcept; + + /** + * @brief + * @param other + * @return + */ + XLTableRowIterator& operator=(const XLTableRowIterator& other); + + /** + * @brief + * @param other + * @return + */ + XLTableRowIterator& operator=(XLTableRowIterator&& other) noexcept; + + /** + * @brief + * @return + */ + XLTableRowIterator& operator++(); + + /** + * @brief + * @return + */ + XLTableRowIterator operator++(int); // NOLINT + + /** + * @brief + * @return + */ + reference operator*(); + + /** + * @brief + * @return + */ + pointer operator->(); + + /** + * @brief + * @param rhs + * @return + */ + bool operator==(const XLTableRowIterator& rhs) const; + + /** + * @brief + * @param rhs + * @return + */ + bool operator!=(const XLTableRowIterator& rhs) const; + + /** + * @brief + * @return + */ + explicit operator bool() const; + + + private: + XLCellRange m_range; /**< The cell range reference of the first cell in the range */ + uint32_t m_firstIterRow; + uint32_t m_lastIterRow; + /**< */ + }; + + //---------------------------------------------------------------------------------------------------------------------- + // Class XLTableRows + //---------------------------------------------------------------------------------------------------------------------- + + /** + * @brief Class XLTableRows derived from XLRowRange. + * This an iterable object within the databodyrange + */ + class OPENXLSX_EXPORT XLTableRows : public XLRowRange + { + friend class XLTableRowIterator; + + public: + /** + * @brief The constructor. + * @param XMLNode from the table file + */ + XLTableRows(const XMLNode& dataNode, + uint32_t firstRow, + uint32_t lastRow, + uint16_t firstCol, + uint16_t lastCol, + const XLWorksheet* wks); + /** + * @brief Copy Constructor + * @param other + */ + XLTableRows(const XLTableRows& other); + + /** + * @brief Move Constructor + * @param other + */ + XLTableRows(XLTableRows&& other) noexcept; + + + //XLTableRows(const XLTableColumn&) = delete; + //XLTableRows& operator=(const XLTableColumn&) = delete; + ~XLTableRows(); + + /** + * @brief Copy assignment operator. + * @note The copy assignment operator . + */ + XLTableRows& operator=(const XLTableRows& other); + + /** + * @brief Move assignment operator. + * @note The move assignment operator has been explicitly deleted. + */ + XLTableRows& operator=(XLTableRows&& other) noexcept; + + /** + * @brief Overloading [] operator to access elements in array style + * @return a XLCellRange corresponding to the index row + * @note if the index is out of range, the entire table will be returned + */ + XLCellRange operator[](uint32_t index) const; + + /** + * @brief + * @return + */ + XLTableRowIterator begin(); + + /** + * @brief + * @return + */ + XLTableRowIterator end(); + + /** + * @brief + * @return the number of rows of the table + */ + uint32_t numRows() const; + + + private: + uint16_t m_firstCol; /**< The cell reference of the first cell in the range */ + uint16_t m_lastCol; + }; +} // namespace OpenXLSX + +//#pragma warning(pop) +#endif // OPENXLSX_XLTABLEROWS_HPP diff --git a/OpenXLSX/headers/XLTableStyle.hpp b/OpenXLSX/headers/XLTableStyle.hpp new file mode 100644 index 00000000..f3be0bf0 --- /dev/null +++ b/OpenXLSX/headers/XLTableStyle.hpp @@ -0,0 +1,181 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +#ifndef OPENXLSX_XLTABLESTYLE_HPP +#define OPENXLSX_XLTABLESTYLE_HPP + +#pragma warning(push) +//#pragma warning(disable : 4251) +//#pragma warning(disable : 4275) + +// ===== External Includes ===== // +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "OpenXLSX-Exports.hpp" +#include "XLXmlParser.hpp" + +namespace OpenXLSX +{ + class XLTable; + + class OPENXLSX_EXPORT XLTableStyle + { + public: + /** + * @brief The constructor. + * @param dataNode XMLNode of the column + */ + XLTableStyle(const XMLNode& dataNode, XLTable* table); + + /** + * @brief Copy Constructor + * @note The copy constructor is explicitly deleted + */ + XLTableStyle(const XLTableStyle& other); + + /** + * @brief Move Constructor + * @note The move constructor has been explicitly deleted. + */ + XLTableStyle(XLTableStyle&& other) noexcept; + + //XLtableStyle(constXLTableStyle&) = delete; + //XLtableStyle& operator=(constXLTableStyle&) = delete; + ~XLTableStyle(); + + /** + * @brief Copy assignment operator. + * @note The copy assignment operator is explicitly deleted. + */ + XLTableStyle& operator=(const XLTableStyle& other); + + /** + * @brief Move assignment operator. + * @note The move assignment operator has been explicitly deleted. + */ + XLTableStyle& operator=(XLTableStyle&& other) noexcept; + + + /** + * @brief + * @return the style name + */ + const std::string style() const; + + /** + * @brief + * @return true id the colum stripes are shown + */ + bool columnStripes() const; + + /** + * @brief + * @param show true to show the stripes, default is true + */ + void showColumnStripes(bool show = true) const; + + /** + * @brief + * @return true if the colum stripes are shown + */ + bool rowStripes() const; + + /** + * @brief + * @param show true to show the stripes, default is true + */ + void showRowStripes(bool show = true) const; + + /** + * @brief + * @return true fd the first column is highligted + */ + bool firstColumnHighlighted() const; + + /** + * @brief + * @param show true highlight the column, default is true + */ + void showFirstColumnHighlighted(bool show = true) const; + + /** + * @brief + * @return true fd the first column is highligted + */ + bool lastColumnHighlighted() const; + + /** + * @brief + * @param show true highlight the column, default is true + */ + void showLastColumnHighlighted(bool show = true) const; + + /** + * @brief + * @param name the style name. If not availble, do nothing + */ + void setStyle(const std::string& style) const; + + //---------------------------------------------------------------------------------------------------------------------- + // Protected + //---------------------------------------------------------------------------------------------------------------------- + protected: + /** + * @brief return a vector of available table style + * @param style style to be checked if available + */ + bool isTableStyleAvailable(const std::string& style) const; + + + private: + std::unique_ptr m_dataNode; + XLTable* m_table; + }; +} // namespace OpenXLSX + +//#pragma warning(pop) +#endif // OPENXLSX_XLSTYLE_HPP diff --git a/OpenXLSX/headers/XLTemplates.hpp b/OpenXLSX/headers/XLTemplates.hpp new file mode 100644 index 00000000..52a2f32b --- /dev/null +++ b/OpenXLSX/headers/XLTemplates.hpp @@ -0,0 +1,462 @@ +// +// Created by Written by Akira SHIMAHARA on 28/12/2022. +// + +#ifndef OPENXLSX_XLTEMPLATES_HPP +#define OPENXLSX_XLTEMPLATES_HPP + +#include +namespace OpenXLSX +{ + namespace XLTemplate { + const std::string sharedStrings { + "\n" + "" + "" + }; + + const std::string emptyWorksheet { + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + }; + + const std::string emptyTable { + "(\r\n" + "" + "" + "
" + }; + + const std::string emptySheetRels { + "\r\n" + "" + "" + }; + + const std::string tableRels { + "" + }; + + const std::string totalsRowFunctionList[] = { + "average", "count", "countNums", "custom", "max", "min", "none", "stdDev", "sum", "var" + }; + + // Warning : this shall be in the same order than totalsRowFunctionList + const std::string sheetTotalFunctionList[] = { + "101", "103", "102", "custom", "104", "105", "none", "107", "109", "110" + }; + + const std::string defaultTableStyle[] = { + "TableStyleMedium1", "TableStyleMedium2", "TableStyleMedium3", "TableStyleMedium4", "TableStyleMedium5", + "TableStyleMedium6", "TableStyleMedium7", "TableStyleMedium8", "TableStyleMedium9", "TableStyleMedium10", + "TableStyleMedium11", "TableStyleMedium12", "TableStyleMedium13", "TableStyleMedium14", "TableStyleMedium15", + "TableStyleMedium16", "TableStyleMedium17", "TableStyleMedium18", "TableStyleMedium19", "TableStyleMedium20", + "TableStyleMedium21", "TableStyleMedium22", "TableStyleMedium23", "TableStyleMedium24", "TableStyleMedium25", + "TableStyleMedium26", "TableStyleMedium27", "TableStyleMedium28", + "TableStyleLight1", "TableStyleLight2", "TableStyleLight3", "TableStyleLight4", "TableStyleLight5", + "TableStyleLight6", "TableStyleLight7", "TableStyleLight8", "TableStyleLight9", "TableStyleLight10", + "TableStyleLight11", "TableStyleLight12", "TableStyleLight13", "TableStyleLight14", "TableStyleLight15", + "TableStyleLight16", "TableStyleLight17", "TableStyleLight18", "TableStyleLight19", "TableStyleLight20", + "TableStyleLight21", + "TableStyleDark1", "TableStyleDark2", "TableStyleDark3", "TableStyleDark4", "TableStyleDark5", + "TableStyleDark6", "TableStyleDark7", "TableStyleDark8", "TableStyleDark9", "TableStyleDark10", + "TableStyleDark11", + }; + + const std::string defaultPivotStyle[] = { + "PivotStyleMedium1", "PivotStyleMedium2", "PivotStyleMedium3", "PivotStyleMedium4", "PivotStyleMedium5", + "PivotStyleMedium6", "PivotStyleMedium7", "PivotStyleMedium8", "PivotStyleMedium9", "PivotStyleMedium10", + "PivotStyleMedium11", "PivotStyleMedium12", "PivotStyleMedium13", "PivotStyleMedium14", "PivotStyleMedium15", + "PivotStyleMedium16", "PivotStyleMedium17", "PivotStyleMedium18", "PivotStyleMedium19", "PivotStyleMedium20", + "PivotStyleMedium21", "PivotStyleMedium22", "PivotStyleMedium23", "PivotStyleMedium24", "PivotStyleMedium25", + "PivotStyleMedium26", "PivotStyleMedium27", "PivotStyleMedium28", + "PivotStyleLight1", "PivotStyleLight2", "PivotStyleLight3", "PivotStyleLight4", "PivotStyleLight5", + "PivotStyleLight6", "PivotStyleLight7", "PivotStyleLight8", "PivotStyleLight9", "PivotStyleLight10", + "PivotStyleLight11", "PivotStyleLight12", "PivotStyleLight13", "PivotStyleLight14", "PivotStyleLight15", + "PivotStyleLight16", "PivotStyleLight17", "PivotStyleLight18", "PivotStyleLight19", "PivotStyleLight20", + "PivotStyleLight21", "PivotStyleLight22", "PivotStyleLight23", "PivotStyleLight24", "PivotStyleLight25", + "PivotStyleLight26", "PivotStyleLight27", "PivotStyleLight28", + "PivotStyleDark1", "PivotStyleDark2", "PivotStyleDark3", "PivotStyleDark4", "PivotStyleDark5", + "PivotStyleDark6", "PivotStyleDark7", "PivotStyleDark8", "PivotStyleDark9", "PivotStyleDark10", + "PivotStyleDark11", "PivotStyleDark12", "PivotStyleDark13", "PivotStyleDark14", "PivotStyleDark15", + "PivotStyleDark16", "PivotStyleDark17", "PivotStyleDark18", "PivotStyleDark19", "PivotStyleDark20", + "PivotStyleDark21", "PivotStyleDark22", "PivotStyleDark23", "PivotStyleDark24", "PivotStyleDark25", + "PivotStyleDark26", "PivotStyleDark27", "PivotStyleDark28", + }; + + constexpr int templateSize = 7714; + + constexpr unsigned char templateData[7714] = { + 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xb5, 0x55, 0x30, 0x23, 0xf4, 0x00, 0x00, 0x00, + 0x4c, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x08, 0x02, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x20, 0xa2, 0x04, + 0x02, 0x28, 0xa0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x92, 0x4d, 0x4f, 0xc3, 0x30, 0x0c, 0x86, 0xef, 0x48, 0xfc, + 0x87, 0xc8, 0xf7, 0xd5, 0xdd, 0x90, 0x10, 0x42, 0x4b, 0x77, 0x41, 0x48, 0xbb, 0x21, 0x54, 0x7e, 0x80, 0x49, 0xdc, 0x0f, 0xb5, 0x8d, + 0xa3, 0x24, 0x1b, 0xdd, 0xbf, 0x27, 0x1c, 0x10, 0x54, 0x1a, 0x83, 0x03, 0x47, 0x7f, 0xbd, 0x7e, 0xfc, 0xca, 0xdb, 0xdd, 0x3c, 0x8d, + 0xea, 0xc8, 0x21, 0xf6, 0xe2, 0x34, 0xac, 0x8b, 0x12, 0x14, 0x3b, 0x23, 0xb6, 0x77, 0xad, 0x86, 0x97, 0xfa, 0x71, 0x75, 0x07, 0x2a, + 0x26, 0x72, 0x96, 0x46, 0x71, 0xac, 0xe1, 0xc4, 0x11, 0x76, 0xd5, 0xf5, 0xd5, 0xf6, 0x99, 0x47, 0x4a, 0x79, 0x28, 0x76, 0xbd, 0x8f, + 0x2a, 0xab, 0xb8, 0xa8, 0xa1, 0x4b, 0xc9, 0xdf, 0x23, 0x46, 0xd3, 0xf1, 0x44, 0xb1, 0x10, 0xcf, 0x2e, 0x57, 0x1a, 0x09, 0x13, 0xa5, + 0x1c, 0x86, 0x16, 0x3d, 0x99, 0x81, 0x5a, 0xc6, 0x4d, 0x59, 0xde, 0x62, 0xf8, 0xae, 0x01, 0xd5, 0x42, 0x53, 0xed, 0xad, 0x86, 0xb0, + 0xb7, 0x37, 0xa0, 0xea, 0x93, 0xcf, 0x9b, 0x7f, 0xd7, 0x96, 0xa6, 0xe9, 0x0d, 0x3f, 0x88, 0x39, 0x4c, 0xec, 0xd2, 0x99, 0x15, 0xc8, + 0x73, 0x62, 0x67, 0xd9, 0xae, 0x7c, 0xc8, 0x6c, 0x21, 0xf5, 0xf9, 0x1a, 0x55, 0x53, 0x68, 0x39, 0x69, 0xb0, 0x62, 0x9e, 0x72, 0x3a, + 0x22, 0x79, 0x5f, 0x64, 0x6c, 0xc0, 0xf3, 0x44, 0x9b, 0xbf, 0x13, 0xfd, 0x7c, 0x2d, 0x4e, 0x9c, 0xc8, 0x52, 0x22, 0x34, 0x12, 0xf8, + 0x32, 0xcf, 0x47, 0xc7, 0x25, 0xa0, 0xf5, 0x7f, 0x5a, 0xb4, 0x34, 0xf1, 0xcb, 0x9d, 0x79, 0xc4, 0x37, 0x09, 0xc3, 0xab, 0xc8, 0xf0, + 0xc9, 0x82, 0x8b, 0x1f, 0xa8, 0xde, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x47, 0x88, 0xbc, 0xe2, 0x5d, 0x03, 0x00, 0x00, 0x35, 0x08, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, + 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0xac, 0x55, 0x6d, 0x6f, 0xa3, 0x38, 0x10, 0xfe, + 0x7e, 0xd2, 0xfd, 0x07, 0xc4, 0x77, 0x8a, 0x4d, 0xcc, 0x4b, 0x50, 0xe9, 0x2a, 0x90, 0xa0, 0xab, 0xb4, 0x5d, 0x55, 0x6d, 0xb6, 0xfb, + 0xf1, 0xe4, 0x80, 0x29, 0x56, 0x01, 0x73, 0xc6, 0x34, 0xa9, 0xaa, 0xfd, 0xef, 0x3b, 0x76, 0x42, 0xda, 0x6e, 0x57, 0xa7, 0x5c, 0xf7, + 0xaa, 0xd4, 0xc6, 0x9e, 0xe1, 0xf1, 0x33, 0x33, 0xcf, 0x98, 0xf3, 0x4f, 0xbb, 0xb6, 0xb1, 0x1e, 0x99, 0x1c, 0xb8, 0xe8, 0x12, 0x1b, + 0x9f, 0x21, 0xdb, 0x62, 0x5d, 0x21, 0x4a, 0xde, 0xdd, 0x27, 0xf6, 0xd7, 0x75, 0xee, 0x44, 0xb6, 0x35, 0x28, 0xda, 0x95, 0xb4, 0x11, + 0x1d, 0x4b, 0xec, 0x27, 0x36, 0xd8, 0x9f, 0x2e, 0xfe, 0xfc, 0xe3, 0x7c, 0x2b, 0xe4, 0xc3, 0x46, 0x88, 0x07, 0x0b, 0x00, 0xba, 0x21, + 0xb1, 0x6b, 0xa5, 0xfa, 0xd8, 0x75, 0x87, 0xa2, 0x66, 0x2d, 0x1d, 0xce, 0x44, 0xcf, 0x3a, 0xb0, 0x54, 0x42, 0xb6, 0x54, 0xc1, 0x52, + 0xde, 0xbb, 0x43, 0x2f, 0x19, 0x2d, 0x87, 0x9a, 0x31, 0xd5, 0x36, 0xae, 0x87, 0x50, 0xe0, 0xb6, 0x94, 0x77, 0xf6, 0x1e, 0x21, 0x96, + 0xa7, 0x60, 0x88, 0xaa, 0xe2, 0x05, 0x5b, 0x8a, 0x62, 0x6c, 0x59, 0xa7, 0xf6, 0x20, 0x92, 0x35, 0x54, 0x01, 0xfd, 0xa1, 0xe6, 0xfd, + 0x30, 0xa1, 0xb5, 0xc5, 0x29, 0x70, 0x2d, 0x95, 0x0f, 0x63, 0xef, 0x14, 0xa2, 0xed, 0x01, 0x62, 0xc3, 0x1b, 0xae, 0x9e, 0x0c, 0xa8, + 0x6d, 0xb5, 0x45, 0x7c, 0x79, 0xdf, 0x09, 0x49, 0x37, 0x0d, 0x84, 0xbd, 0xc3, 0xbe, 0xb5, 0x93, 0xf0, 0x0b, 0xe0, 0x1f, 0x23, 0x18, + 0xbc, 0xe9, 0x24, 0x30, 0xbd, 0x3b, 0xaa, 0xe5, 0x85, 0x14, 0x83, 0xa8, 0xd4, 0x19, 0x40, 0xbb, 0x7b, 0xd2, 0xef, 0xe2, 0xc7, 0xc8, + 0xc5, 0xf8, 0x4d, 0x0a, 0x76, 0xef, 0x73, 0x70, 0x1a, 0x12, 0x71, 0x25, 0x7b, 0xe4, 0xba, 0x86, 0x47, 0x56, 0x32, 0xf8, 0x20, 0xab, + 0xe0, 0x88, 0x15, 0xbc, 0x80, 0x61, 0xf4, 0xdb, 0x68, 0x18, 0xa4, 0x65, 0xb4, 0x12, 0x43, 0xf2, 0x3e, 0x88, 0xe6, 0x1f, 0xb9, 0x79, + 0xf6, 0xc5, 0x79, 0xc5, 0x1b, 0x76, 0xb7, 0x97, 0xae, 0x45, 0xfb, 0xfe, 0x0b, 0x6d, 0x75, 0xa5, 0x1a, 0xdb, 0x6a, 0xe8, 0xa0, 0x56, + 0x25, 0x57, 0xac, 0x4c, 0xec, 0x10, 0x96, 0x62, 0xcb, 0xde, 0x6c, 0xc8, 0xb1, 0x4f, 0x47, 0xde, 0x80, 0x15, 0xa3, 0x08, 0x7b, 0xb6, + 0x7b, 0x71, 0x94, 0xf3, 0xb5, 0xb4, 0x4a, 0x56, 0xd1, 0xb1, 0x51, 0x6b, 0x10, 0xf2, 0x04, 0x0f, 0x8e, 0x41, 0x30, 0xf7, 0x7c, 0xed, + 0x09, 0xc2, 0x58, 0x34, 0x8a, 0xc9, 0x8e, 0x2a, 0x96, 0x89, 0x4e, 0x81, 0x0e, 0x0f, 0x71, 0xfd, 0xae, 0xe6, 0x0c, 0x76, 0x56, 0x0b, + 0x50, 0xb8, 0x75, 0xc3, 0xfe, 0x19, 0xb9, 0x64, 0xd0, 0x58, 0xa0, 0x2f, 0x88, 0x15, 0x46, 0x5a, 0xc4, 0x74, 0x33, 0x5c, 0x53, 0x55, + 0x5b, 0xa3, 0x6c, 0x12, 0xdb, 0xfd, 0x3a, 0x40, 0xf0, 0xee, 0x5a, 0x8a, 0x06, 0xba, 0xd3, 0x5d, 0xb2, 0x47, 0xd6, 0x88, 0xde, 0xf4, + 0xc5, 0x86, 0x77, 0x5e, 0xe1, 0xbe, 0x52, 0x28, 0x7d, 0xdf, 0x0e, 0xff, 0x41, 0xa3, 0xb4, 0xd0, 0x81, 0xbb, 0x10, 0xf9, 0x9e, 0xdd, + 0xfe, 0xf9, 0xe7, 0x2c, 0x00, 0x49, 0x19, 0x4f, 0x3a, 0xbc, 0x56, 0xd2, 0x82, 0xe7, 0xcb, 0xe5, 0x67, 0xa8, 0xc5, 0x2d, 0x7d, 0x84, + 0xca, 0x40, 0xfd, 0xcb, 0x43, 0xe3, 0x5e, 0x42, 0xea, 0xa3, 0xbf, 0x9f, 0x17, 0xd9, 0x6c, 0xb6, 0x08, 0xf2, 0xd4, 0xc1, 0x01, 0x9a, + 0x39, 0x5e, 0x40, 0xb0, 0xb3, 0x20, 0x28, 0x77, 0xd2, 0x30, 0x0b, 0x73, 0x2f, 0x45, 0xab, 0x60, 0x1e, 0x7d, 0x87, 0x28, 0x64, 0x10, + 0x17, 0x82, 0x8e, 0xaa, 0x3e, 0x54, 0x5b, 0x63, 0x26, 0x36, 0x21, 0xbf, 0x30, 0x5d, 0xd1, 0xdd, 0x64, 0xc1, 0x28, 0x1e, 0x79, 0xf9, + 0x72, 0xfe, 0x33, 0x3a, 0xfc, 0x39, 0x7a, 0xfe, 0x69, 0x98, 0x6c, 0xdf, 0x75, 0xa4, 0xfa, 0x5e, 0xbb, 0xe3, 0x6c, 0x3b, 0xbc, 0xe8, + 0x42, 0x2f, 0xad, 0xdd, 0x37, 0xde, 0x95, 0x62, 0x9b, 0xd8, 0x5e, 0x18, 0x41, 0x34, 0x4f, 0xd3, 0x12, 0xfb, 0x01, 0x2c, 0xb7, 0xc6, + 0xf8, 0x8d, 0x97, 0xaa, 0x06, 0x8f, 0x08, 0x91, 0xe3, 0xde, 0x5f, 0x8c, 0xdf, 0xd7, 0xc0, 0x18, 0x87, 0x44, 0x6f, 0x82, 0xfe, 0x35, + 0xb3, 0xc4, 0x7e, 0xce, 0x73, 0x32, 0x4f, 0x23, 0xbc, 0x70, 0xb0, 0x17, 0x2e, 0x9c, 0x94, 0x90, 0x0c, 0x12, 0x40, 0x52, 0x27, 0xcf, + 0x51, 0x3e, 0x5b, 0xe5, 0x51, 0x9e, 0xe5, 0x73, 0xc3, 0xc8, 0x7d, 0x45, 0xc9, 0xdc, 0xa0, 0x40, 0xcd, 0xcc, 0x56, 0x67, 0x54, 0x7f, + 0xab, 0x6f, 0x55, 0x0c, 0x57, 0xb5, 0x9e, 0x75, 0x76, 0xe1, 0x59, 0xc6, 0xfa, 0x0c, 0x79, 0x59, 0x62, 0x53, 0xbd, 0xe9, 0xb5, 0x82, + 0x36, 0x05, 0xa8, 0x5c, 0x4f, 0xc6, 0x31, 0xc2, 0xc8, 0x9b, 0x6b, 0x0f, 0xb6, 0x53, 0x9f, 0x07, 0x65, 0x66, 0x10, 0x18, 0x07, 0x7a, + 0x98, 0xa0, 0x45, 0x88, 0xe6, 0xc4, 0x41, 0xab, 0x99, 0xef, 0x90, 0x68, 0xee, 0x39, 0x11, 0x99, 0x79, 0x4e, 0x46, 0x96, 0xde, 0xca, + 0x0f, 0x57, 0xcb, 0x55, 0xea, 0xeb, 0xfa, 0xe8, 0x2f, 0x40, 0xfc, 0x7f, 0xdc, 0x83, 0x46, 0xe7, 0xf1, 0xf4, 0x69, 0xd1, 0x2c, 0x6b, + 0x2a, 0xd5, 0x5a, 0xd2, 0xe2, 0x01, 0x3e, 0x48, 0x37, 0xac, 0x4a, 0xe9, 0x00, 0x4a, 0xda, 0x07, 0x04, 0x7c, 0x5f, 0x93, 0x4d, 0xfd, + 0x28, 0x45, 0x33, 0xa0, 0x48, 0x72, 0x9c, 0x3b, 0x04, 0xcf, 0x91, 0x93, 0xa6, 0x01, 0x71, 0xfc, 0x65, 0x3e, 0xf3, 0x43, 0xbc, 0xcc, + 0x56, 0x7e, 0xfe, 0x42, 0x56, 0x87, 0x5f, 0x7d, 0xf0, 0x16, 0x8a, 0x5c, 0xf3, 0x36, 0xa3, 0x6a, 0x84, 0x0e, 0xd5, 0xcd, 0x69, 0xd6, + 0xb1, 0x1e, 0xf3, 0xc3, 0xee, 0x71, 0xb3, 0xda, 0x6f, 0x1c, 0xea, 0xf4, 0xa6, 0xe9, 0xe2, 0x9b, 0xa5, 0xce, 0xfb, 0xe1, 0xed, 0x7f, + 0x73, 0xbc, 0x85, 0xe8, 0x1b, 0x76, 0xa2, 0x73, 0x7e, 0x77, 0xa2, 0x63, 0xf6, 0xe5, 0x6a, 0x7d, 0x65, 0xb4, 0xf1, 0xcb, 0x00, 0x5c, + 0x93, 0x60, 0x3d, 0x1a, 0x59, 0xb8, 0x53, 0x59, 0x2e, 0x7e, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, + 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xf0, 0x08, 0x58, 0xf4, 0xa5, 0x02, 0x00, 0x00, 0x52, 0x06, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0xa4, 0x55, 0x6d, 0x6b, 0xdb, 0x30, + 0x10, 0xfe, 0x3e, 0xd8, 0x7f, 0x10, 0xfa, 0xee, 0xca, 0x76, 0xe3, 0x2c, 0x09, 0xb6, 0xcb, 0xd2, 0xd4, 0x50, 0xe8, 0xc6, 0xa0, 0x1d, + 0xec, 0xab, 0x62, 0xcb, 0x89, 0xa8, 0x5e, 0x8c, 0x24, 0x67, 0xce, 0xc6, 0xfe, 0xfb, 0x4e, 0x76, 0x5e, 0x1c, 0x3a, 0xb6, 0xd1, 0x7e, + 0x89, 0x4e, 0xe7, 0xd3, 0x73, 0xcf, 0xdd, 0x73, 0x52, 0xd2, 0x9b, 0x4e, 0x0a, 0xb4, 0x63, 0xc6, 0x72, 0xad, 0x32, 0x1c, 0x5d, 0x85, + 0x18, 0x31, 0x55, 0xea, 0x8a, 0xab, 0x4d, 0x86, 0xbf, 0x3e, 0x15, 0xc1, 0x0c, 0x23, 0xeb, 0xa8, 0xaa, 0xa8, 0xd0, 0x8a, 0x65, 0x78, + 0xcf, 0x2c, 0xbe, 0xc9, 0xdf, 0xbf, 0x4b, 0xad, 0xdb, 0x0b, 0xf6, 0xb8, 0x65, 0xcc, 0x21, 0x80, 0x50, 0x36, 0xc3, 0x5b, 0xe7, 0x9a, + 0x05, 0x21, 0xb6, 0xdc, 0x32, 0x49, 0xed, 0x95, 0x6e, 0x98, 0x82, 0x2f, 0xb5, 0x36, 0x92, 0x3a, 0xd8, 0x9a, 0x0d, 0xb1, 0x8d, 0x61, + 0xb4, 0xb2, 0xfe, 0x90, 0x14, 0x24, 0x0e, 0xc3, 0x29, 0x91, 0x94, 0x2b, 0x3c, 0x20, 0x2c, 0x64, 0xf9, 0x3f, 0x20, 0x92, 0x9a, 0xe7, + 0xb6, 0x09, 0x4a, 0x2d, 0x1b, 0xea, 0xf8, 0x9a, 0x0b, 0xee, 0xf6, 0x3d, 0x16, 0x46, 0xb2, 0x5c, 0xdc, 0x6f, 0x94, 0x36, 0x74, 0x2d, + 0x80, 0x6a, 0x17, 0x4d, 0x68, 0x89, 0xba, 0x68, 0x6a, 0x62, 0xd4, 0x99, 0x63, 0x92, 0xde, 0xfb, 0x22, 0x8f, 0xe4, 0xa5, 0xd1, 0x56, + 0xd7, 0xee, 0x0a, 0x70, 0x89, 0xae, 0x6b, 0x5e, 0xb2, 0x97, 0x74, 0xe7, 0x64, 0x4e, 0x68, 0x79, 0x46, 0x02, 0xe4, 0xd7, 0x21, 0x45, + 0x09, 0x09, 0xe3, 0x8b, 0xda, 0x3b, 0xf3, 0x4a, 0xa4, 0x09, 0x31, 0x6c, 0xc7, 0xbd, 0x7c, 0x38, 0x4f, 0x6b, 0xad, 0x9c, 0x45, 0xa5, + 0x6e, 0x95, 0x03, 0x31, 0x81, 0xa8, 0x6f, 0xc1, 0xe2, 0x59, 0xe9, 0xef, 0xaa, 0xf0, 0x9f, 0xbc, 0x73, 0x88, 0xca, 0x53, 0xfb, 0x03, + 0xed, 0xa8, 0x00, 0x4f, 0x8c, 0x49, 0x9e, 0x96, 0x5a, 0x68, 0x83, 0x1c, 0x48, 0x07, 0x9d, 0x8b, 0xbc, 0x47, 0x51, 0xc9, 0x86, 0x88, + 0x5b, 0x2a, 0xf8, 0xda, 0x70, 0xef, 0xac, 0xa9, 0xe4, 0x62, 0x3f, 0xb8, 0xfb, 0x73, 0xbd, 0xda, 0x87, 0x38, 0xc9, 0xa1, 0xf7, 0x3e, + 0x8a, 0x78, 0x1e, 0x87, 0xc5, 0xc2, 0x21, 0x2e, 0xc4, 0x89, 0x55, 0xec, 0x09, 0x80, 0x23, 0x4f, 0x41, 0x3e, 0xc7, 0x8c, 0x2a, 0x60, + 0x83, 0x0e, 0xf6, 0xd3, 0xbe, 0x81, 0xf4, 0x0a, 0x26, 0x6d, 0x80, 0xe9, 0xe3, 0xfe, 0x11, 0xbd, 0x31, 0x74, 0x1f, 0xc5, 0xc9, 0xe8, + 0x00, 0xe9, 0x13, 0xe6, 0xe9, 0x5a, 0x9b, 0x0a, 0x26, 0xfb, 0xdc, 0x8f, 0xa3, 0x2b, 0x4f, 0x05, 0xab, 0x1d, 0x10, 0x35, 0x7c, 0xb3, + 0xf5, 0xab, 0xd3, 0x0d, 0xfc, 0xae, 0xb5, 0x73, 0xa0, 0x7e, 0x9e, 0x56, 0x9c, 0x6e, 0xb4, 0xa2, 0xc2, 0x97, 0x32, 0x80, 0x9c, 0x0c, + 0x28, 0xa7, 0x64, 0x42, 0x3c, 0xfa, 0xe9, 0xff, 0x56, 0x5f, 0x60, 0x77, 0x35, 0x52, 0xad, 0x2c, 0xa4, 0xbb, 0xaf, 0x32, 0x0c, 0xf7, + 0xc8, 0x37, 0xe1, 0x68, 0x42, 0x21, 0x07, 0x73, 0xc0, 0x1b, 0x36, 0x1e, 0x7f, 0x8c, 0x36, 0x60, 0xbf, 0x19, 0x16, 0x75, 0xf5, 0x25, + 0x3e, 0x20, 0x8e, 0x68, 0x5f, 0x90, 0x3e, 0xa5, 0x47, 0x5e, 0xef, 0x0c, 0x7f, 0xf6, 0xd7, 0x55, 0xc0, 0xe4, 0x1c, 0x20, 0xd0, 0xba, + 0xe5, 0xc2, 0x71, 0xf5, 0x07, 0xc2, 0x80, 0x59, 0x75, 0xe7, 0x16, 0x84, 0x5e, 0x01, 0xe7, 0xaf, 0x5e, 0xdf, 0x9c, 0x53, 0x16, 0xe8, + 0x44, 0xc5, 0x6a, 0xda, 0x0a, 0xf7, 0x74, 0xfa, 0x98, 0xe1, 0xb3, 0xfd, 0x89, 0x55, 0xbc, 0x95, 0xf1, 0x29, 0xea, 0x0b, 0xdf, 0x69, + 0xd7, 0x43, 0x64, 0xf8, 0x6c, 0x3f, 0x78, 0xa5, 0xa2, 0xa9, 0xcf, 0xc1, 0x3a, 0xf7, 0x60, 0x61, 0xbc, 0x60, 0x45, 0xad, 0xe1, 0x19, + 0xfe, 0x79, 0xb7, 0xfc, 0x30, 0x5f, 0xdd, 0x15, 0x71, 0x30, 0x0b, 0x97, 0xb3, 0x60, 0x72, 0xcd, 0x92, 0x60, 0x9e, 0x2c, 0x57, 0x41, + 0x32, 0xb9, 0x5d, 0xae, 0x56, 0xc5, 0x3c, 0x8c, 0xc3, 0xdb, 0x5f, 0xa3, 0x07, 0xe0, 0x0d, 0xd7, 0xbf, 0x7f, 0xaf, 0xf2, 0x14, 0x2e, + 0xd6, 0xc2, 0x0a, 0x78, 0x24, 0xcc, 0xa1, 0xd8, 0x43, 0x89, 0x8f, 0x67, 0x5f, 0x86, 0x47, 0x9b, 0x81, 0x7e, 0x3f, 0xa3, 0x40, 0x7b, + 0xcc, 0x7d, 0x1e, 0x4f, 0xc3, 0x8f, 0x49, 0x14, 0x06, 0xc5, 0x75, 0x18, 0x05, 0x93, 0x29, 0x9d, 0x05, 0xb3, 0xe9, 0x75, 0x12, 0x14, + 0x49, 0x14, 0xaf, 0xa6, 0x93, 0xe5, 0x5d, 0x52, 0x24, 0x23, 0xee, 0xc9, 0x2b, 0x9f, 0x89, 0x90, 0x44, 0xd1, 0xf0, 0xe0, 0x78, 0xf2, + 0xc9, 0xc2, 0x71, 0xc9, 0x04, 0x57, 0x47, 0xad, 0x8e, 0x0a, 0x8d, 0xbd, 0x20, 0x12, 0x6c, 0xff, 0x52, 0x04, 0x39, 0x2a, 0x41, 0xce, + 0x7f, 0x06, 0xf9, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xc1, 0x17, 0x10, 0xbe, 0x4e, 0x07, 0x00, 0x00, 0xc6, 0x20, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, + 0x74, 0x68, 0x65, 0x6d, 0x65, 0x2f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0xec, 0x59, 0xcd, 0x8b, 0x1b, 0x37, + 0x14, 0xbf, 0x17, 0xfa, 0x3f, 0x0c, 0x73, 0x77, 0xfc, 0x35, 0xe3, 0x8f, 0x25, 0xde, 0xe0, 0xcf, 0x6c, 0x93, 0xdd, 0x24, 0x64, 0x9d, + 0x94, 0x1c, 0xb5, 0xb6, 0xec, 0x51, 0x56, 0x33, 0x32, 0x92, 0xbc, 0x1b, 0x13, 0x02, 0x25, 0x39, 0xf5, 0x52, 0x28, 0xa4, 0xa5, 0x97, + 0x42, 0x6f, 0x3d, 0x94, 0xd2, 0x40, 0x03, 0x0d, 0xbd, 0xf4, 0x8f, 0x09, 0x24, 0xb4, 0xe9, 0x1f, 0xd1, 0x27, 0xcd, 0xd8, 0x23, 0xad, + 0xe5, 0x24, 0x9b, 0x6c, 0x4a, 0x5a, 0x76, 0x0d, 0x8b, 0x47, 0xfe, 0xbd, 0xa7, 0xa7, 0xf7, 0x9e, 0x7e, 0x7a, 0xf3, 0x74, 0xf1, 0xd2, + 0xbd, 0x98, 0x7a, 0x47, 0x98, 0x0b, 0xc2, 0x92, 0x96, 0x5f, 0xbe, 0x50, 0xf2, 0x3d, 0x9c, 0x8c, 0xd8, 0x98, 0x24, 0xd3, 0x96, 0x7f, + 0x6b, 0x38, 0x28, 0x34, 0x7c, 0x4f, 0x48, 0x94, 0x8c, 0x11, 0x65, 0x09, 0x6e, 0xf9, 0x0b, 0x2c, 0xfc, 0x4b, 0xdb, 0x9f, 0x7e, 0x72, + 0x11, 0x6d, 0xc9, 0x08, 0xc7, 0xd8, 0x03, 0xf9, 0x44, 0x6c, 0xa1, 0x96, 0x1f, 0x49, 0x39, 0xdb, 0x2a, 0x16, 0xc5, 0x08, 0x86, 0x91, + 0xb8, 0xc0, 0x66, 0x38, 0x81, 0xdf, 0x26, 0x8c, 0xc7, 0x48, 0xc2, 0x23, 0x9f, 0x16, 0xc7, 0x1c, 0x1d, 0x83, 0xde, 0x98, 0x16, 0x2b, + 0xa5, 0x52, 0xad, 0x18, 0x23, 0x92, 0xf8, 0x5e, 0x82, 0x62, 0x50, 0x7b, 0x7d, 0x32, 0x21, 0x23, 0xec, 0x0d, 0x95, 0x4a, 0x7f, 0x7b, + 0xa9, 0xbc, 0x4f, 0xe1, 0x31, 0x91, 0x42, 0x0d, 0x8c, 0x28, 0xdf, 0x57, 0xaa, 0xb1, 0x25, 0xa1, 0xb1, 0xe3, 0xc3, 0xb2, 0x42, 0x88, + 0x85, 0xe8, 0x52, 0xee, 0x1d, 0x21, 0xda, 0xf2, 0x61, 0x9e, 0x31, 0x3b, 0x1e, 0xe2, 0x7b, 0xd2, 0xf7, 0x28, 0x12, 0x12, 0x7e, 0x68, + 0xf9, 0x25, 0xfd, 0xe7, 0x17, 0xb7, 0x2f, 0x16, 0xd1, 0x56, 0x26, 0x44, 0xe5, 0x06, 0x59, 0x43, 0x6e, 0xa0, 0xff, 0x32, 0xb9, 0x4c, + 0x60, 0x7c, 0x58, 0xd1, 0x73, 0xf2, 0xe9, 0xc1, 0x6a, 0xd2, 0x20, 0x08, 0x83, 0x5a, 0x7b, 0xa5, 0x5f, 0x03, 0xa8, 0x5c, 0xc7, 0xf5, + 0xeb, 0xfd, 0x5a, 0xbf, 0xb6, 0xd2, 0xa7, 0x01, 0x68, 0x34, 0x82, 0x95, 0xa6, 0xb6, 0xd8, 0x3a, 0xeb, 0x95, 0x6e, 0x90, 0x61, 0x0d, + 0x50, 0xfa, 0xd5, 0xa1, 0xbb, 0x57, 0xef, 0x55, 0xcb, 0x16, 0xde, 0xd0, 0x5f, 0x5d, 0xb3, 0xb9, 0x1d, 0xaa, 0x8f, 0x85, 0xd7, 0xa0, + 0x54, 0x7f, 0xb0, 0x86, 0x1f, 0x0c, 0xba, 0xe0, 0x45, 0x0b, 0xaf, 0x41, 0x29, 0x3e, 0x5c, 0xc3, 0x87, 0x9d, 0x66, 0xa7, 0x67, 0xeb, + 0xd7, 0xa0, 0x14, 0x5f, 0x5b, 0xc3, 0xd7, 0x4b, 0xed, 0x5e, 0x50, 0xb7, 0xf4, 0x6b, 0x50, 0x44, 0x49, 0x72, 0xb8, 0x86, 0x2e, 0x85, + 0xb5, 0x6a, 0x77, 0xb9, 0xda, 0x15, 0x64, 0xc2, 0xe8, 0x8e, 0x13, 0xde, 0x0c, 0x83, 0x41, 0xbd, 0x92, 0x29, 0xcf, 0x51, 0x90, 0x0d, + 0xab, 0xec, 0x52, 0x53, 0x4c, 0x58, 0x22, 0x37, 0xe5, 0x5a, 0x8c, 0xee, 0x32, 0x3e, 0x00, 0x80, 0x02, 0x52, 0x24, 0x49, 0xe2, 0xc9, + 0xc5, 0x0c, 0x4f, 0xd0, 0x08, 0xb2, 0xb8, 0x8b, 0x28, 0x39, 0xe0, 0xc4, 0xdb, 0x25, 0xd3, 0x08, 0x12, 0x6f, 0x86, 0x12, 0x26, 0x60, + 0xb8, 0x54, 0x29, 0x0d, 0x4a, 0x55, 0xf8, 0xaf, 0x3e, 0x81, 0xfe, 0xa6, 0x23, 0x8a, 0xb6, 0x30, 0x32, 0xa4, 0x95, 0x5d, 0x60, 0x89, + 0x58, 0x1b, 0x52, 0xf6, 0x78, 0x62, 0xc4, 0xc9, 0x4c, 0xb6, 0xfc, 0x2b, 0xa0, 0xd5, 0x37, 0x20, 0x2f, 0x9e, 0x3d, 0x7b, 0xfe, 0xf0, + 0xe9, 0xf3, 0x87, 0xbf, 0x3d, 0x7f, 0xf4, 0xe8, 0xf9, 0xc3, 0x5f, 0xb2, 0xb9, 0xb5, 0x2a, 0x4b, 0x6e, 0x07, 0x25, 0x53, 0x53, 0xee, + 0xd5, 0x8f, 0x5f, 0xff, 0xfd, 0xfd, 0x17, 0xde, 0x5f, 0xbf, 0xfe, 0xf0, 0xea, 0xf1, 0x37, 0xe9, 0xd4, 0x27, 0xf1, 0xc2, 0xc4, 0xbf, + 0xfc, 0xf9, 0xcb, 0x97, 0xbf, 0xff, 0xf1, 0x3a, 0xf5, 0xb0, 0xe2, 0xdc, 0x15, 0x2f, 0xbe, 0x7d, 0xf2, 0xf2, 0xe9, 0x93, 0x17, 0xdf, + 0x7d, 0xf5, 0xe7, 0x4f, 0x8f, 0x1d, 0xda, 0xdb, 0x1c, 0x1d, 0x98, 0xf0, 0x21, 0x89, 0xb1, 0xf0, 0xae, 0xe1, 0x63, 0xef, 0x26, 0x8b, + 0x61, 0x81, 0x0e, 0xfb, 0xf1, 0x01, 0x3f, 0x9d, 0xc4, 0x30, 0x42, 0xc4, 0x92, 0x40, 0x11, 0xe8, 0x76, 0xa8, 0xee, 0xcb, 0xc8, 0x02, + 0x5e, 0x5b, 0x20, 0xea, 0xc2, 0x75, 0xb0, 0xed, 0xc2, 0xdb, 0x1c, 0x58, 0xc6, 0x05, 0xbc, 0x3c, 0xbf, 0x6b, 0xd9, 0xba, 0x1f, 0xf1, + 0xb9, 0x24, 0x8e, 0x99, 0xaf, 0x46, 0xb1, 0x05, 0xdc, 0x63, 0x8c, 0x76, 0x18, 0x77, 0x3a, 0xe0, 0xaa, 0x9a, 0xcb, 0xf0, 0xf0, 0x70, + 0x9e, 0x4c, 0xdd, 0x93, 0xf3, 0xb9, 0x89, 0xbb, 0x89, 0xd0, 0x91, 0x6b, 0xee, 0x2e, 0x4a, 0xac, 0x00, 0xf7, 0xe7, 0x33, 0xa0, 0x57, + 0xe2, 0x52, 0xd9, 0x8d, 0xb0, 0x65, 0xe6, 0x0d, 0x8a, 0x12, 0x89, 0xa6, 0x38, 0xc1, 0xd2, 0x53, 0xbf, 0xb1, 0x43, 0x8c, 0x1d, 0xab, + 0xbb, 0x43, 0x88, 0xe5, 0xd7, 0x3d, 0x32, 0xe2, 0x4c, 0xb0, 0x89, 0xf4, 0xee, 0x10, 0xaf, 0x83, 0x88, 0xd3, 0x25, 0x43, 0x72, 0x60, + 0x25, 0x52, 0x2e, 0xb4, 0x43, 0x62, 0x88, 0xcb, 0xc2, 0x65, 0x20, 0x84, 0xda, 0xf2, 0xcd, 0xde, 0x6d, 0xaf, 0xc3, 0xa8, 0x6b, 0xd5, + 0x3d, 0x7c, 0x64, 0x23, 0x61, 0x5b, 0x20, 0xea, 0x30, 0x7e, 0x88, 0xa9, 0xe5, 0xc6, 0xcb, 0x68, 0x2e, 0x51, 0xec, 0x52, 0x39, 0x44, + 0x31, 0x35, 0x1d, 0xbe, 0x8b, 0x64, 0xe4, 0x32, 0x72, 0x7f, 0xc1, 0x47, 0x26, 0xae, 0x2f, 0x24, 0x44, 0x7a, 0x8a, 0x29, 0xf3, 0xfa, + 0x63, 0x2c, 0x84, 0x4b, 0xe6, 0x3a, 0x87, 0xf5, 0x1a, 0x41, 0xbf, 0x0a, 0x0c, 0xe3, 0x0e, 0xfb, 0x1e, 0x5d, 0xc4, 0x36, 0x92, 0x4b, + 0x72, 0xe8, 0xd2, 0xb9, 0x8b, 0x18, 0x33, 0x91, 0x3d, 0x76, 0xd8, 0x8d, 0x50, 0x3c, 0x73, 0xda, 0x4c, 0x92, 0xc8, 0xc4, 0x7e, 0x26, + 0x0e, 0x21, 0x45, 0x91, 0x77, 0x83, 0x49, 0x17, 0x7c, 0x8f, 0xd9, 0x3b, 0x44, 0x3d, 0x43, 0x1c, 0x50, 0xb2, 0x31, 0xdc, 0xb7, 0x09, + 0xb6, 0xc2, 0xfd, 0x66, 0x22, 0xb8, 0x05, 0xe4, 0x6a, 0x9a, 0x94, 0x27, 0x88, 0xfa, 0x65, 0xce, 0x1d, 0xb1, 0xbc, 0x8c, 0x99, 0xbd, + 0x1f, 0x17, 0x74, 0x82, 0xb0, 0x8b, 0x65, 0xda, 0x3c, 0xb6, 0xd8, 0xb5, 0xcd, 0x89, 0x33, 0x3b, 0x3a, 0xf3, 0xa9, 0x95, 0xda, 0xbb, + 0x18, 0x53, 0x74, 0x8c, 0xc6, 0x18, 0x7b, 0xb7, 0x3e, 0x73, 0x58, 0xd0, 0x61, 0x33, 0xcb, 0xe7, 0xb9, 0xd1, 0x57, 0x22, 0x60, 0x95, + 0x1d, 0xec, 0x4a, 0xac, 0x2b, 0xc8, 0xce, 0x55, 0xf5, 0x9c, 0x60, 0x01, 0x65, 0x92, 0xaa, 0x6b, 0xd6, 0x29, 0x72, 0x97, 0x08, 0x2b, + 0x65, 0xf7, 0xf1, 0x94, 0x6d, 0xb0, 0x67, 0x6f, 0x71, 0x82, 0x78, 0x16, 0x28, 0x89, 0x11, 0xdf, 0xa4, 0xf9, 0x1a, 0x44, 0xdd, 0x4a, + 0x5d, 0x38, 0xe5, 0x9c, 0x54, 0x7a, 0x9d, 0x8e, 0x0e, 0x4d, 0xe0, 0x35, 0x02, 0xe5, 0x1f, 0xe4, 0x8b, 0xd3, 0x29, 0xd7, 0x05, 0xe8, + 0x30, 0x92, 0xbb, 0xbf, 0x49, 0xeb, 0x8d, 0x08, 0x59, 0x67, 0x97, 0x7a, 0x16, 0xee, 0x7c, 0x5d, 0x70, 0x2b, 0x7e, 0x6f, 0xb3, 0xc7, + 0x60, 0x5f, 0xde, 0x3d, 0xed, 0xbe, 0x04, 0x19, 0x7c, 0x6a, 0x19, 0x20, 0xf6, 0xb7, 0xf6, 0xcd, 0x10, 0x51, 0x6b, 0x82, 0x3c, 0x61, + 0x86, 0x08, 0x0a, 0x0c, 0x17, 0xdd, 0x82, 0x88, 0x15, 0xfe, 0x5c, 0x44, 0x9d, 0xab, 0x5a, 0x6c, 0xee, 0x94, 0x9b, 0xd8, 0x9b, 0x36, + 0x0f, 0x03, 0x14, 0x46, 0x56, 0xbd, 0x13, 0x93, 0xe4, 0x8d, 0xc5, 0xcf, 0x89, 0xb2, 0x27, 0xfc, 0x77, 0xca, 0x1e, 0x77, 0x01, 0x73, + 0x06, 0x05, 0x8f, 0x5b, 0xf1, 0xfb, 0x94, 0x3a, 0x9b, 0x28, 0x65, 0xe7, 0x44, 0x81, 0xb3, 0x09, 0xf7, 0x1f, 0x2c, 0x6b, 0x7a, 0x68, + 0x9e, 0xdc, 0xc0, 0x70, 0x92, 0xac, 0x73, 0xd6, 0x79, 0x55, 0x73, 0x5e, 0xd5, 0xf8, 0xff, 0xfb, 0xaa, 0x66, 0xd3, 0x5e, 0x3e, 0xaf, + 0x65, 0xce, 0x6b, 0x99, 0xf3, 0x5a, 0xc6, 0xf5, 0xf6, 0xf5, 0x41, 0x6a, 0x99, 0xbc, 0x7c, 0x81, 0xca, 0x26, 0xef, 0xf2, 0xe8, 0x9e, + 0x4f, 0xbc, 0xb1, 0xe5, 0x33, 0x21, 0x94, 0xee, 0xcb, 0x05, 0xc5, 0xbb, 0x42, 0x77, 0x7d, 0x04, 0xbc, 0xd1, 0x8c, 0x07, 0x30, 0xa8, + 0xdb, 0x51, 0xba, 0x27, 0xb9, 0x6a, 0x01, 0xce, 0x22, 0xf8, 0x9a, 0x35, 0x98, 0x2c, 0xdc, 0x94, 0x23, 0x2d, 0xe3, 0x71, 0x26, 0x3f, + 0x27, 0x32, 0xda, 0x8f, 0xd0, 0x0c, 0x5a, 0x43, 0x65, 0xdd, 0xc0, 0x9c, 0x8a, 0x4c, 0xf5, 0x54, 0x78, 0x33, 0x26, 0xa0, 0x63, 0xa4, + 0x87, 0x75, 0x2b, 0x15, 0x9f, 0xd0, 0xad, 0xfb, 0x4e, 0xf3, 0x78, 0x8f, 0x8d, 0xd3, 0x4e, 0x67, 0xb9, 0xac, 0xba, 0x9a, 0xa9, 0x0b, + 0x05, 0x92, 0xf9, 0x78, 0x29, 0x5c, 0x8d, 0x43, 0x97, 0x4a, 0xa6, 0xe8, 0x5a, 0x3d, 0xef, 0xde, 0xad, 0xd4, 0xeb, 0x7e, 0xe8, 0x54, + 0x77, 0x59, 0x97, 0x06, 0x28, 0xd9, 0xd3, 0x18, 0x61, 0x4c, 0x66, 0x1b, 0x51, 0x75, 0x18, 0x51, 0x5f, 0x0e, 0x42, 0x14, 0x5e, 0x67, + 0x84, 0x5e, 0xd9, 0x99, 0x58, 0xd1, 0x74, 0x58, 0xd1, 0x50, 0xea, 0x97, 0xa1, 0x5a, 0x46, 0x71, 0xe5, 0x0a, 0x30, 0x6d, 0x15, 0x15, + 0x78, 0xe5, 0xf6, 0xe0, 0x45, 0xbd, 0xe5, 0x87, 0x41, 0xda, 0x41, 0x86, 0x66, 0x1c, 0x94, 0xe7, 0x63, 0x15, 0xa7, 0xb4, 0x99, 0xbc, + 0x8c, 0xae, 0x0a, 0xce, 0x99, 0x46, 0x7a, 0x93, 0x33, 0xa9, 0x99, 0x01, 0x50, 0x62, 0x2f, 0x33, 0x20, 0x8f, 0x74, 0x53, 0xd9, 0xba, + 0x71, 0x79, 0x6a, 0x75, 0x69, 0xaa, 0xbd, 0x45, 0xa4, 0x2d, 0x23, 0x8c, 0x74, 0xb3, 0x8d, 0x30, 0xd2, 0x30, 0x82, 0x17, 0xe1, 0x2c, + 0x3b, 0xcd, 0x96, 0xfb, 0x59, 0xc6, 0xba, 0x99, 0x87, 0xd4, 0x32, 0x4f, 0xb9, 0x62, 0xb9, 0x1b, 0x72, 0x33, 0xea, 0x8d, 0x0f, 0x11, + 0x6b, 0x45, 0x22, 0x27, 0xb8, 0x81, 0x26, 0x26, 0x53, 0xd0, 0xc4, 0x3b, 0x6e, 0xf9, 0xb5, 0x6a, 0x08, 0xb7, 0x2a, 0x23, 0x34, 0x6b, + 0xf9, 0x13, 0xe8, 0x18, 0xc3, 0xd7, 0x78, 0x06, 0xb9, 0x23, 0xd4, 0x5b, 0x17, 0xa2, 0x53, 0xb8, 0x76, 0x19, 0x49, 0x9e, 0x6e, 0xf8, + 0x77, 0x61, 0x96, 0x19, 0x17, 0xb2, 0x87, 0x44, 0x94, 0x3a, 0x5c, 0x93, 0x4e, 0xca, 0x06, 0x31, 0x91, 0x98, 0x7b, 0x94, 0xc4, 0x2d, + 0x5f, 0x2d, 0x7f, 0x95, 0x0d, 0x34, 0xd1, 0x1c, 0xa2, 0x6d, 0x2b, 0x57, 0x80, 0x10, 0x3e, 0x5a, 0xe3, 0x9a, 0x40, 0x2b, 0x1f, 0x9b, + 0x71, 0x10, 0x74, 0x3b, 0xc8, 0x78, 0x32, 0xc1, 0x23, 0x69, 0x86, 0xdd, 0x18, 0x51, 0x9e, 0x4e, 0x1f, 0x81, 0xe1, 0x53, 0xae, 0x70, + 0xfe, 0xaa, 0xc5, 0xdf, 0x1d, 0xac, 0x24, 0xd9, 0x1c, 0xc2, 0xbd, 0x1f, 0x8d, 0x8f, 0xbd, 0x03, 0x3a, 0xe7, 0x37, 0x11, 0xa4, 0x58, + 0x58, 0x2f, 0x2b, 0x07, 0x8e, 0x89, 0x80, 0x8b, 0x83, 0x72, 0xea, 0xcd, 0x31, 0x81, 0x9b, 0xb0, 0x15, 0x91, 0xe5, 0xf9, 0x77, 0xe2, + 0x60, 0xca, 0x68, 0xd7, 0xbc, 0x8a, 0xd2, 0x39, 0x94, 0x8e, 0x23, 0x3a, 0x8b, 0x50, 0x76, 0xa2, 0x98, 0x64, 0x9e, 0xc2, 0x35, 0x89, + 0xae, 0xcc, 0xd1, 0x4f, 0x2b, 0x1f, 0x18, 0x4f, 0xd9, 0x9a, 0xc1, 0xa1, 0xeb, 0x2e, 0x3c, 0x98, 0xaa, 0x03, 0xf6, 0xbd, 0x4f, 0xdd, + 0x37, 0x1f, 0xd5, 0xca, 0x73, 0x06, 0x69, 0xe6, 0x67, 0xa6, 0xc5, 0x2a, 0xea, 0xd4, 0x74, 0x93, 0xe9, 0x87, 0x3b, 0xe4, 0x0d, 0xab, + 0xf2, 0x43, 0xd4, 0xb2, 0x2a, 0xa5, 0x6e, 0xfd, 0x4e, 0x2d, 0x72, 0xae, 0x6b, 0x2e, 0xb9, 0x0e, 0x12, 0xd5, 0x79, 0x4a, 0xbc, 0xe1, + 0xd4, 0x7d, 0x8b, 0x03, 0xc1, 0x30, 0x2d, 0x9f, 0xcc, 0x32, 0x4d, 0x59, 0xbc, 0x4e, 0xc3, 0x8a, 0xb3, 0xb3, 0x51, 0xdb, 0xb4, 0x33, + 0x2c, 0x08, 0x0c, 0x4f, 0xd4, 0x36, 0xf8, 0x6d, 0x75, 0x46, 0x38, 0x3d, 0xf1, 0xae, 0x27, 0x3f, 0xc8, 0x9d, 0xcc, 0x5a, 0x75, 0x40, + 0x2c, 0xeb, 0x4a, 0x9d, 0xf8, 0xfa, 0xca, 0xdc, 0xbc, 0xd5, 0x66, 0x07, 0x77, 0x81, 0x3c, 0x7a, 0x70, 0x7f, 0x38, 0xa7, 0x52, 0xe8, + 0x50, 0x42, 0x6f, 0x97, 0x23, 0x28, 0xfa, 0xd2, 0x1b, 0xc8, 0x94, 0x36, 0x60, 0x8b, 0xdc, 0x93, 0x59, 0x8d, 0x08, 0xdf, 0xbc, 0x39, + 0x27, 0x2d, 0xff, 0x7e, 0x29, 0x6c, 0x07, 0xdd, 0x4a, 0xd8, 0x2d, 0x94, 0x1a, 0x61, 0xbf, 0x10, 0x54, 0x83, 0x52, 0xa1, 0x11, 0xb6, + 0xab, 0x85, 0x76, 0x18, 0x56, 0xcb, 0xfd, 0xb0, 0x5c, 0xea, 0x75, 0x2a, 0x0f, 0xe0, 0x60, 0x91, 0x51, 0x5c, 0x0e, 0xd3, 0xeb, 0xfa, + 0x01, 0x5c, 0x61, 0xd0, 0x45, 0x76, 0x69, 0xaf, 0xc7, 0xd7, 0x2e, 0xee, 0xe3, 0xe5, 0x2d, 0xcd, 0x85, 0x11, 0x8b, 0x8b, 0x4c, 0x5f, + 0xcc, 0x17, 0xb5, 0xe1, 0xfa, 0xe2, 0xbe, 0x5c, 0xd9, 0x7c, 0x71, 0xef, 0x11, 0x20, 0x9d, 0xfb, 0xb5, 0xca, 0xa0, 0x59, 0x6d, 0x76, + 0x6a, 0x85, 0x66, 0xb5, 0x3d, 0x28, 0x04, 0xbd, 0x4e, 0xa3, 0xd0, 0xec, 0xd6, 0x3a, 0x85, 0x5e, 0xad, 0x5b, 0xef, 0x0d, 0x7a, 0xdd, + 0xb0, 0xd1, 0x1c, 0x3c, 0xf0, 0xbd, 0x23, 0x0d, 0x0e, 0xda, 0xd5, 0x6e, 0x50, 0xeb, 0x37, 0x0a, 0xb5, 0x72, 0xb7, 0x5b, 0x08, 0x6a, + 0x25, 0x65, 0x7e, 0xa3, 0x59, 0xa8, 0x07, 0x95, 0x4a, 0x3b, 0xa8, 0xb7, 0x1b, 0xfd, 0xa0, 0xfd, 0x20, 0x2b, 0x63, 0x60, 0xe5, 0x29, + 0x7d, 0x64, 0xbe, 0x00, 0xf7, 0x6a, 0xbb, 0xb6, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xff, 0x6b, 0x0c, 0xe9, 0xcd, 0x01, 0x00, 0x00, 0xb5, 0x03, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x73, 0x2f, 0x73, 0x68, 0x65, 0x65, 0x74, 0x31, + 0x2e, 0x78, 0x6d, 0x6c, 0x9c, 0x93, 0x4d, 0x8b, 0xdb, 0x30, 0x10, 0x86, 0xef, 0x85, 0xfe, 0x07, 0xa1, 0xbb, 0x2d, 0xdb, 0xf1, 0x3a, + 0x89, 0x89, 0xb3, 0x6c, 0x36, 0x0d, 0xdd, 0x43, 0xa1, 0xf4, 0xf3, 0x2c, 0xcb, 0x63, 0x5b, 0xc4, 0x92, 0x8c, 0xa4, 0x6c, 0x12, 0x4a, + 0xff, 0x7b, 0xc7, 0x0e, 0xf1, 0x16, 0x72, 0x09, 0x0b, 0x16, 0x68, 0xc6, 0x9a, 0x67, 0x66, 0xa4, 0x77, 0x56, 0x8f, 0x27, 0xd5, 0x91, + 0x57, 0xb0, 0x4e, 0x1a, 0x5d, 0xd0, 0x38, 0x8c, 0x28, 0x01, 0x2d, 0x4c, 0x25, 0x75, 0x53, 0xd0, 0x9f, 0x3f, 0x76, 0xc1, 0x82, 0x12, + 0xe7, 0xb9, 0xae, 0x78, 0x67, 0x34, 0x14, 0xf4, 0x0c, 0x8e, 0x3e, 0xae, 0x3f, 0x7e, 0x58, 0x1d, 0x8d, 0xdd, 0xbb, 0x16, 0xc0, 0x13, + 0x24, 0x68, 0x57, 0xd0, 0xd6, 0xfb, 0x3e, 0x67, 0xcc, 0x89, 0x16, 0x14, 0x77, 0xa1, 0xe9, 0x41, 0xe3, 0x9f, 0xda, 0x58, 0xc5, 0x3d, + 0x9a, 0xb6, 0x61, 0xae, 0xb7, 0xc0, 0xab, 0x31, 0x48, 0x75, 0x2c, 0x89, 0xa2, 0x8c, 0x29, 0x2e, 0x35, 0xbd, 0x10, 0x72, 0x7b, 0x0f, + 0xc3, 0xd4, 0xb5, 0x14, 0xb0, 0x35, 0xe2, 0xa0, 0x40, 0xfb, 0x0b, 0xc4, 0x42, 0xc7, 0x3d, 0xd6, 0xef, 0x5a, 0xd9, 0xbb, 0x2b, 0x4d, + 0x89, 0x7b, 0x70, 0x8a, 0xdb, 0xfd, 0xa1, 0x0f, 0x84, 0x51, 0x3d, 0x22, 0x4a, 0xd9, 0x49, 0x7f, 0x1e, 0xa1, 0x94, 0x28, 0x91, 0xbf, + 0x34, 0xda, 0x58, 0x5e, 0x76, 0xd8, 0xf7, 0x29, 0x4e, 0xb9, 0x20, 0x27, 0x8b, 0x5f, 0x82, 0x6b, 0x76, 0x4d, 0x33, 0xfa, 0x6f, 0x32, + 0x29, 0x29, 0xac, 0x71, 0xa6, 0xf6, 0x21, 0x92, 0xd9, 0xa5, 0xe6, 0xdb, 0xf6, 0x97, 0x6c, 0xc9, 0xb8, 0x98, 0x48, 0xb7, 0xfd, 0xdf, + 0x85, 0x89, 0x53, 0x66, 0xe1, 0x55, 0x0e, 0x0f, 0xf8, 0x86, 0x4a, 0xde, 0x57, 0x52, 0xfc, 0x30, 0xb1, 0x92, 0x37, 0xd8, 0xec, 0x9d, + 0xb0, 0x6c, 0x82, 0x0d, 0xd7, 0x65, 0xf3, 0x83, 0xac, 0x0a, 0xfa, 0x27, 0x4a, 0x97, 0xbb, 0x4d, 0xbc, 0x49, 0x83, 0xe4, 0x29, 0x8b, + 0x83, 0x34, 0x4b, 0x67, 0xc1, 0x62, 0x11, 0x67, 0xc1, 0x62, 0x33, 0xcf, 0x3e, 0x3d, 0xcf, 0xb2, 0xdd, 0x36, 0xdd, 0xfc, 0xa5, 0xeb, + 0x55, 0x25, 0xf1, 0x85, 0x87, 0xae, 0x88, 0x85, 0xba, 0xa0, 0x4f, 0x31, 0x65, 0xeb, 0xd5, 0x28, 0x9e, 0x5f, 0x12, 0x8e, 0xee, 0xbf, + 0x3d, 0xf1, 0xbc, 0xfc, 0x0e, 0x1d, 0x08, 0x0f, 0x98, 0x20, 0xa6, 0x64, 0xd0, 0x66, 0x69, 0xcc, 0x7e, 0x38, 0xf8, 0x82, 0xae, 0x68, + 0x08, 0x65, 0x37, 0xb1, 0xbb, 0x51, 0x9b, 0x5f, 0x2d, 0x29, 0xb9, 0x83, 0x67, 0xd3, 0xfd, 0x96, 0x95, 0x6f, 0x11, 0x80, 0x33, 0x50, + 0x41, 0xcd, 0x0f, 0x9d, 0xff, 0x66, 0x8e, 0x9f, 0x41, 0x36, 0xad, 0x47, 0x6f, 0x86, 0x3d, 0x0c, 0x22, 0xc8, 0xab, 0xf3, 0x16, 0x9c, + 0x40, 0xf5, 0x21, 0x38, 0x4c, 0xa6, 0xaa, 0xb6, 0xdc, 0x73, 0x4c, 0xd3, 0xf3, 0x06, 0xbe, 0x70, 0xdb, 0x48, 0xed, 0x48, 0x07, 0xf5, + 0x78, 0x68, 0x4e, 0x89, 0xbd, 0x50, 0xa2, 0x10, 0xf7, 0xde, 0xf4, 0x43, 0xe8, 0xfc, 0x81, 0x92, 0xd2, 0x78, 0x6f, 0xd4, 0xd5, 0x6a, + 0x71, 0x3c, 0x00, 0x65, 0x10, 0x85, 0x78, 0x61, 0xb5, 0x31, 0xfe, 0x6a, 0x0c, 0xe5, 0x4f, 0x03, 0xb7, 0xfe, 0x07, 0x00, 0x00, 0xff, + 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc2, 0x5e, 0x59, 0x08, 0x90, + 0x01, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00, 0x10, 0x00, 0x08, 0x01, 0x64, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x2f, 0x61, 0x70, + 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x20, 0xa2, 0x04, 0x01, 0x28, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x92, 0x4d, 0x6f, 0xdb, 0x30, 0x0c, 0x86, 0xef, 0x03, 0xfa, 0x1f, 0x0c, 0xdd, 0x1b, 0x39, 0x6d, + 0x51, 0x0c, 0x81, 0xac, 0x62, 0x48, 0x57, 0xf4, 0xb0, 0x62, 0x01, 0x92, 0x76, 0x67, 0x4e, 0xa6, 0x63, 0xa1, 0xb2, 0x24, 0x88, 0xac, + 0x91, 0xec, 0xd7, 0x4f, 0xb6, 0xd1, 0xd4, 0xd9, 0x76, 0xda, 0x8d, 0x1f, 0x2f, 0x5e, 0x3e, 0xa2, 0xa8, 0xee, 0x0e, 0x9d, 0x2b, 0x7a, + 0x4c, 0x64, 0x83, 0xaf, 0xc4, 0x72, 0x51, 0x8a, 0x02, 0xbd, 0x09, 0xb5, 0xf5, 0xfb, 0x4a, 0x3c, 0xef, 0x1e, 0x2e, 0x3f, 0x8b, 0x82, + 0x18, 0x7c, 0x0d, 0x2e, 0x78, 0xac, 0xc4, 0x11, 0x49, 0xdc, 0xe9, 0x8b, 0x4f, 0x6a, 0x93, 0x42, 0xc4, 0xc4, 0x16, 0xa9, 0xc8, 0x16, + 0x9e, 0x2a, 0xd1, 0x32, 0xc7, 0x95, 0x94, 0x64, 0x5a, 0xec, 0x80, 0x16, 0xb9, 0xed, 0x73, 0xa7, 0x09, 0xa9, 0x03, 0xce, 0x69, 0xda, + 0xcb, 0xd0, 0x34, 0xd6, 0xe0, 0x7d, 0x30, 0x6f, 0x1d, 0x7a, 0x96, 0x57, 0x65, 0x79, 0x2b, 0xf1, 0xc0, 0xe8, 0x6b, 0xac, 0x2f, 0xe3, + 0xc9, 0x50, 0x4c, 0x8e, 0xab, 0x9e, 0xff, 0xd7, 0xb4, 0x0e, 0x66, 0xe0, 0xa3, 0x97, 0xdd, 0x31, 0x66, 0x60, 0xad, 0xbe, 0xc4, 0xe8, + 0xac, 0x01, 0xce, 0xaf, 0xd4, 0x4f, 0xd6, 0xa4, 0x40, 0xa1, 0xe1, 0xe2, 0x09, 0x8c, 0xf5, 0x1c, 0xa8, 0x2d, 0xbe, 0x1e, 0x0c, 0x3a, + 0x25, 0xe7, 0x32, 0x95, 0x39, 0xb7, 0x68, 0xde, 0x92, 0xe5, 0xa3, 0x2e, 0x95, 0x9c, 0xa7, 0x6a, 0x6b, 0xc0, 0xe1, 0x3a, 0x8f, 0xd0, + 0x0d, 0x38, 0x42, 0x25, 0x3f, 0x0a, 0xea, 0x11, 0x61, 0x58, 0xdf, 0x06, 0x6c, 0x22, 0xad, 0x7a, 0x5e, 0xf5, 0x68, 0x38, 0xa4, 0x82, + 0xec, 0xaf, 0xbc, 0xc0, 0x2b, 0x51, 0xfc, 0x04, 0xc2, 0x01, 0xac, 0x12, 0x3d, 0x24, 0x0b, 0x9e, 0x33, 0xe0, 0x20, 0x9b, 0x92, 0x31, + 0x76, 0x91, 0x38, 0xe9, 0x1f, 0x21, 0xbd, 0x52, 0x8b, 0xc8, 0xa4, 0x64, 0x16, 0x4c, 0xc5, 0x31, 0x9c, 0x6b, 0xe7, 0xb1, 0xbd, 0xd1, + 0xcb, 0x51, 0x90, 0x83, 0x73, 0xe1, 0x60, 0x30, 0x81, 0xe4, 0xc6, 0x39, 0xe2, 0xce, 0xb2, 0x43, 0xfa, 0xde, 0x6c, 0x20, 0xf1, 0x3f, + 0x88, 0x97, 0x73, 0xe2, 0x91, 0x61, 0xe2, 0x9d, 0x70, 0xb6, 0x03, 0xdf, 0x34, 0x73, 0xce, 0x37, 0x3e, 0x39, 0x4f, 0xfa, 0xc3, 0x7b, + 0x1d, 0xba, 0x08, 0xfe, 0x98, 0x1b, 0xa7, 0xe8, 0x9b, 0xf5, 0xaf, 0xf4, 0x1c, 0x77, 0xe1, 0x1e, 0x18, 0xdf, 0xd7, 0x79, 0x5e, 0x54, + 0xdb, 0x16, 0x12, 0xd6, 0xf9, 0x07, 0x4e, 0xeb, 0x3e, 0x15, 0xd4, 0x63, 0xde, 0x64, 0x72, 0x83, 0xc9, 0xba, 0x05, 0xbf, 0xc7, 0xfa, + 0x5d, 0xf3, 0x77, 0x63, 0x38, 0x83, 0x97, 0xe9, 0xd6, 0xf5, 0xf2, 0x76, 0x51, 0x5e, 0x97, 0xf9, 0x5f, 0x67, 0x35, 0x25, 0x3f, 0xae, + 0x5a, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, + 0x00, 0xbd, 0xde, 0xac, 0xb8, 0x45, 0x01, 0x00, 0x00, 0x6f, 0x02, 0x00, 0x00, 0x11, 0x00, 0x08, 0x01, 0x64, 0x6f, 0x63, 0x50, 0x72, + 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x78, 0x6d, 0x6c, 0x20, 0xa2, 0x04, 0x01, 0x28, 0xa0, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x92, 0x5f, 0x4b, 0xc3, 0x30, 0x14, 0xc5, 0xdf, 0x05, + 0xbf, 0x43, 0xc9, 0x7b, 0x9b, 0x66, 0x1b, 0x63, 0x96, 0xb6, 0x83, 0x29, 0x7b, 0xd1, 0x81, 0xe0, 0x44, 0xf1, 0x2d, 0x24, 0x77, 0x5b, + 0xb0, 0xf9, 0x43, 0x12, 0xd7, 0xed, 0xdb, 0x9b, 0xb6, 0x5b, 0xad, 0xd4, 0x17, 0x1f, 0x93, 0x73, 0xee, 0x2f, 0xe7, 0x5c, 0x92, 0x2f, + 0x4f, 0xb2, 0x8a, 0x8e, 0x60, 0x9d, 0xd0, 0xaa, 0x40, 0x24, 0x49, 0x51, 0x04, 0x8a, 0x69, 0x2e, 0xd4, 0xbe, 0x40, 0xaf, 0xdb, 0x75, + 0xbc, 0x40, 0x91, 0xf3, 0x54, 0x71, 0x5a, 0x69, 0x05, 0x05, 0x3a, 0x83, 0x43, 0xcb, 0xf2, 0xf6, 0x26, 0x67, 0x26, 0x63, 0xda, 0xc2, + 0xb3, 0xd5, 0x06, 0xac, 0x17, 0xe0, 0xa2, 0x40, 0x52, 0x2e, 0x63, 0xa6, 0x40, 0x07, 0xef, 0x4d, 0x86, 0xb1, 0x63, 0x07, 0x90, 0xd4, + 0x25, 0xc1, 0xa1, 0x82, 0xb8, 0xd3, 0x56, 0x52, 0x1f, 0x8e, 0x76, 0x8f, 0x0d, 0x65, 0x9f, 0x74, 0x0f, 0x78, 0x92, 0xa6, 0x73, 0x2c, + 0xc1, 0x53, 0x4e, 0x3d, 0xc5, 0x0d, 0x30, 0x36, 0x3d, 0x11, 0x5d, 0x90, 0x9c, 0xf5, 0x48, 0xf3, 0x65, 0xab, 0x16, 0xc0, 0x19, 0x86, + 0x0a, 0x24, 0x28, 0xef, 0x30, 0x49, 0x08, 0xfe, 0xf1, 0x7a, 0xb0, 0xd2, 0xfd, 0x39, 0xd0, 0x2a, 0x03, 0xa7, 0x14, 0xfe, 0x6c, 0x42, + 0xa7, 0x4b, 0xdc, 0x21, 0x9b, 0xb3, 0x4e, 0xec, 0xdd, 0x27, 0x27, 0x7a, 0x63, 0x5d, 0xd7, 0x49, 0x3d, 0x6d, 0x63, 0x84, 0xfc, 0x04, + 0xbf, 0x6f, 0x9e, 0x5e, 0xda, 0xaa, 0xb1, 0x50, 0xcd, 0xae, 0x18, 0xa0, 0x32, 0xe7, 0x2c, 0x63, 0x16, 0xa8, 0xd7, 0xb6, 0x7c, 0x04, + 0xa5, 0xc0, 0x1f, 0xa2, 0x15, 0xad, 0x5c, 0x05, 0xc7, 0x1c, 0x0f, 0xb4, 0x66, 0x8f, 0x15, 0x75, 0x7e, 0x13, 0x56, 0xbe, 0x13, 0xc0, + 0x57, 0xe7, 0xb1, 0x7d, 0x6c, 0x09, 0xf4, 0xb6, 0x4c, 0xf7, 0x04, 0xf0, 0x28, 0xc4, 0xcb, 0xba, 0x32, 0x57, 0xe5, 0x6d, 0x7a, 0xff, + 0xb0, 0x5d, 0xa3, 0x72, 0x92, 0x92, 0xbb, 0x38, 0x5d, 0xc4, 0x64, 0xbe, 0x4d, 0xd3, 0x6c, 0x3a, 0xcb, 0xc8, 0xec, 0xa3, 0x49, 0xf0, + 0x6b, 0xbe, 0x89, 0xdb, 0x5d, 0xc8, 0x4b, 0x8e, 0xff, 0x10, 0x27, 0xf3, 0x01, 0xf1, 0x0a, 0x28, 0x73, 0x3c, 0xfa, 0x22, 0xe5, 0x37, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x9a, 0xab, 0x0f, 0x4f, 0x5e, + 0xff, 0xec, 0xaa, 0x93, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x35, 0x8d, 0x41, 0x0a, 0xc2, 0x30, 0x10, 0x45, 0xf7, + 0x82, 0x77, 0x08, 0xb3, 0xd7, 0xa9, 0x2e, 0x44, 0x24, 0x49, 0x17, 0x82, 0x27, 0xd0, 0x03, 0x84, 0x76, 0x6c, 0x03, 0xcd, 0xa4, 0x66, + 0xa6, 0xa2, 0xb7, 0x37, 0x5d, 0xb8, 0xf8, 0xf0, 0x1f, 0x9f, 0xcf, 0xb3, 0xed, 0x27, 0x4d, 0xe6, 0x4d, 0x45, 0x62, 0x66, 0x07, 0x87, + 0x7d, 0x03, 0x86, 0xb8, 0xcb, 0x7d, 0xe4, 0xc1, 0xc1, 0xe3, 0x7e, 0xdb, 0x9d, 0xc1, 0x88, 0x06, 0xee, 0xc3, 0x94, 0x99, 0x1c, 0x7c, + 0x49, 0xa0, 0xf5, 0xdb, 0x8d, 0x15, 0x51, 0x53, 0xbf, 0x2c, 0x0e, 0x46, 0xd5, 0xf9, 0x82, 0x28, 0xdd, 0x48, 0x29, 0xc8, 0x3e, 0xcf, + 0xc4, 0x75, 0x79, 0xe6, 0x92, 0x82, 0x56, 0x2c, 0x03, 0xca, 0x5c, 0x28, 0xf4, 0x32, 0x12, 0x69, 0x9a, 0xf0, 0xd8, 0x34, 0x27, 0x4c, + 0x21, 0x32, 0x98, 0x2e, 0x2f, 0xac, 0xd5, 0x0b, 0x66, 0xe1, 0xf8, 0x5a, 0xe8, 0xfa, 0x67, 0x6f, 0x25, 0x7a, 0xab, 0xde, 0xe2, 0x9a, + 0xb5, 0x63, 0x35, 0x56, 0xf1, 0x0f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x99, 0x64, 0xef, 0x50, 0xd9, 0x19, + 0xd7, 0x73, 0x3f, 0x01, 0x00, 0x00, 0x7e, 0x04, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x5f, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5d, 0x2e, 0x78, 0x6d, 0x6c, 0xad, 0x93, 0xcd, 0x4e, 0xc3, 0x30, 0x10, 0x84, 0xef, 0x3c, 0x45, + 0xe4, 0x2b, 0x4a, 0xdc, 0x72, 0x40, 0x08, 0x35, 0xed, 0x81, 0x9f, 0x23, 0x54, 0xa2, 0x3c, 0x80, 0xb1, 0x37, 0x8d, 0xd5, 0xf8, 0x47, + 0xbb, 0x6e, 0x69, 0xdf, 0x9e, 0x4d, 0x52, 0x21, 0x40, 0x55, 0x03, 0xb4, 0x97, 0x58, 0xf1, 0xce, 0xcc, 0x37, 0x3e, 0xec, 0x64, 0xb6, + 0x75, 0x4d, 0xb6, 0x01, 0x24, 0x1b, 0x7c, 0x29, 0xc6, 0xc5, 0x48, 0x64, 0xe0, 0x75, 0x30, 0xd6, 0x2f, 0x4b, 0xf1, 0xba, 0x78, 0xcc, + 0x6f, 0xc4, 0x6c, 0x3a, 0x59, 0xec, 0x22, 0x50, 0xc6, 0x52, 0x4f, 0xa5, 0xa8, 0x53, 0x8a, 0xb7, 0x52, 0x92, 0xae, 0xc1, 0x29, 0x2a, + 0x42, 0x04, 0xcf, 0x93, 0x2a, 0xa0, 0x53, 0x89, 0x7f, 0x71, 0x29, 0xa3, 0xd2, 0x2b, 0xb5, 0x04, 0x79, 0x35, 0x1a, 0x5d, 0x4b, 0x1d, + 0x7c, 0x02, 0x9f, 0xf2, 0xd4, 0x66, 0x88, 0xe9, 0xe4, 0x1e, 0x2a, 0xb5, 0x6e, 0x52, 0xf6, 0xb0, 0xe5, 0xeb, 0x1e, 0x8b, 0xd0, 0x90, + 0xc8, 0xee, 0x7a, 0x61, 0xcb, 0x2a, 0x85, 0x8a, 0xb1, 0xb1, 0x5a, 0x25, 0x9e, 0xcb, 0x8d, 0x37, 0x3f, 0x28, 0xf9, 0x9e, 0x50, 0xb0, + 0xb3, 0xd3, 0x50, 0x6d, 0x23, 0x5d, 0xb2, 0x40, 0xc8, 0x83, 0x04, 0x9e, 0x1c, 0x01, 0xec, 0x7d, 0xcf, 0x1b, 0x40, 0xb4, 0x06, 0xb2, + 0xb9, 0xc2, 0xf4, 0xa4, 0x1c, 0xab, 0xe4, 0xb6, 0x91, 0xef, 0x01, 0x57, 0x6f, 0x21, 0xac, 0x0a, 0x96, 0xfd, 0xad, 0x65, 0xa8, 0x2a, + 0xab, 0xc1, 0x04, 0xbd, 0x76, 0x6c, 0x29, 0x28, 0x22, 0x28, 0x43, 0x35, 0x40, 0x72, 0x4d, 0xd1, 0x9d, 0x85, 0x53, 0xd6, 0x5f, 0x0e, + 0xf3, 0x3b, 0x31, 0xc9, 0xee, 0x18, 0x9f, 0xb9, 0xc8, 0x67, 0xfe, 0x40, 0x8f, 0x54, 0x83, 0x83, 0xfe, 0x7b, 0x7a, 0x85, 0x2e, 0x66, + 0x00, 0x48, 0x69, 0xd7, 0x00, 0x9d, 0x8c, 0xfa, 0xfe, 0xda, 0x3e, 0x74, 0x88, 0x5c, 0x2b, 0x04, 0xf3, 0x92, 0x90, 0xd7, 0xe0, 0xec, + 0x05, 0xbe, 0x64, 0x1f, 0xed, 0xc1, 0xfe, 0x39, 0x86, 0x48, 0x52, 0x07, 0x84, 0xb6, 0xc4, 0xff, 0x56, 0xa4, 0x75, 0xe7, 0x11, 0x79, + 0x8a, 0xc9, 0xc2, 0xef, 0x88, 0x1c, 0x7d, 0xf2, 0xab, 0xa1, 0xdd, 0x3e, 0x03, 0xe6, 0x00, 0x5b, 0xb6, 0x79, 0x34, 0xbd, 0xf8, 0x00, + 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xa7, 0x64, 0xef, 0x50, 0x59, 0xaf, 0xf9, 0x51, 0xdc, 0x00, 0x00, 0x00, + 0xa8, 0x02, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, + 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0xad, 0x92, 0xcd, 0x6a, 0xc3, 0x30, 0x10, 0x84, 0xef, 0x7d, + 0x0a, 0xb1, 0xf7, 0x5a, 0x76, 0x5a, 0x4a, 0x29, 0x51, 0x72, 0x29, 0x85, 0x5c, 0xdb, 0xf4, 0x01, 0x84, 0xbc, 0xb6, 0x4c, 0x6c, 0x49, + 0xec, 0x6e, 0x7f, 0xf2, 0xf6, 0x11, 0x0e, 0x24, 0x31, 0x84, 0x90, 0x83, 0x4f, 0x62, 0x46, 0xda, 0x99, 0x4f, 0xb0, 0xcb, 0xf5, 0xff, + 0xd0, 0xab, 0x5f, 0x24, 0xee, 0x62, 0x30, 0x50, 0x15, 0x25, 0x28, 0x0c, 0x2e, 0xd6, 0x5d, 0x68, 0x0d, 0x7c, 0x6f, 0x3f, 0x1e, 0x5f, + 0x61, 0xbd, 0x5a, 0x7e, 0x62, 0x6f, 0x25, 0xbf, 0x60, 0xdf, 0x25, 0x56, 0x79, 0x24, 0xb0, 0x01, 0x2f, 0x92, 0xde, 0xb4, 0x66, 0xe7, + 0x71, 0xb0, 0x5c, 0xc4, 0x84, 0x21, 0xdf, 0x34, 0x91, 0x06, 0x2b, 0x59, 0x52, 0xab, 0x93, 0x75, 0x3b, 0xdb, 0xa2, 0x5e, 0x94, 0xe5, + 0x8b, 0xa6, 0xcb, 0x0c, 0x98, 0x66, 0xaa, 0x4d, 0x6d, 0x80, 0x36, 0xf5, 0x33, 0xa8, 0xed, 0x3e, 0xe1, 0x3d, 0xd9, 0xb1, 0x69, 0x3a, + 0x87, 0xef, 0xd1, 0xfd, 0x0c, 0x18, 0xe4, 0x4a, 0x85, 0x66, 0x6f, 0x09, 0xeb, 0x2f, 0xa1, 0xfc, 0x17, 0xce, 0xc1, 0x96, 0x5a, 0x14, + 0x03, 0x13, 0xbb, 0xc8, 0xa9, 0xa0, 0xaf, 0xc3, 0x3c, 0xcd, 0x0a, 0x23, 0xfb, 0x1e, 0x2f, 0x28, 0x46, 0x7d, 0xb3, 0x7e, 0x31, 0x67, + 0xbd, 0xe4, 0x59, 0x3c, 0xb7, 0x8f, 0xf2, 0x68, 0x56, 0xb7, 0x18, 0xaa, 0x39, 0x19, 0xfe, 0x22, 0xed, 0xd8, 0x23, 0xca, 0x99, 0xe3, + 0x64, 0xb1, 0x1e, 0x8f, 0x13, 0x8c, 0x9e, 0x6c, 0xdc, 0xea, 0xe1, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xb5, 0x55, 0x30, 0x23, 0xf4, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x2e, 0x72, + 0x65, 0x6c, 0x73, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0x47, 0x88, 0xbc, + 0xe2, 0x5d, 0x03, 0x00, 0x00, 0x35, 0x08, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x25, 0x03, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, + 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xf0, 0x08, 0x58, 0xf4, 0xa5, 0x02, 0x00, 0x00, + 0x52, 0x06, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x06, 0x00, 0x00, + 0x78, 0x6c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc1, 0x17, 0x10, 0xbe, 0x4e, 0x07, 0x00, 0x00, 0xc6, 0x20, 0x00, 0x00, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x09, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x74, 0x68, 0x65, 0x6d, + 0x65, 0x2f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xff, 0x6b, 0x0c, 0xe9, 0xcd, 0x01, 0x00, 0x00, 0xb5, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x10, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x68, 0x65, 0x65, 0x74, 0x73, 0x2f, 0x73, 0x68, 0x65, 0x65, 0x74, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, + 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc2, 0x5e, 0x59, 0x08, 0x90, 0x01, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x64, 0x6f, 0x63, 0x50, + 0x72, 0x6f, 0x70, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xbd, 0xde, 0xac, 0xb8, 0x45, 0x01, 0x00, 0x00, 0x6f, 0x02, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x15, 0x00, 0x00, 0x64, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x73, + 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x9a, + 0xab, 0x0f, 0x4f, 0x5e, 0xff, 0xec, 0xaa, 0x93, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x43, 0x18, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x99, + 0x64, 0xef, 0x50, 0xd9, 0x19, 0xd7, 0x73, 0x3f, 0x01, 0x00, 0x00, 0x7e, 0x04, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x08, 0x19, 0x00, 0x00, 0x5b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x54, 0x79, + 0x70, 0x65, 0x73, 0x5d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xa7, 0x64, + 0xef, 0x50, 0x59, 0xaf, 0xf9, 0x51, 0xdc, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x78, 0x1a, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x77, 0x6f, 0x72, + 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x80, 0x02, 0x00, 0x00, 0x8c, 0x1b, 0x00, 0x00, 0x00, 0x00 + }; + } // namespace XLTemplate +} // namespace OpenXLSX + +#endif // OPENXLSX_XLTEMPLATES_HPP diff --git a/OpenXLSX/headers/XLWorkbook.hpp b/OpenXLSX/headers/XLWorkbook.hpp index 7150ef50..8c30b454 100644 --- a/OpenXLSX/headers/XLWorkbook.hpp +++ b/OpenXLSX/headers/XLWorkbook.hpp @@ -15,33 +15,33 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. MM _MM_ - Copyright (c) 2018, Kenneth Troldal Balslev - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the author nor the - names of any contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ + Copyright (c) 2018, Kenneth Troldal Balslev + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ #ifndef OPENXLSX_XLWORKBOOK_HPP #define OPENXLSX_XLWORKBOOK_HPP @@ -72,6 +72,10 @@ namespace OpenXLSX class XLChartsheet; + class XLNamedRange; + + class XLTable; + /** * @brief The XLSheetType class is an enumeration of the available sheet types, e.g. Worksheet (ordinary * spreadsheets), and Chartsheet (sheets with only a chart). @@ -142,28 +146,49 @@ namespace OpenXLSX * @return A pointer to an XLAbstractSheet with the sheet at the index. * @note The index must be 1-based (rather than 0-based) as this is the default for Excel spreadsheets. */ - XLSheet sheet(uint16_t index); + XLSheet sheet(uint16_t index) const; /** * @brief Get the sheet (worksheet or chartsheet) with the given name. * @param sheetName The name at which the desired sheet is located. * @return A pointer to an XLAbstractSheet with the sheet at the index. */ - XLSheet sheet(const std::string& sheetName); + XLSheet sheet(const std::string& sheetName) const; + + /** + * @brief Get the table with the given name. + * @param tableName The name at which the desired sheet is located. + * @return The table. + */ + XLTable table(const std::string& tableName) const; + + /** + * @brief Get the table with the given index. + * @param index The name at which the desired sheet is located. + * @return The table. + */ + XLTable table(uint16_t index) const; + + /** + * @brief + * @param rangeName + * @return A XLDefinedName object which is derived from XLCellRange + */ + XLNamedRange namedRange(const std::string& rangeName) const; /** * @brief * @param sheetName * @return */ - XLWorksheet worksheet(const std::string& sheetName); + XLWorksheet worksheet(const std::string& sheetName) const; /** * @brief * @param sheetName * @return */ - XLChartsheet chartsheet(const std::string& sheetName); + XLChartsheet chartsheet(const std::string& sheetName) const; /** * @brief Delete sheet (worksheet or chartsheet) from the workbook. @@ -177,9 +202,45 @@ namespace OpenXLSX /** * @brief * @param sheetName + * @return the created worksheet + */ + XLWorksheet addWorksheet(const std::string& sheetName); + + /** + * @brief + * @param rangeName Name of the range to be deleted + * @param localSheetId Id of the sheet where the name is defined, default is 0 (global) + */ + void deleteNamedRange(const std::string& rangeName, + uint32_t localSheetId = 0); + + /** + * @brief + * @param rangeName Name of the range to be created + * @param reference Reference of the cell/range to be named: Sheet1!$I$17:$I$19 + * @param localSheetId Id of the sheet where the name is defined, default is 0 (global) + * @return the created namedRange + */ + XLNamedRange addNamedRange(const std::string& rangeName, + const std::string& reference, + uint32_t localSheetId = 0); + + /** + * @brief + * @param sheetName worksheet which will hold the new table + * @param tableName Name of the range to be created + * @param reference Reference of the cell/range to be named: $I$17:$I$19 + * @return the created table */ - void addWorksheet(const std::string& sheetName); + XLTable addTable(const std::string& sheetName, const std::string& tableName, + const std::string& reference); + /** + * @brief detele table but without clearing the data + * @param tableName Name of the table to be deleted + */ + void deleteTable(const std::string& tableName); + /** * @brief * @param existingName @@ -233,6 +294,12 @@ namespace OpenXLSX */ unsigned int chartsheetCount() const; + /** + * @brief + * @return + */ + unsigned int tableCount() const; + /** * @brief * @return @@ -251,6 +318,12 @@ namespace OpenXLSX */ std::vector chartsheetNames() const; + /** + * @brief + * @return + */ + std::vector tableNames() const; + /** * @brief * @param sheetName @@ -272,6 +345,13 @@ namespace OpenXLSX */ bool chartsheetExists(const std::string& sheetName) const; + /** + * @brief + * @param tableName + * @return + */ + bool tableExists(const std::string& tableName) const; + /** * @brief * @param oldName @@ -283,13 +363,13 @@ namespace OpenXLSX * @brief * @return */ - XLSharedStrings sharedStrings(); + // XLSharedStrings sharedStrings(); /** * @brief * @return */ - bool hasSharedStrings() const; + // bool hasSharedStrings() const; /** * @brief @@ -303,12 +383,6 @@ namespace OpenXLSX private: // ---------- Private Member Functions ---------- // - /** - * @brief - * @return - */ - uint16_t createInternalSheetID(); - /** * @brief * @param sheetName @@ -334,8 +408,11 @@ namespace OpenXLSX * @brief * @param sheetName * @param internalID + * @param sheetID */ - void prepareSheetMetadata(const std::string& sheetName, uint16_t internalID); + void prepareSheetMetadata(const std::string& sheetName, + uint16_t internalID, + uint16_t sheetID); /** * @brief @@ -364,7 +441,7 @@ namespace OpenXLSX * @param state */ void setSheetActive(const std::string& sheetRID); - + }; } // namespace OpenXLSX diff --git a/OpenXLSX/headers/XLXmlData.hpp b/OpenXLSX/headers/XLXmlData.hpp index 8169fbd6..793d9f9d 100644 --- a/OpenXLSX/headers/XLXmlData.hpp +++ b/OpenXLSX/headers/XLXmlData.hpp @@ -55,6 +55,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include #include #include +#include // ===== OpenXLSX Includes ===== // #include "OpenXLSX-Exports.hpp" @@ -93,7 +94,9 @@ namespace OpenXLSX XLXmlData(XLDocument* parentDoc, const std::string& xmlPath, const std::string& xmlId = "", - XLContentType xmlType = XLContentType::Unknown); + const std::string& name = "", + XLContentType xmlType = XLContentType::Unknown, + XLXmlData* parentNode = nullptr); /** * @brief Default destructor. The XLXmlData does not manage any dynamically allocated resources, so a default @@ -137,6 +140,11 @@ namespace OpenXLSX */ void setRawData(const std::string& data); + void setXmlID(const std::string& xmlID); + void setName(const std::string& name); + void setParentNode(XLXmlData* parentNode); + void addChildNode(XLXmlData* childNode); + /** * @brief Get the raw data for the underlying XML document. This function will retrieve the raw XML text data * from the underlying XMLDocument object. This will mainly be used when saving data to the .xlsx package @@ -169,12 +177,26 @@ namespace OpenXLSX */ std::string getXmlID() const; + const std::string& getName() const; + /** * @brief Retrieve the type represented by the XML data. * @return A XLContentType getValue representing the type. */ XLContentType getXmlType() const; + /** + * @brief Retrieve the type represented by the XML data. + * @return A XLContentType getValue representing the type. + */ + XLXmlData* getParentNode() const; + + /** + * @brief Retrieve the type represented by the XML data. + * @return A XLContentType getValue representing the type. + */ + std::vector& getChildNodes(); + /** * @brief Access the underlying XMLDocument object. * @return A pointer to the XMLDocument object. @@ -189,11 +211,14 @@ namespace OpenXLSX private: // ===== PRIVATE MEMBER VARIABLES ===== // - + XLDocument* m_parentDoc {}; /**< A pointer to the parent XLDocument object. >*/ std::string m_xmlPath {}; /**< The path of the XML data in the .xlsx zip archive. >*/ std::string m_xmlID {}; /**< The relationship ID of the XML data. >*/ + std::string m_name{}; XLContentType m_xmlType {}; /**< The type represented by the XML data. >*/ + XLXmlData* m_parentNode {}; /**< A pointer to the parent XLXmlData object. >*/ + std::vector m_childNodes {}; /**< A pointer to the parent XLXmlData object. >*/ mutable std::unique_ptr m_xmlDoc; /**< The underlying XMLDocument object. >*/ }; } // namespace OpenXLSX diff --git a/OpenXLSX/sources/XLAutofilter.cpp b/OpenXLSX/sources/XLAutofilter.cpp new file mode 100644 index 00000000..4bf54084 --- /dev/null +++ b/OpenXLSX/sources/XLAutofilter.cpp @@ -0,0 +1,119 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +// ===== External Includes ===== // +#include +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLAutofilter.hpp" +#include "XLCellRange.hpp" +#include "XLXmlData.hpp" + + + +using namespace OpenXLSX; + + + XLAutofilter:: XLAutofilter(const XMLNode& dataNode, XLXmlData* parent): + m_dataNode(std::make_unique(dataNode)), + m_parent(parent) +{ +} +// TODO check if Autofilter is valid +XLAutofilter::~XLAutofilter() = default; + +std::string XLAutofilter::ref() const +{ + return std::string(m_dataNode->attribute("ref").value()); +} + + +void XLAutofilter::setRef(const std::string& ref) const +{ + auto node = m_dataNode->attribute("ref"); + if(!node) + node = m_dataNode->append_attribute("ref"); + + node.set_value(ref.c_str()); +} + +void XLAutofilter::hideArrow(uint16_t indexCol, bool hide) const +{ + auto node = m_dataNode->find_child_by_attribute("colId", std::to_string(indexCol).c_str()); + if (!node){ + node = m_dataNode->append_child("filterColumn"); + node.append_attribute("colId") + .set_value(std::to_string(indexCol).c_str()); + } + if(!node.attribute("hiddenButton")) + node.append_attribute("hiddenButton"); + + if(hide) + node.attribute("hiddenButton").set_value("1"); + else + node.attribute("hiddenButton").set_value("0"); +} + +void XLAutofilter::hideArrows(bool hide) const +{ + uint16_t nCol = XLCellRange::columnsCount(ref()); + for (uint16_t i = 0; i < nCol;i++) + hideArrow(i,hide); +} +/* +std::string XLTableColumn::name() const +{ + return (m_dataNode->attribute("name").value()); +} + +void XLTableColumn::setName(const std::string& columnName) const +{ + m_dataNode->attribute("name").set_value(columnName.c_str()); + // TODO change the formulas in table.xml + // TODO change the formulas in the sheet +} +*/ diff --git a/OpenXLSX/sources/XLCell.cpp b/OpenXLSX/sources/XLCell.cpp index dc479650..01b2073d 100644 --- a/OpenXLSX/sources/XLCell.cpp +++ b/OpenXLSX/sources/XLCell.cpp @@ -48,7 +48,9 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== OpenXLSX Includes ===== // #include "XLCell.hpp" +#include "XLSheet.hpp" #include "XLCellRange.hpp" +#include "XLStyles.hpp" #include "utilities/XLUtilities.hpp" using namespace OpenXLSX; @@ -58,8 +60,9 @@ using namespace OpenXLSX; */ XLCell::XLCell() : m_cellNode(nullptr), - m_valueProxy(XLCellValueProxy(this, m_cellNode.get())), - m_formulaProxy(XLFormulaProxy(this, m_cellNode.get())) + m_worksheet(nullptr), + m_valueProxy(XLCellValueProxy(this, m_cellNode)), + m_formulaProxy(XLFormulaProxy(this, m_cellNode)) {} /** @@ -68,21 +71,21 @@ XLCell::XLCell() * If a cell XMLNode does not exist (i.e., the cell is empty), use the relevant constructor to create an XLCell * from a XLCellReference parameter. */ -XLCell::XLCell(const XMLNode& cellNode, const XLSharedStrings& sharedStrings) - : m_cellNode(std::make_unique(cellNode)), - m_sharedStrings(sharedStrings), - m_valueProxy(XLCellValueProxy(this, m_cellNode.get())), - m_formulaProxy(XLFormulaProxy(this, m_cellNode.get())) +XLCell::XLCell(const XMLNode& cellNode, const XLWorksheet* wks) + : m_cellNode(std::make_shared(cellNode)), + m_worksheet(wks), + m_valueProxy(XLCellValueProxy(this, m_cellNode)), + m_formulaProxy(XLFormulaProxy(this, m_cellNode)) {} /** * @details */ XLCell::XLCell(const XLCell& other) - : m_cellNode(other.m_cellNode ? std::make_unique(*other.m_cellNode) : nullptr), - m_sharedStrings(other.m_sharedStrings), - m_valueProxy(XLCellValueProxy(this, m_cellNode.get())), - m_formulaProxy(XLFormulaProxy(this, m_cellNode.get())) + : m_cellNode(other.m_cellNode ? std::make_shared(*other.m_cellNode) : nullptr), + m_worksheet(other.m_worksheet), + m_valueProxy(XLCellValueProxy(this, m_cellNode)), + m_formulaProxy(XLFormulaProxy(this, m_cellNode)) {} /** @@ -90,9 +93,9 @@ XLCell::XLCell(const XLCell& other) */ XLCell::XLCell(XLCell&& other) noexcept : m_cellNode(std::move(other.m_cellNode)), - m_sharedStrings(std::move(other.m_sharedStrings)), - m_valueProxy(XLCellValueProxy(this, m_cellNode.get())), - m_formulaProxy(XLFormulaProxy(this, m_cellNode.get())) + m_worksheet(other.m_worksheet), + m_valueProxy(XLCellValueProxy(this, m_cellNode)), + m_formulaProxy(XLFormulaProxy(this, m_cellNode)) {} /** @@ -120,8 +123,9 @@ XLCell& XLCell::operator=(XLCell&& other) noexcept { if (&other != this) { m_cellNode = std::move(other.m_cellNode); - m_sharedStrings = other.m_sharedStrings; - m_valueProxy = XLCellValueProxy(this, m_cellNode.get()); + m_worksheet = other.m_worksheet; + m_valueProxy = XLCellValueProxy(this, m_cellNode); + m_formulaProxy = XLFormulaProxy(this, m_cellNode); } return *this; @@ -140,7 +144,7 @@ XLCell::operator bool() const */ XLCellReference XLCell::cellReference() const { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); return XLCellReference{m_cellNode->attribute("r").value()}; } @@ -149,11 +153,11 @@ XLCellReference XLCell::cellReference() const */ XLCell XLCell::offset(uint16_t rowOffset, uint16_t colOffset) const { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); XLCellReference offsetRef(cellReference().row() + rowOffset, cellReference().column() + colOffset); auto rownode = getRowNode(m_cellNode->parent().parent(), offsetRef.row()); auto cellnode = getCellNode(rownode, offsetRef.column()); - return XLCell{cellnode, m_sharedStrings}; + return XLCell{cellnode, m_worksheet}; } /** @@ -161,7 +165,7 @@ XLCell XLCell::offset(uint16_t rowOffset, uint16_t colOffset) const */ bool XLCell::hasFormula() const { - if (!*this) return false; + if (!this) return false; return m_cellNode->child("f") != nullptr; } @@ -170,7 +174,7 @@ bool XLCell::hasFormula() const */ XLFormulaProxy& XLCell::formula() { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); return m_formulaProxy; } @@ -179,7 +183,7 @@ XLFormulaProxy& XLCell::formula() */ const XLFormulaProxy& XLCell::formula() const { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); return m_formulaProxy; } @@ -189,7 +193,7 @@ const XLFormulaProxy& XLCell::formula() const */ XLCellValueProxy& XLCell::value() { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); return m_valueProxy; } @@ -200,7 +204,7 @@ XLCellValueProxy& XLCell::value() */ const XLCellValueProxy& XLCell::value() const { - if (!*this) throw XLInternalError("XLCell object has not been properly initiated."); + if (!this) throw XLInternalError("XLCell object has not been properly initiated."); return m_valueProxy; } @@ -213,3 +217,22 @@ bool XLCell::isEqual(const XLCell& lhs, const XLCell& rhs) { return *lhs.m_cellNode == *rhs.m_cellNode; } + +/** + * @details + * @pre + * @post + */ +const OpenXLSX::XLStyles& OpenXLSX::XLCell::styles() const{ + return m_worksheet->parentDoc().styles(); +} + +/** + * @details + * @pre + * @post + */ +const XLStyle XLCell::style() const{ + const OpenXLSX::XLStyles& _styles = styles(); + return _styles.style(*this); +} diff --git a/OpenXLSX/sources/XLCellIterator.cpp b/OpenXLSX/sources/XLCellIterator.cpp index 9477f571..1aa7c725 100644 --- a/OpenXLSX/sources/XLCellIterator.cpp +++ b/OpenXLSX/sources/XLCellIterator.cpp @@ -67,12 +67,13 @@ XLCellIterator::XLCellIterator(const XLCellRange& cellRange, XLIteratorLocation : m_dataNode(std::make_unique(*cellRange.m_dataNode)), m_topLeft(cellRange.m_topLeft), m_bottomRight(cellRange.m_bottomRight), - m_sharedStrings(cellRange.m_sharedStrings) + m_worksheet(cellRange.m_worksheet) { if (loc == XLIteratorLocation::End) m_currentCell = XLCell(); else { - m_currentCell = XLCell(getCellNode(getRowNode(*m_dataNode, m_topLeft.row()), m_topLeft.column()), m_sharedStrings); + m_currentCell = XLCell(getCellNode(getRowNode(*m_dataNode, m_topLeft.row()), + m_topLeft.column()), m_worksheet); } } @@ -89,7 +90,7 @@ XLCellIterator::XLCellIterator(const XLCellIterator& other) m_topLeft(other.m_topLeft), m_bottomRight(other.m_bottomRight), m_currentCell(other.m_currentCell), - m_sharedStrings(other.m_sharedStrings) + m_worksheet(other.m_worksheet) {} /** @@ -107,7 +108,7 @@ XLCellIterator& XLCellIterator::operator=(const XLCellIterator& other) m_topLeft = other.m_topLeft; m_bottomRight = other.m_bottomRight; m_currentCell = other.m_currentCell; - m_sharedStrings = other.m_sharedStrings; + m_worksheet = other.m_worksheet; } return *this; @@ -141,7 +142,7 @@ XLCellIterator& XLCellIterator::operator++() node = m_currentCell.m_cellNode->parent().insert_child_after("c", *m_currentCell.m_cellNode); node.append_attribute("r").set_value(ref.address().c_str()); } - m_currentCell = XLCell(node, m_sharedStrings); + m_currentCell = XLCell(node, m_worksheet); } else if (ref.row() > m_currentCell.cellReference().row()) { auto rowNode = m_currentCell.m_cellNode->parent().next_sibling(); @@ -151,7 +152,7 @@ XLCellIterator& XLCellIterator::operator++() // getRowNode(*m_dataNode, ref.row()); } - m_currentCell = XLCell(getCellNode(rowNode, ref.column()), m_sharedStrings); + m_currentCell = XLCell(getCellNode(rowNode, ref.column()), m_worksheet); } else throw XLInternalError("An internal error occured"); diff --git a/OpenXLSX/sources/XLCellRange.cpp b/OpenXLSX/sources/XLCellRange.cpp index 6031cb3a..0b01441b 100644 --- a/OpenXLSX/sources/XLCellRange.cpp +++ b/OpenXLSX/sources/XLCellRange.cpp @@ -49,9 +49,16 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== OpenXLSX Includes ===== // #include "XLCellRange.hpp" +#include "XLSheet.hpp" using namespace OpenXLSX; +namespace OpenXLSX +{ + XMLNode getRowNode(XMLNode sheetDataNode, uint32_t rowNumber); + XMLNode getCellNode(XMLNode rowNode, uint16_t columnNumber); +} + /** * @details From the two XLCellReference objects, the constructor calculates the dimensions of the range. * If the range exceeds the current bounds of the spreadsheet, the spreadsheet is resized to fit. @@ -61,11 +68,11 @@ using namespace OpenXLSX; XLCellRange::XLCellRange(const XMLNode& dataNode, const XLCellReference& topLeft, const XLCellReference& bottomRight, - const XLSharedStrings& sharedStrings) + const XLWorksheet* wks) : m_dataNode(std::make_unique(dataNode)), m_topLeft(topLeft), m_bottomRight(bottomRight), - m_sharedStrings(sharedStrings) + m_worksheet(wks) {} /** @@ -77,7 +84,7 @@ XLCellRange::XLCellRange(const XLCellRange& other) : m_dataNode(std::make_unique(*other.m_dataNode)), m_topLeft(other.m_topLeft), m_bottomRight(other.m_bottomRight), - m_sharedStrings(other.m_sharedStrings) + m_worksheet(other.m_worksheet) {} /** @@ -105,7 +112,7 @@ XLCellRange& XLCellRange::operator=(const XLCellRange& other) *m_dataNode = *other.m_dataNode; m_topLeft = other.m_topLeft; m_bottomRight = other.m_bottomRight; - m_sharedStrings = other.m_sharedStrings; + m_worksheet = other.m_worksheet; } return *this; @@ -122,12 +129,44 @@ XLCellRange& XLCellRange::operator=(XLCellRange&& other) noexcept *m_dataNode = *other.m_dataNode; m_topLeft = other.m_topLeft; m_bottomRight = other.m_bottomRight; - m_sharedStrings = other.m_sharedStrings; + m_worksheet = other.m_worksheet; } return *this; } +/** + * @details + * @pre + * @post + */ +bool XLCellRange::operator==(const XLCellRange& rhs) const +{ + return ((m_topLeft == rhs.m_topLeft)&&(m_bottomRight == rhs.m_bottomRight)); +} + +/** + * @details + * @pre + * @post + */ +bool XLCellRange::operator!=(const XLCellRange& rhs) const +{ + return !(*this == rhs); +} + +XLCell XLCellRange::operator[](uint32_t index) const +{ + uint16_t nCol = numColumns(); + uint32_t row = m_topLeft.row() + index / nCol; + uint16_t col = m_topLeft.column() + index % nCol; + + if(row > m_bottomRight.row()) + return XLCell(); + + return XLCell(getCellNode(getRowNode(*m_dataNode, row), col), m_worksheet); +} + /** * @details * @pre @@ -168,6 +207,17 @@ XLCellIterator XLCellRange::end() const return XLCellIterator(*this, XLIteratorLocation::End); } +/** + * @details + * @pre + * @post + */ +void XLCellRange::offset(int row, int col) +{ + m_topLeft.offset(row,col); + m_bottomRight.offset(row, col); +} + /** * @details * @pre @@ -177,3 +227,57 @@ void XLCellRange::clear() { for(auto& cell: *this) cell.value().clear(); } + +void XLCellRange::setRangeCoordinates(const XLCellReference& topLeft, + const XLCellReference& bottomRight) +{ + m_topLeft = topLeft; + m_bottomRight = bottomRight; +} + + + +std::pair XLCellRange::rangeCoordinates() +{ + return std::make_pair(m_topLeft,m_bottomRight); +} + +const std::pair XLCellRange::rangeCoordinates() const +{ + return std::make_pair(m_topLeft,m_bottomRight); +} + +std::pair XLCellRange::topLeftBottomRight(const std::string& ref) +{ + std::string::size_type n = ref.find(':'); + if(n>ref.size()) // there is no : separator + return std::make_pair(ref,ref); + //throw XLInputError("Invalid reference \"" + ref + "\" for a range"); + + return std::make_pair(ref.substr(0, n),ref.substr(n+1)); +} + +uint16_t XLCellRange::columnsCount(const std::string& ref) +{ + std::string::size_type n = ref.find(':'); + if(n>ref.size()) // there is no : separator + return 1; + + XLCoordinates topleft = XLCellReference::coordinatesFromAddress(ref.substr(0,n)); + XLCoordinates bottomright = XLCellReference::coordinatesFromAddress(ref.substr(n+1)); + + return bottomright.second - topleft.second + 1; +} + +uint32_t XLCellRange::rowsCount(const std::string& ref) +{ + std::string::size_type n = ref.find(':'); + if(n>ref.size()) // there is no : separator + return 1; + + XLCoordinates topleft = XLCellReference::coordinatesFromAddress(ref.substr(0,n)); + XLCoordinates bottomright = XLCellReference::coordinatesFromAddress(ref.substr(n+1)); + + return bottomright.first - topleft.first + 1; + +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLCellReference.cpp b/OpenXLSX/sources/XLCellReference.cpp index ae889cc9..3c31fab5 100644 --- a/OpenXLSX/sources/XLCellReference.cpp +++ b/OpenXLSX/sources/XLCellReference.cpp @@ -46,6 +46,8 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== External Includes ===== // #include #include +#include +#include #ifdef CHARCONV_ENABLED # include #endif @@ -184,6 +186,26 @@ XLCellReference& XLCellReference::operator=(XLCellReference&& other) noexcept = return oldRef; } + +XLCellReference& XLCellReference::offset(int rows, int columns) +{ + if (m_row + rows > MAX_ROWS) + m_row = MAX_ROWS; + else if (m_row + rows < 1) + m_row = 1; + else + m_row = m_row + rows; + + if (m_column + columns > MAX_COLS) + setColumn(MAX_COLS); + else if (m_column + columns < 1) + setColumn(1); + else + setColumn(m_column + columns); + + return *this; +} + /** * @details Returns the m_row property. */ @@ -237,12 +259,23 @@ void XLCellReference::setRowAndColumn(uint32_t row, uint16_t column) m_cellAddress = columnAsString(m_column) + rowAsString(m_row); } +void XLCellReference::setCoordinates(const XLCoordinates& coord) +{ + setRowAndColumn(coord.first,coord.second); +} + + +XLCoordinates XLCellReference::coordinates() +{ + return std::make_pair(m_row,m_column); +} + /** * @details Returns the m_cellAddress property. */ -std::string XLCellReference::address() const +std::string XLCellReference::address(bool absolute) const { - return m_cellAddress; + return adressFromCoordinates(m_row, m_column, absolute); } /** @@ -254,7 +287,7 @@ void XLCellReference::setAddress(const std::string& address) auto coordinates = coordinatesFromAddress(address); m_row = coordinates.first; m_column = coordinates.second; - m_cellAddress = address; + m_cellAddress = adressFromCoordinates(m_row, m_column); } /** @@ -340,15 +373,30 @@ uint16_t XLCellReference::columnAsNumber(const std::string& column) */ XLCoordinates XLCellReference::coordinatesFromAddress(const std::string& address) { + // Escape the $ if any + std::string refAdress = address; + refAdress.erase(std::remove( refAdress.begin(), + refAdress.end(),'$'), + refAdress.end()); + uint64_t letterCount = 0; - for (auto letter : address) { + for (auto letter : refAdress) { if (letter >= 65) // NOLINT ++letterCount; else if (letter <= 57) // NOLINT break; } - auto numberCount = address.size() - letterCount; + auto numberCount = refAdress.size() - letterCount; + + return std::make_pair(rowAsNumber(refAdress.substr(letterCount, numberCount)), columnAsNumber(refAdress.substr(0, letterCount))); +} + +std::string XLCellReference::adressFromCoordinates(uint32_t row, uint16_t column, bool absolute) +{ + std::string separator = std::string(""); + if (absolute) + separator = "$"; - return std::make_pair(rowAsNumber(address.substr(letterCount, numberCount)), columnAsNumber(address.substr(0, letterCount))); + return separator + columnAsString(column) + separator + rowAsString(row); } \ No newline at end of file diff --git a/OpenXLSX/sources/XLCellValue.cpp b/OpenXLSX/sources/XLCellValue.cpp index 27cb64e5..51cabddf 100644 --- a/OpenXLSX/sources/XLCellValue.cpp +++ b/OpenXLSX/sources/XLCellValue.cpp @@ -8,6 +8,8 @@ // ===== OpenXLSX Includes ===== // #include "XLCell.hpp" #include "XLCellValue.hpp" +#include "XLSharedStrings.hpp" +#include "XLSheet.hpp" #include "XLException.hpp" using namespace OpenXLSX; @@ -117,7 +119,7 @@ std::string XLCellValue::typeAsString() const * @pre The cell and cellNode pointers must not be nullptr and must point to valid objects. * @post A valid XLCellValueProxy has been created. */ -XLCellValueProxy::XLCellValueProxy(XLCell* cell, XMLNode* cellNode) : m_cell(cell), m_cellNode(cellNode) +XLCellValueProxy::XLCellValueProxy(XLCell* cell, std::shared_ptr cellNode) : m_cell(cell), m_cellNode(cellNode) { assert(cell); // NOLINT // assert(cellNode); // NOLINT @@ -197,6 +199,9 @@ XLCellValueProxy& XLCellValueProxy::clear() // ===== Remove the value node. m_cellNode->remove_child("v"); + + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); return *this; } /** @@ -226,6 +231,9 @@ XLCellValueProxy& XLCellValueProxy::setError(const std::string &error) // ===== Disable space preservation (only relevant for strings). m_cellNode->remove_attribute(" xml:space"); + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); + return *this; } @@ -318,6 +326,9 @@ void XLCellValueProxy::setInteger(int64_t numberValue) // ===== Disable space preservation (only relevant for strings). m_cellNode->child("v").remove_attribute(m_cellNode->child("v").attribute("xml:space")); + + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); } /** @@ -346,6 +357,9 @@ void XLCellValueProxy::setBoolean(bool numberValue) // ===== Disable space preservation (only relevant for strings). m_cellNode->child("v").remove_attribute(m_cellNode->child("v").attribute("xml:space")); + + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); } /** @@ -373,6 +387,9 @@ void XLCellValueProxy::setFloat(double numberValue) // ===== Disable space preservation (only relevant for strings). m_cellNode->child("v").remove_attribute(m_cellNode->child("v").attribute("xml:space")); + + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); } else { setError("#NUM!"); @@ -402,12 +419,19 @@ void XLCellValueProxy::setString(const char* stringValue) m_cellNode->attribute("t").set_value("s"); // ===== Get or create the index in the XLSharedStrings object. - auto index = (m_cell->m_sharedStrings.stringExists(stringValue) ? m_cell->m_sharedStrings.getStringIndex(stringValue) - : m_cell->m_sharedStrings.appendString(stringValue)); + + uint32_t index; + if (getSharedString()->stringExists(stringValue)) + index = getSharedString()->getStringIndex(stringValue); + else + index = getSharedString()->appendString(stringValue); // ===== Set the text of the value node. m_cellNode->child("v").text().set(index); + // ===== Remove the is node (only relevant in case previous cell type was "inlineStr") + m_cellNode->remove_child("is"); + // IMPLEMENTATION FOR EMBEDDED STRINGS: // m_cellNode->attribute("t").set_value("str"); // m_cellNode->child("v").text().set(stringValue); @@ -419,6 +443,12 @@ void XLCellValueProxy::setString(const char* stringValue) // } } +XLSharedStrings* XLCellValueProxy::getSharedString() const +{ + XLQuery query(XLQueryType::QuerySharedStrings); + return m_cell->m_worksheet->parentDoc().execQuery(query).template result(); +} + /** * @details Get a copy of the XLCellValue object for the cell. This is private helper function for returning an * XLCellValue object corresponding to the cell value. @@ -443,7 +473,7 @@ XLCellValue XLCellValueProxy::getValue() const case XLValueType::String: if (strcmp(m_cellNode->attribute("t").value(), "s") == 0) - return XLCellValue { m_cell->m_sharedStrings.getString(static_cast(m_cellNode->child("v").text().as_ullong())) }; + return XLCellValue {getSharedString()->getString(static_cast(m_cellNode->child("v").text().as_ullong())) }; else if (strcmp(m_cellNode->attribute("t").value(), "str") == 0) return XLCellValue { m_cellNode->child("v").text().get() }; else if (strcmp(m_cellNode->attribute("t").value(), "inlineStr") == 0) diff --git a/OpenXLSX/sources/XLDateTime.cpp b/OpenXLSX/sources/XLDateTime.cpp index 86e2df13..e5de265c 100644 --- a/OpenXLSX/sources/XLDateTime.cpp +++ b/OpenXLSX/sources/XLDateTime.cpp @@ -1,11 +1,13 @@ // // Created by Kenneth Balslev on 28/08/2021. // +#include +#include +#include #include "XLDateTime.hpp" #include "XLException.hpp" -#include -#include + namespace { @@ -211,7 +213,7 @@ namespace OpenXLSX // ===== Count the number of whole years since 1900. while (true) { auto days = (isLeapYear(result.tm_year + 1900) ? 366 : 365); - if (days > serial) break; + if (days >= static_cast(serial)) break; serial -= days; ++result.tm_year; } diff --git a/OpenXLSX/sources/XLDocument.cpp b/OpenXLSX/sources/XLDocument.cpp index 17cea9e5..ce6fc600 100644 --- a/OpenXLSX/sources/XLDocument.cpp +++ b/OpenXLSX/sources/XLDocument.cpp @@ -50,372 +50,19 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. # include #endif +#include +#include + // ===== OpenXLSX Includes ===== // #include "XLContentTypes.hpp" +#include "XLConstants.hpp" #include "XLDocument.hpp" #include "XLSheet.hpp" +#include "XLCellRange.hpp" +#include "XLTemplates.hpp" using namespace OpenXLSX; -namespace -{ - const int templateSize = 7714; - const unsigned char templateData[7714] = { - 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xb5, 0x55, 0x30, 0x23, 0xf4, 0x00, 0x00, 0x00, - 0x4c, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x08, 0x02, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x20, 0xa2, 0x04, - 0x02, 0x28, 0xa0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x92, 0x4d, 0x4f, 0xc3, 0x30, 0x0c, 0x86, 0xef, 0x48, 0xfc, - 0x87, 0xc8, 0xf7, 0xd5, 0xdd, 0x90, 0x10, 0x42, 0x4b, 0x77, 0x41, 0x48, 0xbb, 0x21, 0x54, 0x7e, 0x80, 0x49, 0xdc, 0x0f, 0xb5, 0x8d, - 0xa3, 0x24, 0x1b, 0xdd, 0xbf, 0x27, 0x1c, 0x10, 0x54, 0x1a, 0x83, 0x03, 0x47, 0x7f, 0xbd, 0x7e, 0xfc, 0xca, 0xdb, 0xdd, 0x3c, 0x8d, - 0xea, 0xc8, 0x21, 0xf6, 0xe2, 0x34, 0xac, 0x8b, 0x12, 0x14, 0x3b, 0x23, 0xb6, 0x77, 0xad, 0x86, 0x97, 0xfa, 0x71, 0x75, 0x07, 0x2a, - 0x26, 0x72, 0x96, 0x46, 0x71, 0xac, 0xe1, 0xc4, 0x11, 0x76, 0xd5, 0xf5, 0xd5, 0xf6, 0x99, 0x47, 0x4a, 0x79, 0x28, 0x76, 0xbd, 0x8f, - 0x2a, 0xab, 0xb8, 0xa8, 0xa1, 0x4b, 0xc9, 0xdf, 0x23, 0x46, 0xd3, 0xf1, 0x44, 0xb1, 0x10, 0xcf, 0x2e, 0x57, 0x1a, 0x09, 0x13, 0xa5, - 0x1c, 0x86, 0x16, 0x3d, 0x99, 0x81, 0x5a, 0xc6, 0x4d, 0x59, 0xde, 0x62, 0xf8, 0xae, 0x01, 0xd5, 0x42, 0x53, 0xed, 0xad, 0x86, 0xb0, - 0xb7, 0x37, 0xa0, 0xea, 0x93, 0xcf, 0x9b, 0x7f, 0xd7, 0x96, 0xa6, 0xe9, 0x0d, 0x3f, 0x88, 0x39, 0x4c, 0xec, 0xd2, 0x99, 0x15, 0xc8, - 0x73, 0x62, 0x67, 0xd9, 0xae, 0x7c, 0xc8, 0x6c, 0x21, 0xf5, 0xf9, 0x1a, 0x55, 0x53, 0x68, 0x39, 0x69, 0xb0, 0x62, 0x9e, 0x72, 0x3a, - 0x22, 0x79, 0x5f, 0x64, 0x6c, 0xc0, 0xf3, 0x44, 0x9b, 0xbf, 0x13, 0xfd, 0x7c, 0x2d, 0x4e, 0x9c, 0xc8, 0x52, 0x22, 0x34, 0x12, 0xf8, - 0x32, 0xcf, 0x47, 0xc7, 0x25, 0xa0, 0xf5, 0x7f, 0x5a, 0xb4, 0x34, 0xf1, 0xcb, 0x9d, 0x79, 0xc4, 0x37, 0x09, 0xc3, 0xab, 0xc8, 0xf0, - 0xc9, 0x82, 0x8b, 0x1f, 0xa8, 0xde, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x21, 0x00, 0x47, 0x88, 0xbc, 0xe2, 0x5d, 0x03, 0x00, 0x00, 0x35, 0x08, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x78, - 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0xac, 0x55, 0x6d, 0x6f, 0xa3, 0x38, 0x10, 0xfe, - 0x7e, 0xd2, 0xfd, 0x07, 0xc4, 0x77, 0x8a, 0x4d, 0xcc, 0x4b, 0x50, 0xe9, 0x2a, 0x90, 0xa0, 0xab, 0xb4, 0x5d, 0x55, 0x6d, 0xb6, 0xfb, - 0xf1, 0xe4, 0x80, 0x29, 0x56, 0x01, 0x73, 0xc6, 0x34, 0xa9, 0xaa, 0xfd, 0xef, 0x3b, 0x76, 0x42, 0xda, 0x6e, 0x57, 0xa7, 0x5c, 0xf7, - 0xaa, 0xd4, 0xc6, 0x9e, 0xe1, 0xf1, 0x33, 0x33, 0xcf, 0x98, 0xf3, 0x4f, 0xbb, 0xb6, 0xb1, 0x1e, 0x99, 0x1c, 0xb8, 0xe8, 0x12, 0x1b, - 0x9f, 0x21, 0xdb, 0x62, 0x5d, 0x21, 0x4a, 0xde, 0xdd, 0x27, 0xf6, 0xd7, 0x75, 0xee, 0x44, 0xb6, 0x35, 0x28, 0xda, 0x95, 0xb4, 0x11, - 0x1d, 0x4b, 0xec, 0x27, 0x36, 0xd8, 0x9f, 0x2e, 0xfe, 0xfc, 0xe3, 0x7c, 0x2b, 0xe4, 0xc3, 0x46, 0x88, 0x07, 0x0b, 0x00, 0xba, 0x21, - 0xb1, 0x6b, 0xa5, 0xfa, 0xd8, 0x75, 0x87, 0xa2, 0x66, 0x2d, 0x1d, 0xce, 0x44, 0xcf, 0x3a, 0xb0, 0x54, 0x42, 0xb6, 0x54, 0xc1, 0x52, - 0xde, 0xbb, 0x43, 0x2f, 0x19, 0x2d, 0x87, 0x9a, 0x31, 0xd5, 0x36, 0xae, 0x87, 0x50, 0xe0, 0xb6, 0x94, 0x77, 0xf6, 0x1e, 0x21, 0x96, - 0xa7, 0x60, 0x88, 0xaa, 0xe2, 0x05, 0x5b, 0x8a, 0x62, 0x6c, 0x59, 0xa7, 0xf6, 0x20, 0x92, 0x35, 0x54, 0x01, 0xfd, 0xa1, 0xe6, 0xfd, - 0x30, 0xa1, 0xb5, 0xc5, 0x29, 0x70, 0x2d, 0x95, 0x0f, 0x63, 0xef, 0x14, 0xa2, 0xed, 0x01, 0x62, 0xc3, 0x1b, 0xae, 0x9e, 0x0c, 0xa8, - 0x6d, 0xb5, 0x45, 0x7c, 0x79, 0xdf, 0x09, 0x49, 0x37, 0x0d, 0x84, 0xbd, 0xc3, 0xbe, 0xb5, 0x93, 0xf0, 0x0b, 0xe0, 0x1f, 0x23, 0x18, - 0xbc, 0xe9, 0x24, 0x30, 0xbd, 0x3b, 0xaa, 0xe5, 0x85, 0x14, 0x83, 0xa8, 0xd4, 0x19, 0x40, 0xbb, 0x7b, 0xd2, 0xef, 0xe2, 0xc7, 0xc8, - 0xc5, 0xf8, 0x4d, 0x0a, 0x76, 0xef, 0x73, 0x70, 0x1a, 0x12, 0x71, 0x25, 0x7b, 0xe4, 0xba, 0x86, 0x47, 0x56, 0x32, 0xf8, 0x20, 0xab, - 0xe0, 0x88, 0x15, 0xbc, 0x80, 0x61, 0xf4, 0xdb, 0x68, 0x18, 0xa4, 0x65, 0xb4, 0x12, 0x43, 0xf2, 0x3e, 0x88, 0xe6, 0x1f, 0xb9, 0x79, - 0xf6, 0xc5, 0x79, 0xc5, 0x1b, 0x76, 0xb7, 0x97, 0xae, 0x45, 0xfb, 0xfe, 0x0b, 0x6d, 0x75, 0xa5, 0x1a, 0xdb, 0x6a, 0xe8, 0xa0, 0x56, - 0x25, 0x57, 0xac, 0x4c, 0xec, 0x10, 0x96, 0x62, 0xcb, 0xde, 0x6c, 0xc8, 0xb1, 0x4f, 0x47, 0xde, 0x80, 0x15, 0xa3, 0x08, 0x7b, 0xb6, - 0x7b, 0x71, 0x94, 0xf3, 0xb5, 0xb4, 0x4a, 0x56, 0xd1, 0xb1, 0x51, 0x6b, 0x10, 0xf2, 0x04, 0x0f, 0x8e, 0x41, 0x30, 0xf7, 0x7c, 0xed, - 0x09, 0xc2, 0x58, 0x34, 0x8a, 0xc9, 0x8e, 0x2a, 0x96, 0x89, 0x4e, 0x81, 0x0e, 0x0f, 0x71, 0xfd, 0xae, 0xe6, 0x0c, 0x76, 0x56, 0x0b, - 0x50, 0xb8, 0x75, 0xc3, 0xfe, 0x19, 0xb9, 0x64, 0xd0, 0x58, 0xa0, 0x2f, 0x88, 0x15, 0x46, 0x5a, 0xc4, 0x74, 0x33, 0x5c, 0x53, 0x55, - 0x5b, 0xa3, 0x6c, 0x12, 0xdb, 0xfd, 0x3a, 0x40, 0xf0, 0xee, 0x5a, 0x8a, 0x06, 0xba, 0xd3, 0x5d, 0xb2, 0x47, 0xd6, 0x88, 0xde, 0xf4, - 0xc5, 0x86, 0x77, 0x5e, 0xe1, 0xbe, 0x52, 0x28, 0x7d, 0xdf, 0x0e, 0xff, 0x41, 0xa3, 0xb4, 0xd0, 0x81, 0xbb, 0x10, 0xf9, 0x9e, 0xdd, - 0xfe, 0xf9, 0xe7, 0x2c, 0x00, 0x49, 0x19, 0x4f, 0x3a, 0xbc, 0x56, 0xd2, 0x82, 0xe7, 0xcb, 0xe5, 0x67, 0xa8, 0xc5, 0x2d, 0x7d, 0x84, - 0xca, 0x40, 0xfd, 0xcb, 0x43, 0xe3, 0x5e, 0x42, 0xea, 0xa3, 0xbf, 0x9f, 0x17, 0xd9, 0x6c, 0xb6, 0x08, 0xf2, 0xd4, 0xc1, 0x01, 0x9a, - 0x39, 0x5e, 0x40, 0xb0, 0xb3, 0x20, 0x28, 0x77, 0xd2, 0x30, 0x0b, 0x73, 0x2f, 0x45, 0xab, 0x60, 0x1e, 0x7d, 0x87, 0x28, 0x64, 0x10, - 0x17, 0x82, 0x8e, 0xaa, 0x3e, 0x54, 0x5b, 0x63, 0x26, 0x36, 0x21, 0xbf, 0x30, 0x5d, 0xd1, 0xdd, 0x64, 0xc1, 0x28, 0x1e, 0x79, 0xf9, - 0x72, 0xfe, 0x33, 0x3a, 0xfc, 0x39, 0x7a, 0xfe, 0x69, 0x98, 0x6c, 0xdf, 0x75, 0xa4, 0xfa, 0x5e, 0xbb, 0xe3, 0x6c, 0x3b, 0xbc, 0xe8, - 0x42, 0x2f, 0xad, 0xdd, 0x37, 0xde, 0x95, 0x62, 0x9b, 0xd8, 0x5e, 0x18, 0x41, 0x34, 0x4f, 0xd3, 0x12, 0xfb, 0x01, 0x2c, 0xb7, 0xc6, - 0xf8, 0x8d, 0x97, 0xaa, 0x06, 0x8f, 0x08, 0x91, 0xe3, 0xde, 0x5f, 0x8c, 0xdf, 0xd7, 0xc0, 0x18, 0x87, 0x44, 0x6f, 0x82, 0xfe, 0x35, - 0xb3, 0xc4, 0x7e, 0xce, 0x73, 0x32, 0x4f, 0x23, 0xbc, 0x70, 0xb0, 0x17, 0x2e, 0x9c, 0x94, 0x90, 0x0c, 0x12, 0x40, 0x52, 0x27, 0xcf, - 0x51, 0x3e, 0x5b, 0xe5, 0x51, 0x9e, 0xe5, 0x73, 0xc3, 0xc8, 0x7d, 0x45, 0xc9, 0xdc, 0xa0, 0x40, 0xcd, 0xcc, 0x56, 0x67, 0x54, 0x7f, - 0xab, 0x6f, 0x55, 0x0c, 0x57, 0xb5, 0x9e, 0x75, 0x76, 0xe1, 0x59, 0xc6, 0xfa, 0x0c, 0x79, 0x59, 0x62, 0x53, 0xbd, 0xe9, 0xb5, 0x82, - 0x36, 0x05, 0xa8, 0x5c, 0x4f, 0xc6, 0x31, 0xc2, 0xc8, 0x9b, 0x6b, 0x0f, 0xb6, 0x53, 0x9f, 0x07, 0x65, 0x66, 0x10, 0x18, 0x07, 0x7a, - 0x98, 0xa0, 0x45, 0x88, 0xe6, 0xc4, 0x41, 0xab, 0x99, 0xef, 0x90, 0x68, 0xee, 0x39, 0x11, 0x99, 0x79, 0x4e, 0x46, 0x96, 0xde, 0xca, - 0x0f, 0x57, 0xcb, 0x55, 0xea, 0xeb, 0xfa, 0xe8, 0x2f, 0x40, 0xfc, 0x7f, 0xdc, 0x83, 0x46, 0xe7, 0xf1, 0xf4, 0x69, 0xd1, 0x2c, 0x6b, - 0x2a, 0xd5, 0x5a, 0xd2, 0xe2, 0x01, 0x3e, 0x48, 0x37, 0xac, 0x4a, 0xe9, 0x00, 0x4a, 0xda, 0x07, 0x04, 0x7c, 0x5f, 0x93, 0x4d, 0xfd, - 0x28, 0x45, 0x33, 0xa0, 0x48, 0x72, 0x9c, 0x3b, 0x04, 0xcf, 0x91, 0x93, 0xa6, 0x01, 0x71, 0xfc, 0x65, 0x3e, 0xf3, 0x43, 0xbc, 0xcc, - 0x56, 0x7e, 0xfe, 0x42, 0x56, 0x87, 0x5f, 0x7d, 0xf0, 0x16, 0x8a, 0x5c, 0xf3, 0x36, 0xa3, 0x6a, 0x84, 0x0e, 0xd5, 0xcd, 0x69, 0xd6, - 0xb1, 0x1e, 0xf3, 0xc3, 0xee, 0x71, 0xb3, 0xda, 0x6f, 0x1c, 0xea, 0xf4, 0xa6, 0xe9, 0xe2, 0x9b, 0xa5, 0xce, 0xfb, 0xe1, 0xed, 0x7f, - 0x73, 0xbc, 0x85, 0xe8, 0x1b, 0x76, 0xa2, 0x73, 0x7e, 0x77, 0xa2, 0x63, 0xf6, 0xe5, 0x6a, 0x7d, 0x65, 0xb4, 0xf1, 0xcb, 0x00, 0x5c, - 0x93, 0x60, 0x3d, 0x1a, 0x59, 0xb8, 0x53, 0x59, 0x2e, 0x7e, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, - 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xf0, 0x08, 0x58, 0xf4, 0xa5, 0x02, 0x00, 0x00, 0x52, 0x06, 0x00, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0xa4, 0x55, 0x6d, 0x6b, 0xdb, 0x30, - 0x10, 0xfe, 0x3e, 0xd8, 0x7f, 0x10, 0xfa, 0xee, 0xca, 0x76, 0xe3, 0x2c, 0x09, 0xb6, 0xcb, 0xd2, 0xd4, 0x50, 0xe8, 0xc6, 0xa0, 0x1d, - 0xec, 0xab, 0x62, 0xcb, 0x89, 0xa8, 0x5e, 0x8c, 0x24, 0x67, 0xce, 0xc6, 0xfe, 0xfb, 0x4e, 0x76, 0x5e, 0x1c, 0x3a, 0xb6, 0xd1, 0x7e, - 0x89, 0x4e, 0xe7, 0xd3, 0x73, 0xcf, 0xdd, 0x73, 0x52, 0xd2, 0x9b, 0x4e, 0x0a, 0xb4, 0x63, 0xc6, 0x72, 0xad, 0x32, 0x1c, 0x5d, 0x85, - 0x18, 0x31, 0x55, 0xea, 0x8a, 0xab, 0x4d, 0x86, 0xbf, 0x3e, 0x15, 0xc1, 0x0c, 0x23, 0xeb, 0xa8, 0xaa, 0xa8, 0xd0, 0x8a, 0x65, 0x78, - 0xcf, 0x2c, 0xbe, 0xc9, 0xdf, 0xbf, 0x4b, 0xad, 0xdb, 0x0b, 0xf6, 0xb8, 0x65, 0xcc, 0x21, 0x80, 0x50, 0x36, 0xc3, 0x5b, 0xe7, 0x9a, - 0x05, 0x21, 0xb6, 0xdc, 0x32, 0x49, 0xed, 0x95, 0x6e, 0x98, 0x82, 0x2f, 0xb5, 0x36, 0x92, 0x3a, 0xd8, 0x9a, 0x0d, 0xb1, 0x8d, 0x61, - 0xb4, 0xb2, 0xfe, 0x90, 0x14, 0x24, 0x0e, 0xc3, 0x29, 0x91, 0x94, 0x2b, 0x3c, 0x20, 0x2c, 0x64, 0xf9, 0x3f, 0x20, 0x92, 0x9a, 0xe7, - 0xb6, 0x09, 0x4a, 0x2d, 0x1b, 0xea, 0xf8, 0x9a, 0x0b, 0xee, 0xf6, 0x3d, 0x16, 0x46, 0xb2, 0x5c, 0xdc, 0x6f, 0x94, 0x36, 0x74, 0x2d, - 0x80, 0x6a, 0x17, 0x4d, 0x68, 0x89, 0xba, 0x68, 0x6a, 0x62, 0xd4, 0x99, 0x63, 0x92, 0xde, 0xfb, 0x22, 0x8f, 0xe4, 0xa5, 0xd1, 0x56, - 0xd7, 0xee, 0x0a, 0x70, 0x89, 0xae, 0x6b, 0x5e, 0xb2, 0x97, 0x74, 0xe7, 0x64, 0x4e, 0x68, 0x79, 0x46, 0x02, 0xe4, 0xd7, 0x21, 0x45, - 0x09, 0x09, 0xe3, 0x8b, 0xda, 0x3b, 0xf3, 0x4a, 0xa4, 0x09, 0x31, 0x6c, 0xc7, 0xbd, 0x7c, 0x38, 0x4f, 0x6b, 0xad, 0x9c, 0x45, 0xa5, - 0x6e, 0x95, 0x03, 0x31, 0x81, 0xa8, 0x6f, 0xc1, 0xe2, 0x59, 0xe9, 0xef, 0xaa, 0xf0, 0x9f, 0xbc, 0x73, 0x88, 0xca, 0x53, 0xfb, 0x03, - 0xed, 0xa8, 0x00, 0x4f, 0x8c, 0x49, 0x9e, 0x96, 0x5a, 0x68, 0x83, 0x1c, 0x48, 0x07, 0x9d, 0x8b, 0xbc, 0x47, 0x51, 0xc9, 0x86, 0x88, - 0x5b, 0x2a, 0xf8, 0xda, 0x70, 0xef, 0xac, 0xa9, 0xe4, 0x62, 0x3f, 0xb8, 0xfb, 0x73, 0xbd, 0xda, 0x87, 0x38, 0xc9, 0xa1, 0xf7, 0x3e, - 0x8a, 0x78, 0x1e, 0x87, 0xc5, 0xc2, 0x21, 0x2e, 0xc4, 0x89, 0x55, 0xec, 0x09, 0x80, 0x23, 0x4f, 0x41, 0x3e, 0xc7, 0x8c, 0x2a, 0x60, - 0x83, 0x0e, 0xf6, 0xd3, 0xbe, 0x81, 0xf4, 0x0a, 0x26, 0x6d, 0x80, 0xe9, 0xe3, 0xfe, 0x11, 0xbd, 0x31, 0x74, 0x1f, 0xc5, 0xc9, 0xe8, - 0x00, 0xe9, 0x13, 0xe6, 0xe9, 0x5a, 0x9b, 0x0a, 0x26, 0xfb, 0xdc, 0x8f, 0xa3, 0x2b, 0x4f, 0x05, 0xab, 0x1d, 0x10, 0x35, 0x7c, 0xb3, - 0xf5, 0xab, 0xd3, 0x0d, 0xfc, 0xae, 0xb5, 0x73, 0xa0, 0x7e, 0x9e, 0x56, 0x9c, 0x6e, 0xb4, 0xa2, 0xc2, 0x97, 0x32, 0x80, 0x9c, 0x0c, - 0x28, 0xa7, 0x64, 0x42, 0x3c, 0xfa, 0xe9, 0xff, 0x56, 0x5f, 0x60, 0x77, 0x35, 0x52, 0xad, 0x2c, 0xa4, 0xbb, 0xaf, 0x32, 0x0c, 0xf7, - 0xc8, 0x37, 0xe1, 0x68, 0x42, 0x21, 0x07, 0x73, 0xc0, 0x1b, 0x36, 0x1e, 0x7f, 0x8c, 0x36, 0x60, 0xbf, 0x19, 0x16, 0x75, 0xf5, 0x25, - 0x3e, 0x20, 0x8e, 0x68, 0x5f, 0x90, 0x3e, 0xa5, 0x47, 0x5e, 0xef, 0x0c, 0x7f, 0xf6, 0xd7, 0x55, 0xc0, 0xe4, 0x1c, 0x20, 0xd0, 0xba, - 0xe5, 0xc2, 0x71, 0xf5, 0x07, 0xc2, 0x80, 0x59, 0x75, 0xe7, 0x16, 0x84, 0x5e, 0x01, 0xe7, 0xaf, 0x5e, 0xdf, 0x9c, 0x53, 0x16, 0xe8, - 0x44, 0xc5, 0x6a, 0xda, 0x0a, 0xf7, 0x74, 0xfa, 0x98, 0xe1, 0xb3, 0xfd, 0x89, 0x55, 0xbc, 0x95, 0xf1, 0x29, 0xea, 0x0b, 0xdf, 0x69, - 0xd7, 0x43, 0x64, 0xf8, 0x6c, 0x3f, 0x78, 0xa5, 0xa2, 0xa9, 0xcf, 0xc1, 0x3a, 0xf7, 0x60, 0x61, 0xbc, 0x60, 0x45, 0xad, 0xe1, 0x19, - 0xfe, 0x79, 0xb7, 0xfc, 0x30, 0x5f, 0xdd, 0x15, 0x71, 0x30, 0x0b, 0x97, 0xb3, 0x60, 0x72, 0xcd, 0x92, 0x60, 0x9e, 0x2c, 0x57, 0x41, - 0x32, 0xb9, 0x5d, 0xae, 0x56, 0xc5, 0x3c, 0x8c, 0xc3, 0xdb, 0x5f, 0xa3, 0x07, 0xe0, 0x0d, 0xd7, 0xbf, 0x7f, 0xaf, 0xf2, 0x14, 0x2e, - 0xd6, 0xc2, 0x0a, 0x78, 0x24, 0xcc, 0xa1, 0xd8, 0x43, 0x89, 0x8f, 0x67, 0x5f, 0x86, 0x47, 0x9b, 0x81, 0x7e, 0x3f, 0xa3, 0x40, 0x7b, - 0xcc, 0x7d, 0x1e, 0x4f, 0xc3, 0x8f, 0x49, 0x14, 0x06, 0xc5, 0x75, 0x18, 0x05, 0x93, 0x29, 0x9d, 0x05, 0xb3, 0xe9, 0x75, 0x12, 0x14, - 0x49, 0x14, 0xaf, 0xa6, 0x93, 0xe5, 0x5d, 0x52, 0x24, 0x23, 0xee, 0xc9, 0x2b, 0x9f, 0x89, 0x90, 0x44, 0xd1, 0xf0, 0xe0, 0x78, 0xf2, - 0xc9, 0xc2, 0x71, 0xc9, 0x04, 0x57, 0x47, 0xad, 0x8e, 0x0a, 0x8d, 0xbd, 0x20, 0x12, 0x6c, 0xff, 0x52, 0x04, 0x39, 0x2a, 0x41, 0xce, - 0x7f, 0x06, 0xf9, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x21, 0x00, 0xc1, 0x17, 0x10, 0xbe, 0x4e, 0x07, 0x00, 0x00, 0xc6, 0x20, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, - 0x74, 0x68, 0x65, 0x6d, 0x65, 0x2f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0xec, 0x59, 0xcd, 0x8b, 0x1b, 0x37, - 0x14, 0xbf, 0x17, 0xfa, 0x3f, 0x0c, 0x73, 0x77, 0xfc, 0x35, 0xe3, 0x8f, 0x25, 0xde, 0xe0, 0xcf, 0x6c, 0x93, 0xdd, 0x24, 0x64, 0x9d, - 0x94, 0x1c, 0xb5, 0xb6, 0xec, 0x51, 0x56, 0x33, 0x32, 0x92, 0xbc, 0x1b, 0x13, 0x02, 0x25, 0x39, 0xf5, 0x52, 0x28, 0xa4, 0xa5, 0x97, - 0x42, 0x6f, 0x3d, 0x94, 0xd2, 0x40, 0x03, 0x0d, 0xbd, 0xf4, 0x8f, 0x09, 0x24, 0xb4, 0xe9, 0x1f, 0xd1, 0x27, 0xcd, 0xd8, 0x23, 0xad, - 0xe5, 0x24, 0x9b, 0x6c, 0x4a, 0x5a, 0x76, 0x0d, 0x8b, 0x47, 0xfe, 0xbd, 0xa7, 0xa7, 0xf7, 0x9e, 0x7e, 0x7a, 0xf3, 0x74, 0xf1, 0xd2, - 0xbd, 0x98, 0x7a, 0x47, 0x98, 0x0b, 0xc2, 0x92, 0x96, 0x5f, 0xbe, 0x50, 0xf2, 0x3d, 0x9c, 0x8c, 0xd8, 0x98, 0x24, 0xd3, 0x96, 0x7f, - 0x6b, 0x38, 0x28, 0x34, 0x7c, 0x4f, 0x48, 0x94, 0x8c, 0x11, 0x65, 0x09, 0x6e, 0xf9, 0x0b, 0x2c, 0xfc, 0x4b, 0xdb, 0x9f, 0x7e, 0x72, - 0x11, 0x6d, 0xc9, 0x08, 0xc7, 0xd8, 0x03, 0xf9, 0x44, 0x6c, 0xa1, 0x96, 0x1f, 0x49, 0x39, 0xdb, 0x2a, 0x16, 0xc5, 0x08, 0x86, 0x91, - 0xb8, 0xc0, 0x66, 0x38, 0x81, 0xdf, 0x26, 0x8c, 0xc7, 0x48, 0xc2, 0x23, 0x9f, 0x16, 0xc7, 0x1c, 0x1d, 0x83, 0xde, 0x98, 0x16, 0x2b, - 0xa5, 0x52, 0xad, 0x18, 0x23, 0x92, 0xf8, 0x5e, 0x82, 0x62, 0x50, 0x7b, 0x7d, 0x32, 0x21, 0x23, 0xec, 0x0d, 0x95, 0x4a, 0x7f, 0x7b, - 0xa9, 0xbc, 0x4f, 0xe1, 0x31, 0x91, 0x42, 0x0d, 0x8c, 0x28, 0xdf, 0x57, 0xaa, 0xb1, 0x25, 0xa1, 0xb1, 0xe3, 0xc3, 0xb2, 0x42, 0x88, - 0x85, 0xe8, 0x52, 0xee, 0x1d, 0x21, 0xda, 0xf2, 0x61, 0x9e, 0x31, 0x3b, 0x1e, 0xe2, 0x7b, 0xd2, 0xf7, 0x28, 0x12, 0x12, 0x7e, 0x68, - 0xf9, 0x25, 0xfd, 0xe7, 0x17, 0xb7, 0x2f, 0x16, 0xd1, 0x56, 0x26, 0x44, 0xe5, 0x06, 0x59, 0x43, 0x6e, 0xa0, 0xff, 0x32, 0xb9, 0x4c, - 0x60, 0x7c, 0x58, 0xd1, 0x73, 0xf2, 0xe9, 0xc1, 0x6a, 0xd2, 0x20, 0x08, 0x83, 0x5a, 0x7b, 0xa5, 0x5f, 0x03, 0xa8, 0x5c, 0xc7, 0xf5, - 0xeb, 0xfd, 0x5a, 0xbf, 0xb6, 0xd2, 0xa7, 0x01, 0x68, 0x34, 0x82, 0x95, 0xa6, 0xb6, 0xd8, 0x3a, 0xeb, 0x95, 0x6e, 0x90, 0x61, 0x0d, - 0x50, 0xfa, 0xd5, 0xa1, 0xbb, 0x57, 0xef, 0x55, 0xcb, 0x16, 0xde, 0xd0, 0x5f, 0x5d, 0xb3, 0xb9, 0x1d, 0xaa, 0x8f, 0x85, 0xd7, 0xa0, - 0x54, 0x7f, 0xb0, 0x86, 0x1f, 0x0c, 0xba, 0xe0, 0x45, 0x0b, 0xaf, 0x41, 0x29, 0x3e, 0x5c, 0xc3, 0x87, 0x9d, 0x66, 0xa7, 0x67, 0xeb, - 0xd7, 0xa0, 0x14, 0x5f, 0x5b, 0xc3, 0xd7, 0x4b, 0xed, 0x5e, 0x50, 0xb7, 0xf4, 0x6b, 0x50, 0x44, 0x49, 0x72, 0xb8, 0x86, 0x2e, 0x85, - 0xb5, 0x6a, 0x77, 0xb9, 0xda, 0x15, 0x64, 0xc2, 0xe8, 0x8e, 0x13, 0xde, 0x0c, 0x83, 0x41, 0xbd, 0x92, 0x29, 0xcf, 0x51, 0x90, 0x0d, - 0xab, 0xec, 0x52, 0x53, 0x4c, 0x58, 0x22, 0x37, 0xe5, 0x5a, 0x8c, 0xee, 0x32, 0x3e, 0x00, 0x80, 0x02, 0x52, 0x24, 0x49, 0xe2, 0xc9, - 0xc5, 0x0c, 0x4f, 0xd0, 0x08, 0xb2, 0xb8, 0x8b, 0x28, 0x39, 0xe0, 0xc4, 0xdb, 0x25, 0xd3, 0x08, 0x12, 0x6f, 0x86, 0x12, 0x26, 0x60, - 0xb8, 0x54, 0x29, 0x0d, 0x4a, 0x55, 0xf8, 0xaf, 0x3e, 0x81, 0xfe, 0xa6, 0x23, 0x8a, 0xb6, 0x30, 0x32, 0xa4, 0x95, 0x5d, 0x60, 0x89, - 0x58, 0x1b, 0x52, 0xf6, 0x78, 0x62, 0xc4, 0xc9, 0x4c, 0xb6, 0xfc, 0x2b, 0xa0, 0xd5, 0x37, 0x20, 0x2f, 0x9e, 0x3d, 0x7b, 0xfe, 0xf0, - 0xe9, 0xf3, 0x87, 0xbf, 0x3d, 0x7f, 0xf4, 0xe8, 0xf9, 0xc3, 0x5f, 0xb2, 0xb9, 0xb5, 0x2a, 0x4b, 0x6e, 0x07, 0x25, 0x53, 0x53, 0xee, - 0xd5, 0x8f, 0x5f, 0xff, 0xfd, 0xfd, 0x17, 0xde, 0x5f, 0xbf, 0xfe, 0xf0, 0xea, 0xf1, 0x37, 0xe9, 0xd4, 0x27, 0xf1, 0xc2, 0xc4, 0xbf, - 0xfc, 0xf9, 0xcb, 0x97, 0xbf, 0xff, 0xf1, 0x3a, 0xf5, 0xb0, 0xe2, 0xdc, 0x15, 0x2f, 0xbe, 0x7d, 0xf2, 0xf2, 0xe9, 0x93, 0x17, 0xdf, - 0x7d, 0xf5, 0xe7, 0x4f, 0x8f, 0x1d, 0xda, 0xdb, 0x1c, 0x1d, 0x98, 0xf0, 0x21, 0x89, 0xb1, 0xf0, 0xae, 0xe1, 0x63, 0xef, 0x26, 0x8b, - 0x61, 0x81, 0x0e, 0xfb, 0xf1, 0x01, 0x3f, 0x9d, 0xc4, 0x30, 0x42, 0xc4, 0x92, 0x40, 0x11, 0xe8, 0x76, 0xa8, 0xee, 0xcb, 0xc8, 0x02, - 0x5e, 0x5b, 0x20, 0xea, 0xc2, 0x75, 0xb0, 0xed, 0xc2, 0xdb, 0x1c, 0x58, 0xc6, 0x05, 0xbc, 0x3c, 0xbf, 0x6b, 0xd9, 0xba, 0x1f, 0xf1, - 0xb9, 0x24, 0x8e, 0x99, 0xaf, 0x46, 0xb1, 0x05, 0xdc, 0x63, 0x8c, 0x76, 0x18, 0x77, 0x3a, 0xe0, 0xaa, 0x9a, 0xcb, 0xf0, 0xf0, 0x70, - 0x9e, 0x4c, 0xdd, 0x93, 0xf3, 0xb9, 0x89, 0xbb, 0x89, 0xd0, 0x91, 0x6b, 0xee, 0x2e, 0x4a, 0xac, 0x00, 0xf7, 0xe7, 0x33, 0xa0, 0x57, - 0xe2, 0x52, 0xd9, 0x8d, 0xb0, 0x65, 0xe6, 0x0d, 0x8a, 0x12, 0x89, 0xa6, 0x38, 0xc1, 0xd2, 0x53, 0xbf, 0xb1, 0x43, 0x8c, 0x1d, 0xab, - 0xbb, 0x43, 0x88, 0xe5, 0xd7, 0x3d, 0x32, 0xe2, 0x4c, 0xb0, 0x89, 0xf4, 0xee, 0x10, 0xaf, 0x83, 0x88, 0xd3, 0x25, 0x43, 0x72, 0x60, - 0x25, 0x52, 0x2e, 0xb4, 0x43, 0x62, 0x88, 0xcb, 0xc2, 0x65, 0x20, 0x84, 0xda, 0xf2, 0xcd, 0xde, 0x6d, 0xaf, 0xc3, 0xa8, 0x6b, 0xd5, - 0x3d, 0x7c, 0x64, 0x23, 0x61, 0x5b, 0x20, 0xea, 0x30, 0x7e, 0x88, 0xa9, 0xe5, 0xc6, 0xcb, 0x68, 0x2e, 0x51, 0xec, 0x52, 0x39, 0x44, - 0x31, 0x35, 0x1d, 0xbe, 0x8b, 0x64, 0xe4, 0x32, 0x72, 0x7f, 0xc1, 0x47, 0x26, 0xae, 0x2f, 0x24, 0x44, 0x7a, 0x8a, 0x29, 0xf3, 0xfa, - 0x63, 0x2c, 0x84, 0x4b, 0xe6, 0x3a, 0x87, 0xf5, 0x1a, 0x41, 0xbf, 0x0a, 0x0c, 0xe3, 0x0e, 0xfb, 0x1e, 0x5d, 0xc4, 0x36, 0x92, 0x4b, - 0x72, 0xe8, 0xd2, 0xb9, 0x8b, 0x18, 0x33, 0x91, 0x3d, 0x76, 0xd8, 0x8d, 0x50, 0x3c, 0x73, 0xda, 0x4c, 0x92, 0xc8, 0xc4, 0x7e, 0x26, - 0x0e, 0x21, 0x45, 0x91, 0x77, 0x83, 0x49, 0x17, 0x7c, 0x8f, 0xd9, 0x3b, 0x44, 0x3d, 0x43, 0x1c, 0x50, 0xb2, 0x31, 0xdc, 0xb7, 0x09, - 0xb6, 0xc2, 0xfd, 0x66, 0x22, 0xb8, 0x05, 0xe4, 0x6a, 0x9a, 0x94, 0x27, 0x88, 0xfa, 0x65, 0xce, 0x1d, 0xb1, 0xbc, 0x8c, 0x99, 0xbd, - 0x1f, 0x17, 0x74, 0x82, 0xb0, 0x8b, 0x65, 0xda, 0x3c, 0xb6, 0xd8, 0xb5, 0xcd, 0x89, 0x33, 0x3b, 0x3a, 0xf3, 0xa9, 0x95, 0xda, 0xbb, - 0x18, 0x53, 0x74, 0x8c, 0xc6, 0x18, 0x7b, 0xb7, 0x3e, 0x73, 0x58, 0xd0, 0x61, 0x33, 0xcb, 0xe7, 0xb9, 0xd1, 0x57, 0x22, 0x60, 0x95, - 0x1d, 0xec, 0x4a, 0xac, 0x2b, 0xc8, 0xce, 0x55, 0xf5, 0x9c, 0x60, 0x01, 0x65, 0x92, 0xaa, 0x6b, 0xd6, 0x29, 0x72, 0x97, 0x08, 0x2b, - 0x65, 0xf7, 0xf1, 0x94, 0x6d, 0xb0, 0x67, 0x6f, 0x71, 0x82, 0x78, 0x16, 0x28, 0x89, 0x11, 0xdf, 0xa4, 0xf9, 0x1a, 0x44, 0xdd, 0x4a, - 0x5d, 0x38, 0xe5, 0x9c, 0x54, 0x7a, 0x9d, 0x8e, 0x0e, 0x4d, 0xe0, 0x35, 0x02, 0xe5, 0x1f, 0xe4, 0x8b, 0xd3, 0x29, 0xd7, 0x05, 0xe8, - 0x30, 0x92, 0xbb, 0xbf, 0x49, 0xeb, 0x8d, 0x08, 0x59, 0x67, 0x97, 0x7a, 0x16, 0xee, 0x7c, 0x5d, 0x70, 0x2b, 0x7e, 0x6f, 0xb3, 0xc7, - 0x60, 0x5f, 0xde, 0x3d, 0xed, 0xbe, 0x04, 0x19, 0x7c, 0x6a, 0x19, 0x20, 0xf6, 0xb7, 0xf6, 0xcd, 0x10, 0x51, 0x6b, 0x82, 0x3c, 0x61, - 0x86, 0x08, 0x0a, 0x0c, 0x17, 0xdd, 0x82, 0x88, 0x15, 0xfe, 0x5c, 0x44, 0x9d, 0xab, 0x5a, 0x6c, 0xee, 0x94, 0x9b, 0xd8, 0x9b, 0x36, - 0x0f, 0x03, 0x14, 0x46, 0x56, 0xbd, 0x13, 0x93, 0xe4, 0x8d, 0xc5, 0xcf, 0x89, 0xb2, 0x27, 0xfc, 0x77, 0xca, 0x1e, 0x77, 0x01, 0x73, - 0x06, 0x05, 0x8f, 0x5b, 0xf1, 0xfb, 0x94, 0x3a, 0x9b, 0x28, 0x65, 0xe7, 0x44, 0x81, 0xb3, 0x09, 0xf7, 0x1f, 0x2c, 0x6b, 0x7a, 0x68, - 0x9e, 0xdc, 0xc0, 0x70, 0x92, 0xac, 0x73, 0xd6, 0x79, 0x55, 0x73, 0x5e, 0xd5, 0xf8, 0xff, 0xfb, 0xaa, 0x66, 0xd3, 0x5e, 0x3e, 0xaf, - 0x65, 0xce, 0x6b, 0x99, 0xf3, 0x5a, 0xc6, 0xf5, 0xf6, 0xf5, 0x41, 0x6a, 0x99, 0xbc, 0x7c, 0x81, 0xca, 0x26, 0xef, 0xf2, 0xe8, 0x9e, - 0x4f, 0xbc, 0xb1, 0xe5, 0x33, 0x21, 0x94, 0xee, 0xcb, 0x05, 0xc5, 0xbb, 0x42, 0x77, 0x7d, 0x04, 0xbc, 0xd1, 0x8c, 0x07, 0x30, 0xa8, - 0xdb, 0x51, 0xba, 0x27, 0xb9, 0x6a, 0x01, 0xce, 0x22, 0xf8, 0x9a, 0x35, 0x98, 0x2c, 0xdc, 0x94, 0x23, 0x2d, 0xe3, 0x71, 0x26, 0x3f, - 0x27, 0x32, 0xda, 0x8f, 0xd0, 0x0c, 0x5a, 0x43, 0x65, 0xdd, 0xc0, 0x9c, 0x8a, 0x4c, 0xf5, 0x54, 0x78, 0x33, 0x26, 0xa0, 0x63, 0xa4, - 0x87, 0x75, 0x2b, 0x15, 0x9f, 0xd0, 0xad, 0xfb, 0x4e, 0xf3, 0x78, 0x8f, 0x8d, 0xd3, 0x4e, 0x67, 0xb9, 0xac, 0xba, 0x9a, 0xa9, 0x0b, - 0x05, 0x92, 0xf9, 0x78, 0x29, 0x5c, 0x8d, 0x43, 0x97, 0x4a, 0xa6, 0xe8, 0x5a, 0x3d, 0xef, 0xde, 0xad, 0xd4, 0xeb, 0x7e, 0xe8, 0x54, - 0x77, 0x59, 0x97, 0x06, 0x28, 0xd9, 0xd3, 0x18, 0x61, 0x4c, 0x66, 0x1b, 0x51, 0x75, 0x18, 0x51, 0x5f, 0x0e, 0x42, 0x14, 0x5e, 0x67, - 0x84, 0x5e, 0xd9, 0x99, 0x58, 0xd1, 0x74, 0x58, 0xd1, 0x50, 0xea, 0x97, 0xa1, 0x5a, 0x46, 0x71, 0xe5, 0x0a, 0x30, 0x6d, 0x15, 0x15, - 0x78, 0xe5, 0xf6, 0xe0, 0x45, 0xbd, 0xe5, 0x87, 0x41, 0xda, 0x41, 0x86, 0x66, 0x1c, 0x94, 0xe7, 0x63, 0x15, 0xa7, 0xb4, 0x99, 0xbc, - 0x8c, 0xae, 0x0a, 0xce, 0x99, 0x46, 0x7a, 0x93, 0x33, 0xa9, 0x99, 0x01, 0x50, 0x62, 0x2f, 0x33, 0x20, 0x8f, 0x74, 0x53, 0xd9, 0xba, - 0x71, 0x79, 0x6a, 0x75, 0x69, 0xaa, 0xbd, 0x45, 0xa4, 0x2d, 0x23, 0x8c, 0x74, 0xb3, 0x8d, 0x30, 0xd2, 0x30, 0x82, 0x17, 0xe1, 0x2c, - 0x3b, 0xcd, 0x96, 0xfb, 0x59, 0xc6, 0xba, 0x99, 0x87, 0xd4, 0x32, 0x4f, 0xb9, 0x62, 0xb9, 0x1b, 0x72, 0x33, 0xea, 0x8d, 0x0f, 0x11, - 0x6b, 0x45, 0x22, 0x27, 0xb8, 0x81, 0x26, 0x26, 0x53, 0xd0, 0xc4, 0x3b, 0x6e, 0xf9, 0xb5, 0x6a, 0x08, 0xb7, 0x2a, 0x23, 0x34, 0x6b, - 0xf9, 0x13, 0xe8, 0x18, 0xc3, 0xd7, 0x78, 0x06, 0xb9, 0x23, 0xd4, 0x5b, 0x17, 0xa2, 0x53, 0xb8, 0x76, 0x19, 0x49, 0x9e, 0x6e, 0xf8, - 0x77, 0x61, 0x96, 0x19, 0x17, 0xb2, 0x87, 0x44, 0x94, 0x3a, 0x5c, 0x93, 0x4e, 0xca, 0x06, 0x31, 0x91, 0x98, 0x7b, 0x94, 0xc4, 0x2d, - 0x5f, 0x2d, 0x7f, 0x95, 0x0d, 0x34, 0xd1, 0x1c, 0xa2, 0x6d, 0x2b, 0x57, 0x80, 0x10, 0x3e, 0x5a, 0xe3, 0x9a, 0x40, 0x2b, 0x1f, 0x9b, - 0x71, 0x10, 0x74, 0x3b, 0xc8, 0x78, 0x32, 0xc1, 0x23, 0x69, 0x86, 0xdd, 0x18, 0x51, 0x9e, 0x4e, 0x1f, 0x81, 0xe1, 0x53, 0xae, 0x70, - 0xfe, 0xaa, 0xc5, 0xdf, 0x1d, 0xac, 0x24, 0xd9, 0x1c, 0xc2, 0xbd, 0x1f, 0x8d, 0x8f, 0xbd, 0x03, 0x3a, 0xe7, 0x37, 0x11, 0xa4, 0x58, - 0x58, 0x2f, 0x2b, 0x07, 0x8e, 0x89, 0x80, 0x8b, 0x83, 0x72, 0xea, 0xcd, 0x31, 0x81, 0x9b, 0xb0, 0x15, 0x91, 0xe5, 0xf9, 0x77, 0xe2, - 0x60, 0xca, 0x68, 0xd7, 0xbc, 0x8a, 0xd2, 0x39, 0x94, 0x8e, 0x23, 0x3a, 0x8b, 0x50, 0x76, 0xa2, 0x98, 0x64, 0x9e, 0xc2, 0x35, 0x89, - 0xae, 0xcc, 0xd1, 0x4f, 0x2b, 0x1f, 0x18, 0x4f, 0xd9, 0x9a, 0xc1, 0xa1, 0xeb, 0x2e, 0x3c, 0x98, 0xaa, 0x03, 0xf6, 0xbd, 0x4f, 0xdd, - 0x37, 0x1f, 0xd5, 0xca, 0x73, 0x06, 0x69, 0xe6, 0x67, 0xa6, 0xc5, 0x2a, 0xea, 0xd4, 0x74, 0x93, 0xe9, 0x87, 0x3b, 0xe4, 0x0d, 0xab, - 0xf2, 0x43, 0xd4, 0xb2, 0x2a, 0xa5, 0x6e, 0xfd, 0x4e, 0x2d, 0x72, 0xae, 0x6b, 0x2e, 0xb9, 0x0e, 0x12, 0xd5, 0x79, 0x4a, 0xbc, 0xe1, - 0xd4, 0x7d, 0x8b, 0x03, 0xc1, 0x30, 0x2d, 0x9f, 0xcc, 0x32, 0x4d, 0x59, 0xbc, 0x4e, 0xc3, 0x8a, 0xb3, 0xb3, 0x51, 0xdb, 0xb4, 0x33, - 0x2c, 0x08, 0x0c, 0x4f, 0xd4, 0x36, 0xf8, 0x6d, 0x75, 0x46, 0x38, 0x3d, 0xf1, 0xae, 0x27, 0x3f, 0xc8, 0x9d, 0xcc, 0x5a, 0x75, 0x40, - 0x2c, 0xeb, 0x4a, 0x9d, 0xf8, 0xfa, 0xca, 0xdc, 0xbc, 0xd5, 0x66, 0x07, 0x77, 0x81, 0x3c, 0x7a, 0x70, 0x7f, 0x38, 0xa7, 0x52, 0xe8, - 0x50, 0x42, 0x6f, 0x97, 0x23, 0x28, 0xfa, 0xd2, 0x1b, 0xc8, 0x94, 0x36, 0x60, 0x8b, 0xdc, 0x93, 0x59, 0x8d, 0x08, 0xdf, 0xbc, 0x39, - 0x27, 0x2d, 0xff, 0x7e, 0x29, 0x6c, 0x07, 0xdd, 0x4a, 0xd8, 0x2d, 0x94, 0x1a, 0x61, 0xbf, 0x10, 0x54, 0x83, 0x52, 0xa1, 0x11, 0xb6, - 0xab, 0x85, 0x76, 0x18, 0x56, 0xcb, 0xfd, 0xb0, 0x5c, 0xea, 0x75, 0x2a, 0x0f, 0xe0, 0x60, 0x91, 0x51, 0x5c, 0x0e, 0xd3, 0xeb, 0xfa, - 0x01, 0x5c, 0x61, 0xd0, 0x45, 0x76, 0x69, 0xaf, 0xc7, 0xd7, 0x2e, 0xee, 0xe3, 0xe5, 0x2d, 0xcd, 0x85, 0x11, 0x8b, 0x8b, 0x4c, 0x5f, - 0xcc, 0x17, 0xb5, 0xe1, 0xfa, 0xe2, 0xbe, 0x5c, 0xd9, 0x7c, 0x71, 0xef, 0x11, 0x20, 0x9d, 0xfb, 0xb5, 0xca, 0xa0, 0x59, 0x6d, 0x76, - 0x6a, 0x85, 0x66, 0xb5, 0x3d, 0x28, 0x04, 0xbd, 0x4e, 0xa3, 0xd0, 0xec, 0xd6, 0x3a, 0x85, 0x5e, 0xad, 0x5b, 0xef, 0x0d, 0x7a, 0xdd, - 0xb0, 0xd1, 0x1c, 0x3c, 0xf0, 0xbd, 0x23, 0x0d, 0x0e, 0xda, 0xd5, 0x6e, 0x50, 0xeb, 0x37, 0x0a, 0xb5, 0x72, 0xb7, 0x5b, 0x08, 0x6a, - 0x25, 0x65, 0x7e, 0xa3, 0x59, 0xa8, 0x07, 0x95, 0x4a, 0x3b, 0xa8, 0xb7, 0x1b, 0xfd, 0xa0, 0xfd, 0x20, 0x2b, 0x63, 0x60, 0xe5, 0x29, - 0x7d, 0x64, 0xbe, 0x00, 0xf7, 0x6a, 0xbb, 0xb6, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, - 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xff, 0x6b, 0x0c, 0xe9, 0xcd, 0x01, 0x00, 0x00, 0xb5, 0x03, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x73, 0x2f, 0x73, 0x68, 0x65, 0x65, 0x74, 0x31, - 0x2e, 0x78, 0x6d, 0x6c, 0x9c, 0x93, 0x4d, 0x8b, 0xdb, 0x30, 0x10, 0x86, 0xef, 0x85, 0xfe, 0x07, 0xa1, 0xbb, 0x2d, 0xdb, 0xf1, 0x3a, - 0x89, 0x89, 0xb3, 0x6c, 0x36, 0x0d, 0xdd, 0x43, 0xa1, 0xf4, 0xf3, 0x2c, 0xcb, 0x63, 0x5b, 0xc4, 0x92, 0x8c, 0xa4, 0x6c, 0x12, 0x4a, - 0xff, 0x7b, 0xc7, 0x0e, 0xf1, 0x16, 0x72, 0x09, 0x0b, 0x16, 0x68, 0xc6, 0x9a, 0x67, 0x66, 0xa4, 0x77, 0x56, 0x8f, 0x27, 0xd5, 0x91, - 0x57, 0xb0, 0x4e, 0x1a, 0x5d, 0xd0, 0x38, 0x8c, 0x28, 0x01, 0x2d, 0x4c, 0x25, 0x75, 0x53, 0xd0, 0x9f, 0x3f, 0x76, 0xc1, 0x82, 0x12, - 0xe7, 0xb9, 0xae, 0x78, 0x67, 0x34, 0x14, 0xf4, 0x0c, 0x8e, 0x3e, 0xae, 0x3f, 0x7e, 0x58, 0x1d, 0x8d, 0xdd, 0xbb, 0x16, 0xc0, 0x13, - 0x24, 0x68, 0x57, 0xd0, 0xd6, 0xfb, 0x3e, 0x67, 0xcc, 0x89, 0x16, 0x14, 0x77, 0xa1, 0xe9, 0x41, 0xe3, 0x9f, 0xda, 0x58, 0xc5, 0x3d, - 0x9a, 0xb6, 0x61, 0xae, 0xb7, 0xc0, 0xab, 0x31, 0x48, 0x75, 0x2c, 0x89, 0xa2, 0x8c, 0x29, 0x2e, 0x35, 0xbd, 0x10, 0x72, 0x7b, 0x0f, - 0xc3, 0xd4, 0xb5, 0x14, 0xb0, 0x35, 0xe2, 0xa0, 0x40, 0xfb, 0x0b, 0xc4, 0x42, 0xc7, 0x3d, 0xd6, 0xef, 0x5a, 0xd9, 0xbb, 0x2b, 0x4d, - 0x89, 0x7b, 0x70, 0x8a, 0xdb, 0xfd, 0xa1, 0x0f, 0x84, 0x51, 0x3d, 0x22, 0x4a, 0xd9, 0x49, 0x7f, 0x1e, 0xa1, 0x94, 0x28, 0x91, 0xbf, - 0x34, 0xda, 0x58, 0x5e, 0x76, 0xd8, 0xf7, 0x29, 0x4e, 0xb9, 0x20, 0x27, 0x8b, 0x5f, 0x82, 0x6b, 0x76, 0x4d, 0x33, 0xfa, 0x6f, 0x32, - 0x29, 0x29, 0xac, 0x71, 0xa6, 0xf6, 0x21, 0x92, 0xd9, 0xa5, 0xe6, 0xdb, 0xf6, 0x97, 0x6c, 0xc9, 0xb8, 0x98, 0x48, 0xb7, 0xfd, 0xdf, - 0x85, 0x89, 0x53, 0x66, 0xe1, 0x55, 0x0e, 0x0f, 0xf8, 0x86, 0x4a, 0xde, 0x57, 0x52, 0xfc, 0x30, 0xb1, 0x92, 0x37, 0xd8, 0xec, 0x9d, - 0xb0, 0x6c, 0x82, 0x0d, 0xd7, 0x65, 0xf3, 0x83, 0xac, 0x0a, 0xfa, 0x27, 0x4a, 0x97, 0xbb, 0x4d, 0xbc, 0x49, 0x83, 0xe4, 0x29, 0x8b, - 0x83, 0x34, 0x4b, 0x67, 0xc1, 0x62, 0x11, 0x67, 0xc1, 0x62, 0x33, 0xcf, 0x3e, 0x3d, 0xcf, 0xb2, 0xdd, 0x36, 0xdd, 0xfc, 0xa5, 0xeb, - 0x55, 0x25, 0xf1, 0x85, 0x87, 0xae, 0x88, 0x85, 0xba, 0xa0, 0x4f, 0x31, 0x65, 0xeb, 0xd5, 0x28, 0x9e, 0x5f, 0x12, 0x8e, 0xee, 0xbf, - 0x3d, 0xf1, 0xbc, 0xfc, 0x0e, 0x1d, 0x08, 0x0f, 0x98, 0x20, 0xa6, 0x64, 0xd0, 0x66, 0x69, 0xcc, 0x7e, 0x38, 0xf8, 0x82, 0xae, 0x68, - 0x08, 0x65, 0x37, 0xb1, 0xbb, 0x51, 0x9b, 0x5f, 0x2d, 0x29, 0xb9, 0x83, 0x67, 0xd3, 0xfd, 0x96, 0x95, 0x6f, 0x11, 0x80, 0x33, 0x50, - 0x41, 0xcd, 0x0f, 0x9d, 0xff, 0x66, 0x8e, 0x9f, 0x41, 0x36, 0xad, 0x47, 0x6f, 0x86, 0x3d, 0x0c, 0x22, 0xc8, 0xab, 0xf3, 0x16, 0x9c, - 0x40, 0xf5, 0x21, 0x38, 0x4c, 0xa6, 0xaa, 0xb6, 0xdc, 0x73, 0x4c, 0xd3, 0xf3, 0x06, 0xbe, 0x70, 0xdb, 0x48, 0xed, 0x48, 0x07, 0xf5, - 0x78, 0x68, 0x4e, 0x89, 0xbd, 0x50, 0xa2, 0x10, 0xf7, 0xde, 0xf4, 0x43, 0xe8, 0xfc, 0x81, 0x92, 0xd2, 0x78, 0x6f, 0xd4, 0xd5, 0x6a, - 0x71, 0x3c, 0x00, 0x65, 0x10, 0x85, 0x78, 0x61, 0xb5, 0x31, 0xfe, 0x6a, 0x0c, 0xe5, 0x4f, 0x03, 0xb7, 0xfe, 0x07, 0x00, 0x00, 0xff, - 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc2, 0x5e, 0x59, 0x08, 0x90, - 0x01, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00, 0x10, 0x00, 0x08, 0x01, 0x64, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x2f, 0x61, 0x70, - 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x20, 0xa2, 0x04, 0x01, 0x28, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x92, 0x4d, 0x6f, 0xdb, 0x30, 0x0c, 0x86, 0xef, 0x03, 0xfa, 0x1f, 0x0c, 0xdd, 0x1b, 0x39, 0x6d, - 0x51, 0x0c, 0x81, 0xac, 0x62, 0x48, 0x57, 0xf4, 0xb0, 0x62, 0x01, 0x92, 0x76, 0x67, 0x4e, 0xa6, 0x63, 0xa1, 0xb2, 0x24, 0x88, 0xac, - 0x91, 0xec, 0xd7, 0x4f, 0xb6, 0xd1, 0xd4, 0xd9, 0x76, 0xda, 0x8d, 0x1f, 0x2f, 0x5e, 0x3e, 0xa2, 0xa8, 0xee, 0x0e, 0x9d, 0x2b, 0x7a, - 0x4c, 0x64, 0x83, 0xaf, 0xc4, 0x72, 0x51, 0x8a, 0x02, 0xbd, 0x09, 0xb5, 0xf5, 0xfb, 0x4a, 0x3c, 0xef, 0x1e, 0x2e, 0x3f, 0x8b, 0x82, - 0x18, 0x7c, 0x0d, 0x2e, 0x78, 0xac, 0xc4, 0x11, 0x49, 0xdc, 0xe9, 0x8b, 0x4f, 0x6a, 0x93, 0x42, 0xc4, 0xc4, 0x16, 0xa9, 0xc8, 0x16, - 0x9e, 0x2a, 0xd1, 0x32, 0xc7, 0x95, 0x94, 0x64, 0x5a, 0xec, 0x80, 0x16, 0xb9, 0xed, 0x73, 0xa7, 0x09, 0xa9, 0x03, 0xce, 0x69, 0xda, - 0xcb, 0xd0, 0x34, 0xd6, 0xe0, 0x7d, 0x30, 0x6f, 0x1d, 0x7a, 0x96, 0x57, 0x65, 0x79, 0x2b, 0xf1, 0xc0, 0xe8, 0x6b, 0xac, 0x2f, 0xe3, - 0xc9, 0x50, 0x4c, 0x8e, 0xab, 0x9e, 0xff, 0xd7, 0xb4, 0x0e, 0x66, 0xe0, 0xa3, 0x97, 0xdd, 0x31, 0x66, 0x60, 0xad, 0xbe, 0xc4, 0xe8, - 0xac, 0x01, 0xce, 0xaf, 0xd4, 0x4f, 0xd6, 0xa4, 0x40, 0xa1, 0xe1, 0xe2, 0x09, 0x8c, 0xf5, 0x1c, 0xa8, 0x2d, 0xbe, 0x1e, 0x0c, 0x3a, - 0x25, 0xe7, 0x32, 0x95, 0x39, 0xb7, 0x68, 0xde, 0x92, 0xe5, 0xa3, 0x2e, 0x95, 0x9c, 0xa7, 0x6a, 0x6b, 0xc0, 0xe1, 0x3a, 0x8f, 0xd0, - 0x0d, 0x38, 0x42, 0x25, 0x3f, 0x0a, 0xea, 0x11, 0x61, 0x58, 0xdf, 0x06, 0x6c, 0x22, 0xad, 0x7a, 0x5e, 0xf5, 0x68, 0x38, 0xa4, 0x82, - 0xec, 0xaf, 0xbc, 0xc0, 0x2b, 0x51, 0xfc, 0x04, 0xc2, 0x01, 0xac, 0x12, 0x3d, 0x24, 0x0b, 0x9e, 0x33, 0xe0, 0x20, 0x9b, 0x92, 0x31, - 0x76, 0x91, 0x38, 0xe9, 0x1f, 0x21, 0xbd, 0x52, 0x8b, 0xc8, 0xa4, 0x64, 0x16, 0x4c, 0xc5, 0x31, 0x9c, 0x6b, 0xe7, 0xb1, 0xbd, 0xd1, - 0xcb, 0x51, 0x90, 0x83, 0x73, 0xe1, 0x60, 0x30, 0x81, 0xe4, 0xc6, 0x39, 0xe2, 0xce, 0xb2, 0x43, 0xfa, 0xde, 0x6c, 0x20, 0xf1, 0x3f, - 0x88, 0x97, 0x73, 0xe2, 0x91, 0x61, 0xe2, 0x9d, 0x70, 0xb6, 0x03, 0xdf, 0x34, 0x73, 0xce, 0x37, 0x3e, 0x39, 0x4f, 0xfa, 0xc3, 0x7b, - 0x1d, 0xba, 0x08, 0xfe, 0x98, 0x1b, 0xa7, 0xe8, 0x9b, 0xf5, 0xaf, 0xf4, 0x1c, 0x77, 0xe1, 0x1e, 0x18, 0xdf, 0xd7, 0x79, 0x5e, 0x54, - 0xdb, 0x16, 0x12, 0xd6, 0xf9, 0x07, 0x4e, 0xeb, 0x3e, 0x15, 0xd4, 0x63, 0xde, 0x64, 0x72, 0x83, 0xc9, 0xba, 0x05, 0xbf, 0xc7, 0xfa, - 0x5d, 0xf3, 0x77, 0x63, 0x38, 0x83, 0x97, 0xe9, 0xd6, 0xf5, 0xf2, 0x76, 0x51, 0x5e, 0x97, 0xf9, 0x5f, 0x67, 0x35, 0x25, 0x3f, 0xae, - 0x5a, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, - 0x00, 0xbd, 0xde, 0xac, 0xb8, 0x45, 0x01, 0x00, 0x00, 0x6f, 0x02, 0x00, 0x00, 0x11, 0x00, 0x08, 0x01, 0x64, 0x6f, 0x63, 0x50, 0x72, - 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x78, 0x6d, 0x6c, 0x20, 0xa2, 0x04, 0x01, 0x28, 0xa0, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x92, 0x5f, 0x4b, 0xc3, 0x30, 0x14, 0xc5, 0xdf, 0x05, - 0xbf, 0x43, 0xc9, 0x7b, 0x9b, 0x66, 0x1b, 0x63, 0x96, 0xb6, 0x83, 0x29, 0x7b, 0xd1, 0x81, 0xe0, 0x44, 0xf1, 0x2d, 0x24, 0x77, 0x5b, - 0xb0, 0xf9, 0x43, 0x12, 0xd7, 0xed, 0xdb, 0x9b, 0xb6, 0x5b, 0xad, 0xd4, 0x17, 0x1f, 0x93, 0x73, 0xee, 0x2f, 0xe7, 0x5c, 0x92, 0x2f, - 0x4f, 0xb2, 0x8a, 0x8e, 0x60, 0x9d, 0xd0, 0xaa, 0x40, 0x24, 0x49, 0x51, 0x04, 0x8a, 0x69, 0x2e, 0xd4, 0xbe, 0x40, 0xaf, 0xdb, 0x75, - 0xbc, 0x40, 0x91, 0xf3, 0x54, 0x71, 0x5a, 0x69, 0x05, 0x05, 0x3a, 0x83, 0x43, 0xcb, 0xf2, 0xf6, 0x26, 0x67, 0x26, 0x63, 0xda, 0xc2, - 0xb3, 0xd5, 0x06, 0xac, 0x17, 0xe0, 0xa2, 0x40, 0x52, 0x2e, 0x63, 0xa6, 0x40, 0x07, 0xef, 0x4d, 0x86, 0xb1, 0x63, 0x07, 0x90, 0xd4, - 0x25, 0xc1, 0xa1, 0x82, 0xb8, 0xd3, 0x56, 0x52, 0x1f, 0x8e, 0x76, 0x8f, 0x0d, 0x65, 0x9f, 0x74, 0x0f, 0x78, 0x92, 0xa6, 0x73, 0x2c, - 0xc1, 0x53, 0x4e, 0x3d, 0xc5, 0x0d, 0x30, 0x36, 0x3d, 0x11, 0x5d, 0x90, 0x9c, 0xf5, 0x48, 0xf3, 0x65, 0xab, 0x16, 0xc0, 0x19, 0x86, - 0x0a, 0x24, 0x28, 0xef, 0x30, 0x49, 0x08, 0xfe, 0xf1, 0x7a, 0xb0, 0xd2, 0xfd, 0x39, 0xd0, 0x2a, 0x03, 0xa7, 0x14, 0xfe, 0x6c, 0x42, - 0xa7, 0x4b, 0xdc, 0x21, 0x9b, 0xb3, 0x4e, 0xec, 0xdd, 0x27, 0x27, 0x7a, 0x63, 0x5d, 0xd7, 0x49, 0x3d, 0x6d, 0x63, 0x84, 0xfc, 0x04, - 0xbf, 0x6f, 0x9e, 0x5e, 0xda, 0xaa, 0xb1, 0x50, 0xcd, 0xae, 0x18, 0xa0, 0x32, 0xe7, 0x2c, 0x63, 0x16, 0xa8, 0xd7, 0xb6, 0x7c, 0x04, - 0xa5, 0xc0, 0x1f, 0xa2, 0x15, 0xad, 0x5c, 0x05, 0xc7, 0x1c, 0x0f, 0xb4, 0x66, 0x8f, 0x15, 0x75, 0x7e, 0x13, 0x56, 0xbe, 0x13, 0xc0, - 0x57, 0xe7, 0xb1, 0x7d, 0x6c, 0x09, 0xf4, 0xb6, 0x4c, 0xf7, 0x04, 0xf0, 0x28, 0xc4, 0xcb, 0xba, 0x32, 0x57, 0xe5, 0x6d, 0x7a, 0xff, - 0xb0, 0x5d, 0xa3, 0x72, 0x92, 0x92, 0xbb, 0x38, 0x5d, 0xc4, 0x64, 0xbe, 0x4d, 0xd3, 0x6c, 0x3a, 0xcb, 0xc8, 0xec, 0xa3, 0x49, 0xf0, - 0x6b, 0xbe, 0x89, 0xdb, 0x5d, 0xc8, 0x4b, 0x8e, 0xff, 0x10, 0x27, 0xf3, 0x01, 0xf1, 0x0a, 0x28, 0x73, 0x3c, 0xfa, 0x22, 0xe5, 0x37, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x9a, 0xab, 0x0f, 0x4f, 0x5e, - 0xff, 0xec, 0xaa, 0x93, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x35, 0x8d, 0x41, 0x0a, 0xc2, 0x30, 0x10, 0x45, 0xf7, - 0x82, 0x77, 0x08, 0xb3, 0xd7, 0xa9, 0x2e, 0x44, 0x24, 0x49, 0x17, 0x82, 0x27, 0xd0, 0x03, 0x84, 0x76, 0x6c, 0x03, 0xcd, 0xa4, 0x66, - 0xa6, 0xa2, 0xb7, 0x37, 0x5d, 0xb8, 0xf8, 0xf0, 0x1f, 0x9f, 0xcf, 0xb3, 0xed, 0x27, 0x4d, 0xe6, 0x4d, 0x45, 0x62, 0x66, 0x07, 0x87, - 0x7d, 0x03, 0x86, 0xb8, 0xcb, 0x7d, 0xe4, 0xc1, 0xc1, 0xe3, 0x7e, 0xdb, 0x9d, 0xc1, 0x88, 0x06, 0xee, 0xc3, 0x94, 0x99, 0x1c, 0x7c, - 0x49, 0xa0, 0xf5, 0xdb, 0x8d, 0x15, 0x51, 0x53, 0xbf, 0x2c, 0x0e, 0x46, 0xd5, 0xf9, 0x82, 0x28, 0xdd, 0x48, 0x29, 0xc8, 0x3e, 0xcf, - 0xc4, 0x75, 0x79, 0xe6, 0x92, 0x82, 0x56, 0x2c, 0x03, 0xca, 0x5c, 0x28, 0xf4, 0x32, 0x12, 0x69, 0x9a, 0xf0, 0xd8, 0x34, 0x27, 0x4c, - 0x21, 0x32, 0x98, 0x2e, 0x2f, 0xac, 0xd5, 0x0b, 0x66, 0xe1, 0xf8, 0x5a, 0xe8, 0xfa, 0x67, 0x6f, 0x25, 0x7a, 0xab, 0xde, 0xe2, 0x9a, - 0xb5, 0x63, 0x35, 0x56, 0xf1, 0x0f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x99, 0x64, 0xef, 0x50, 0xd9, 0x19, - 0xd7, 0x73, 0x3f, 0x01, 0x00, 0x00, 0x7e, 0x04, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x5f, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5d, 0x2e, 0x78, 0x6d, 0x6c, 0xad, 0x93, 0xcd, 0x4e, 0xc3, 0x30, 0x10, 0x84, 0xef, 0x3c, 0x45, - 0xe4, 0x2b, 0x4a, 0xdc, 0x72, 0x40, 0x08, 0x35, 0xed, 0x81, 0x9f, 0x23, 0x54, 0xa2, 0x3c, 0x80, 0xb1, 0x37, 0x8d, 0xd5, 0xf8, 0x47, - 0xbb, 0x6e, 0x69, 0xdf, 0x9e, 0x4d, 0x52, 0x21, 0x40, 0x55, 0x03, 0xb4, 0x97, 0x58, 0xf1, 0xce, 0xcc, 0x37, 0x3e, 0xec, 0x64, 0xb6, - 0x75, 0x4d, 0xb6, 0x01, 0x24, 0x1b, 0x7c, 0x29, 0xc6, 0xc5, 0x48, 0x64, 0xe0, 0x75, 0x30, 0xd6, 0x2f, 0x4b, 0xf1, 0xba, 0x78, 0xcc, - 0x6f, 0xc4, 0x6c, 0x3a, 0x59, 0xec, 0x22, 0x50, 0xc6, 0x52, 0x4f, 0xa5, 0xa8, 0x53, 0x8a, 0xb7, 0x52, 0x92, 0xae, 0xc1, 0x29, 0x2a, - 0x42, 0x04, 0xcf, 0x93, 0x2a, 0xa0, 0x53, 0x89, 0x7f, 0x71, 0x29, 0xa3, 0xd2, 0x2b, 0xb5, 0x04, 0x79, 0x35, 0x1a, 0x5d, 0x4b, 0x1d, - 0x7c, 0x02, 0x9f, 0xf2, 0xd4, 0x66, 0x88, 0xe9, 0xe4, 0x1e, 0x2a, 0xb5, 0x6e, 0x52, 0xf6, 0xb0, 0xe5, 0xeb, 0x1e, 0x8b, 0xd0, 0x90, - 0xc8, 0xee, 0x7a, 0x61, 0xcb, 0x2a, 0x85, 0x8a, 0xb1, 0xb1, 0x5a, 0x25, 0x9e, 0xcb, 0x8d, 0x37, 0x3f, 0x28, 0xf9, 0x9e, 0x50, 0xb0, - 0xb3, 0xd3, 0x50, 0x6d, 0x23, 0x5d, 0xb2, 0x40, 0xc8, 0x83, 0x04, 0x9e, 0x1c, 0x01, 0xec, 0x7d, 0xcf, 0x1b, 0x40, 0xb4, 0x06, 0xb2, - 0xb9, 0xc2, 0xf4, 0xa4, 0x1c, 0xab, 0xe4, 0xb6, 0x91, 0xef, 0x01, 0x57, 0x6f, 0x21, 0xac, 0x0a, 0x96, 0xfd, 0xad, 0x65, 0xa8, 0x2a, - 0xab, 0xc1, 0x04, 0xbd, 0x76, 0x6c, 0x29, 0x28, 0x22, 0x28, 0x43, 0x35, 0x40, 0x72, 0x4d, 0xd1, 0x9d, 0x85, 0x53, 0xd6, 0x5f, 0x0e, - 0xf3, 0x3b, 0x31, 0xc9, 0xee, 0x18, 0x9f, 0xb9, 0xc8, 0x67, 0xfe, 0x40, 0x8f, 0x54, 0x83, 0x83, 0xfe, 0x7b, 0x7a, 0x85, 0x2e, 0x66, - 0x00, 0x48, 0x69, 0xd7, 0x00, 0x9d, 0x8c, 0xfa, 0xfe, 0xda, 0x3e, 0x74, 0x88, 0x5c, 0x2b, 0x04, 0xf3, 0x92, 0x90, 0xd7, 0xe0, 0xec, - 0x05, 0xbe, 0x64, 0x1f, 0xed, 0xc1, 0xfe, 0x39, 0x86, 0x48, 0x52, 0x07, 0x84, 0xb6, 0xc4, 0xff, 0x56, 0xa4, 0x75, 0xe7, 0x11, 0x79, - 0x8a, 0xc9, 0xc2, 0xef, 0x88, 0x1c, 0x7d, 0xf2, 0xab, 0xa1, 0xdd, 0x3e, 0x03, 0xe6, 0x00, 0x5b, 0xb6, 0x79, 0x34, 0xbd, 0xf8, 0x00, - 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xa7, 0x64, 0xef, 0x50, 0x59, 0xaf, 0xf9, 0x51, 0xdc, 0x00, 0x00, 0x00, - 0xa8, 0x02, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, - 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0xad, 0x92, 0xcd, 0x6a, 0xc3, 0x30, 0x10, 0x84, 0xef, 0x7d, - 0x0a, 0xb1, 0xf7, 0x5a, 0x76, 0x5a, 0x4a, 0x29, 0x51, 0x72, 0x29, 0x85, 0x5c, 0xdb, 0xf4, 0x01, 0x84, 0xbc, 0xb6, 0x4c, 0x6c, 0x49, - 0xec, 0x6e, 0x7f, 0xf2, 0xf6, 0x11, 0x0e, 0x24, 0x31, 0x84, 0x90, 0x83, 0x4f, 0x62, 0x46, 0xda, 0x99, 0x4f, 0xb0, 0xcb, 0xf5, 0xff, - 0xd0, 0xab, 0x5f, 0x24, 0xee, 0x62, 0x30, 0x50, 0x15, 0x25, 0x28, 0x0c, 0x2e, 0xd6, 0x5d, 0x68, 0x0d, 0x7c, 0x6f, 0x3f, 0x1e, 0x5f, - 0x61, 0xbd, 0x5a, 0x7e, 0x62, 0x6f, 0x25, 0xbf, 0x60, 0xdf, 0x25, 0x56, 0x79, 0x24, 0xb0, 0x01, 0x2f, 0x92, 0xde, 0xb4, 0x66, 0xe7, - 0x71, 0xb0, 0x5c, 0xc4, 0x84, 0x21, 0xdf, 0x34, 0x91, 0x06, 0x2b, 0x59, 0x52, 0xab, 0x93, 0x75, 0x3b, 0xdb, 0xa2, 0x5e, 0x94, 0xe5, - 0x8b, 0xa6, 0xcb, 0x0c, 0x98, 0x66, 0xaa, 0x4d, 0x6d, 0x80, 0x36, 0xf5, 0x33, 0xa8, 0xed, 0x3e, 0xe1, 0x3d, 0xd9, 0xb1, 0x69, 0x3a, - 0x87, 0xef, 0xd1, 0xfd, 0x0c, 0x18, 0xe4, 0x4a, 0x85, 0x66, 0x6f, 0x09, 0xeb, 0x2f, 0xa1, 0xfc, 0x17, 0xce, 0xc1, 0x96, 0x5a, 0x14, - 0x03, 0x13, 0xbb, 0xc8, 0xa9, 0xa0, 0xaf, 0xc3, 0x3c, 0xcd, 0x0a, 0x23, 0xfb, 0x1e, 0x2f, 0x28, 0x46, 0x7d, 0xb3, 0x7e, 0x31, 0x67, - 0xbd, 0xe4, 0x59, 0x3c, 0xb7, 0x8f, 0xf2, 0x68, 0x56, 0xb7, 0x18, 0xaa, 0x39, 0x19, 0xfe, 0x22, 0xed, 0xd8, 0x23, 0xca, 0x99, 0xe3, - 0x64, 0xb1, 0x1e, 0x8f, 0x13, 0x8c, 0x9e, 0x6c, 0xdc, 0xea, 0xe1, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xb5, 0x55, 0x30, 0x23, 0xf4, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x2e, 0x72, - 0x65, 0x6c, 0x73, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0x47, 0x88, 0xbc, - 0xe2, 0x5d, 0x03, 0x00, 0x00, 0x35, 0x08, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x25, 0x03, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, - 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xf0, 0x08, 0x58, 0xf4, 0xa5, 0x02, 0x00, 0x00, - 0x52, 0x06, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x06, 0x00, 0x00, - 0x78, 0x6c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc1, 0x17, 0x10, 0xbe, 0x4e, 0x07, 0x00, 0x00, 0xc6, 0x20, 0x00, 0x00, 0x13, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x09, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x74, 0x68, 0x65, 0x6d, - 0x65, 0x2f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xff, 0x6b, 0x0c, 0xe9, 0xcd, 0x01, 0x00, 0x00, 0xb5, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x10, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x68, 0x65, 0x65, 0x74, 0x73, 0x2f, 0x73, 0x68, 0x65, 0x65, 0x74, 0x31, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, - 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xc2, 0x5e, 0x59, 0x08, 0x90, 0x01, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x64, 0x6f, 0x63, 0x50, - 0x72, 0x6f, 0x70, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x2d, 0x00, 0x14, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0xbd, 0xde, 0xac, 0xb8, 0x45, 0x01, 0x00, 0x00, 0x6f, 0x02, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x15, 0x00, 0x00, 0x64, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x73, - 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x9a, - 0xab, 0x0f, 0x4f, 0x5e, 0xff, 0xec, 0xaa, 0x93, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x43, 0x18, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x99, - 0x64, 0xef, 0x50, 0xd9, 0x19, 0xd7, 0x73, 0x3f, 0x01, 0x00, 0x00, 0x7e, 0x04, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x08, 0x19, 0x00, 0x00, 0x5b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x54, 0x79, - 0x70, 0x65, 0x73, 0x5d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xa7, 0x64, - 0xef, 0x50, 0x59, 0xaf, 0xf9, 0x51, 0xdc, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xb6, 0x81, 0x78, 0x1a, 0x00, 0x00, 0x78, 0x6c, 0x2f, 0x5f, 0x72, 0x65, 0x6c, 0x73, 0x2f, 0x77, 0x6f, 0x72, - 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x80, 0x02, 0x00, 0x00, 0x8c, 0x1b, 0x00, 0x00, 0x00, 0x00 - }; - -} // namespace - XLDocument::XLDocument(const IZipArchive& zipArchive) : m_archive(zipArchive) {} /** @@ -454,12 +101,18 @@ void XLDocument::open(const std::string& fileName) m_data.emplace_back(this, "_rels/.rels"); m_data.emplace_back(this, "xl/_rels/workbook.xml.rels"); - m_contentTypes = XLContentTypes(getXmlData("[Content_Types].xml")); - m_docRelationships = XLRelationships(getXmlData("_rels/.rels")); - m_wbkRelationships = XLRelationships(getXmlData("xl/_rels/workbook.xml.rels")); + m_contentTypes = XLContentTypes(getXmlDataByPath("[Content_Types].xml")); + m_docRelationships = XLRelationships(getXmlDataByPath("_rels/.rels")); + m_wbkRelationships = XLRelationships(getXmlDataByPath("xl/_rels/workbook.xml.rels")); if (!m_archive.hasEntry("xl/sharedStrings.xml")) execCommand(XLCommand(XLCommandType::AddSharedStrings)); + + // Add /xl/workbook.xml entry if the file didn't have one + auto items = m_contentTypes.getContentItems(); + if(std::find_if(items.begin(), items.end(), [&](const XLContentItem& item) + { return item.path() == "/xl/workbook.xml"; }) == items.end()) + m_contentTypes.addOverride("/xl/workbook.xml", XLContentType::Workbook); // ===== Add remaining spreadsheet elements to the vector of XLXmlData objects. for (auto& item : m_contentTypes.getContentItems()) { @@ -467,37 +120,80 @@ void XLDocument::open(const std::string& fileName) m_data.emplace_back(/* parentDoc */ this, /* xmlPath */ item.path().substr(1), /* xmlID */ m_wbkRelationships.relationshipByTarget(item.path().substr(4)).id(), + /* name */ std::string(), /* xmlType */ item.type()); - else m_data.emplace_back(/* parentDoc */ this, /* xmlPath */ item.path().substr(1), /* xmlID */ m_docRelationships.relationshipByTarget(item.path().substr(1)).id(), + /* name */ std::string(), /* xmlType */ item.type()); } - for (const auto& node : getXmlData("xl/sharedStrings.xml")->getXmlDocument()->document_element().children()){ - if (std::string(node.first_child().name()) == "r") { - std::string result; - for (const auto& elem : node.children()) - result += elem.child("t").text().get(); - m_sharedStringCache.emplace_back(result); - } - - else - m_sharedStringCache.emplace_back(node.first_child().text().get()); - - } + m_XmlWorkbook = getXmlDataByPath("xl/workbook.xml"); + m_XmlWorkbook->setName("workbook"); + m_workbook = XLWorkbook(m_XmlWorkbook); + + // load worksheets/_rels/sheet{0}.xml.rels if any and connect nodes + for (auto& wsItem : m_data){ + if (wsItem.getXmlType() == XLContentType::Worksheet){ + + // Connect worksheets to workbook + m_XmlWorkbook->addChildNode(&wsItem); + wsItem.setParentNode(m_XmlWorkbook); + + // Retrieve sheet name and set it + wsItem.setName(m_XmlWorkbook->getXmlDocument()->document_element().child("sheets") + .find_child_by_attribute("r:id", wsItem.getXmlID().c_str()) + .attribute("name").value()); + + std::string sheetRelsPath = getSheetRelsPath(wsItem.getName()); + if (m_archive.hasEntry(sheetRelsPath)){ // there is a xl/worksheets/_rels/sheet{0}.xml.rels + + // Adding the sheet{0}.xml.rels + XLXmlData* pRels = &(m_data.emplace_back(/* parentDoc */ this, + /* xmlPath */ sheetRelsPath, + /* xmlID */ std::string(), + /* name */ std::string(), + /* xmlType */ XLContentType::WorksheetRelations, + /*parentNode */ &wsItem )); + + wsItem.addChildNode(pRels); + + auto sheetRels = XLRelationships(pRels); + // Loop throught the sheet relations to connect childrens + for (auto& childItem : sheetRels.relationships()){ + std::string targetPath = childItem.target(); + // Some may be relative path + if (targetPath.substr(0,2) == "..") + targetPath = "xl" + targetPath.substr(2); + + if (childItem.type() == XLRelationshipType::Table){ + XLXmlData* pDocChild = getXmlDataByPath(targetPath); // Pointer to table elmnt + + pDocChild->setName(pDocChild->getXmlDocument()->child("table") + .attribute("name").value()); + wsItem.addChildNode(pDocChild); + pDocChild->setParentNode(&wsItem); + pDocChild->setXmlID(sheetRels.relationshipByTarget(childItem.target()).id()); + } // childItem.type() could also be XLRelationshipType::PrinterSettings + } + + } + + } // if XLContentType::Worksheet + } // for m_data // ===== Open the workbook and document property items // TODO: If property data doesn't exist, consider creating them, instead of ignoring it. - m_coreProperties = (hasXmlData("docProps/core.xml") ? XLProperties(getXmlData("docProps/core.xml")) : XLProperties()); - m_appProperties = (hasXmlData("docProps/app.xml") ? XLAppProperties(getXmlData("docProps/app.xml")) : XLAppProperties()); - m_sharedStrings = XLSharedStrings(getXmlData("xl/sharedStrings.xml"), &m_sharedStringCache); - m_workbook = XLWorkbook(getXmlData("xl/workbook.xml")); + m_coreProperties = (hasXmlData("docProps/core.xml") ? XLProperties(getXmlDataByPath("docProps/core.xml")) : XLProperties()); + m_appProperties = (hasXmlData("docProps/app.xml") ? XLAppProperties(getXmlDataByPath("docProps/app.xml")) : XLAppProperties()); + m_sharedStrings = XLSharedStrings(getXmlDataByPath("xl/sharedStrings.xml")); + m_styles = XLStyles(getXmlDataByPath("xl/styles.xml")); } + /** * @details Create a new document. This is done by saving the data in XLTemplate.h in binary format. */ @@ -508,7 +204,7 @@ void XLDocument::create(const std::string& fileName) // ===== Stream the binary data for an empty workbook to the output file. // ===== Casting, in particular reinterpret_cast, is discouraged, but in this case it is unfortunately unavoidable. - outfile.write(reinterpret_cast(templateData), templateSize); // NOLINT + outfile.write(reinterpret_cast(XLTemplate::templateData), XLTemplate::templateSize); // NOLINT outfile.close(); open(fileName); @@ -553,7 +249,8 @@ void XLDocument::saveAs(const std::string& fileName) execCommand(XLCommand(XLCommandType::ResetCalcChain)); // ===== Add all xml items to archive and save the archive. - for (auto& item : m_data) m_archive.addEntry(item.getXmlPath(), item.getRawData()); + for (auto& item : m_data) + m_archive.addEntry(item.getXmlPath(), item.getRawData()); m_archive.save(m_filePath); } @@ -816,50 +513,41 @@ void XLDocument::execCommand(const XLCommand& command) { break; case XLCommandType::AddSharedStrings: { - std::string sharedStrings { - "\n" - "\n" - " \n" - " \n" - " \n" - "" - }; m_contentTypes.addOverride("/xl/sharedStrings.xml", XLContentType::SharedStrings); m_wbkRelationships.addRelationship(XLRelationshipType::SharedStrings, "sharedStrings.xml"); - m_archive.addEntry("xl/sharedStrings.xml", sharedStrings); + m_archive.addEntry("xl/sharedStrings.xml", XLTemplate::sharedStrings); } break; case XLCommandType::AddWorksheet: { - std::string emptyWorksheet { - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - }; - m_contentTypes.addOverride(command.getParam("sheetPath"), XLContentType::Worksheet); - m_wbkRelationships.addRelationship(XLRelationshipType::Worksheet, command.getParam("sheetPath").substr(4)); + auto internalID = availableFileID(XLContentType::Worksheet); + auto sheetID = availableSheetID(); + std::string sheetPath = "xl/worksheets/sheet" + std::to_string(internalID) + ".xml"; + m_contentTypes.addOverride("/" + sheetPath, XLContentType::Worksheet); + m_wbkRelationships.addRelationship(XLRelationshipType::Worksheet, sheetPath.substr(3)); m_appProperties.appendSheetName(command.getParam("sheetName")); - m_archive.addEntry(command.getParam("sheetPath").substr(1), emptyWorksheet); + m_archive.addEntry(sheetPath, XLTemplate::emptyWorksheet); m_data.emplace_back( /* parentDoc */ this, - /* xmlPath */ command.getParam("sheetPath").substr(1), - /* xmlID */ m_wbkRelationships.relationshipByTarget(command.getParam("sheetPath").substr(4)).id(), + /* xmlPath */ sheetPath, + /* xmlID */ m_wbkRelationships.relationshipByTarget(sheetPath.substr(3)).id(), + /* xmlID */ command.getParam("sheetName"), /* xmlType */ XLContentType::Worksheet); + + // TODO move elsewhere ? + m_workbook.prepareSheetMetadata(command.getParam("sheetName"), internalID, sheetID); } break; case XLCommandType::AddChartsheet: // TODO: To be implemented break; + case XLCommandType::AddTable: + { + createTable(command.getParam("worksheet"), + command.getParam("tableName"), + command.getParam("reference")); + } + break; case XLCommandType::DeleteSheet: { m_appProperties.deleteSheetName(command.getParam("sheetName")); @@ -872,9 +560,16 @@ void XLDocument::execCommand(const XLCommand& command) { })); } break; + case XLCommandType::DeleteTable: + { + deleteTable(command.getParam("tableName")); + } + // TODO: To be implemented + break; case XLCommandType::CloneSheet: { - auto internalID = m_workbook.createInternalSheetID(); + auto internalID = availableFileID(XLContentType::Worksheet); + auto sheetID = availableSheetID(); auto sheetPath = "/xl/worksheets/sheet" + std::to_string(internalID) + ".xml"; if (m_workbook.sheetExists(command.getParam("cloneName"))) throw XLInternalError("Sheet named \"" + command.getParam("cloneName") + "\" already exists."); @@ -892,6 +587,7 @@ void XLDocument::execCommand(const XLCommand& command) { /* parentDoc */ this, /* xmlPath */ sheetPath.substr(1), /* xmlID */ m_wbkRelationships.relationshipByTarget(sheetPath.substr(4)).id(), + /* name */ command.getParam("cloneName"), /* xmlType */ XLContentType::Worksheet); } else { @@ -907,10 +603,11 @@ void XLDocument::execCommand(const XLCommand& command) { /* parentDoc */ this, /* xmlPath */ sheetPath.substr(1), /* xmlID */ m_wbkRelationships.relationshipByTarget(sheetPath.substr(4)).id(), + /* name */ command.getParam("cloneName"), /* xmlType */ XLContentType::Chartsheet); } - m_workbook.prepareSheetMetadata(command.getParam("cloneName"), internalID); + m_workbook.prepareSheetMetadata(command.getParam("cloneName"), internalID, sheetID); } break; } @@ -951,13 +648,57 @@ XLQuery XLDocument::execQuery(const XLQuery& query) const return XLQuery(query).setResult(m_wbkRelationships.relationshipById(query.getParam("sheetID")).target()); case XLQueryType::QuerySharedStrings: - return XLQuery(query).setResult(m_sharedStrings); + return XLQuery(query).setResult(&m_sharedStrings); + //return XLQuery(query).setResult(1); + + case XLQueryType::QuerySheetFromName: + return XLQuery(query).setResult(getXmlDataByName(query.getParam("sheetName"))); + + case XLQueryType::QueryTableCount: + { + unsigned int results = 0; + + for (auto& item : m_data){ + if(item.getXmlType() == XLContentType::Table) + ++results; + } + + return XLQuery(query).setResult(results); + } + case XLQueryType::QueryTableFromIndex: + { + unsigned int index = 0; + for (auto& item : m_data){ + if(item.getXmlType() == XLContentType::Table){ + if(index == query.getParam("index")) + return XLQuery(query).setResult(&item); + ++index; + } + } + + return query; // Table not found + } + case XLQueryType::QueryTableFromName: + { + for (auto& item : m_data){ + if(item.getXmlType() == XLContentType::Table){ + if (item.getXmlDocument()->child("table").attribute("name").value() == query.getParam("tableName")) + return XLQuery(query).setResult(&item); + } + } + throw XLInternalError("Table does not exist in zip archive (" + query.getParam("tableName") + ")"); + return query; // Table not found + } case XLQueryType::QueryXmlData: { auto result = std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { return item.getXmlPath() == query.getParam("xmlPath"); }); - if (result == m_data.end()) throw XLInternalError("Path does not exist in zip archive (" + query.getParam("xmlPath") + ")"); + if (result == m_data.end()){ + XLLogError("Path does not exist in zip archive (" + query.getParam("xmlPath") + ")"); + //throw XLInternalError("Path does not exist in zip archive (" + query.getParam("xmlPath") + ")"); + return XLQuery(query).setResult(nullptr); + } return XLQuery(query).setResult(&*result); } } @@ -988,12 +729,18 @@ bool XLDocument::isOpen() const { return this->operator bool(); } +const OpenXLSX::XLStyles& OpenXLSX::XLDocument::styles() const +{ + return m_styles; +} + + /** * @details */ -XLXmlData* XLDocument::getXmlData(const std::string& path) +XLXmlData* XLDocument::getXmlDataByPath(const std::string& path) { - if (!hasXmlData(path)) throw XLInternalError("Path does not exist in zip archive."); + if (!hasXmlData(path)) throw XLInternalError("Path \"" + path + "\" does not exist in zip archive."); return &*std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { return item.getXmlPath() == path; }); // auto result = std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { return item.getXmlPath() == path; }); // if (result == m_data.end()) throw XLInternalError("Path does not exist in zip archive."); @@ -1003,9 +750,9 @@ XLXmlData* XLDocument::getXmlData(const std::string& path) /** * @details */ -const XLXmlData* XLDocument::getXmlData(const std::string& path) const +const XLXmlData* XLDocument::getXmlDataByPath(const std::string& path) const { - if (!hasXmlData(path)) throw XLInternalError("Path does not exist in zip archive."); + if (!hasXmlData(path)) throw XLInternalError("Path \"" + path + "\" does not exist in zip archive."); return &*std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { return item.getXmlPath() == path; }); // if (result == m_data.end()) throw XLInternalError("Path does not exist in zip archive."); // return &*result; @@ -1026,3 +773,278 @@ std::string XLDocument::extractXmlFromArchive(const std::string& path) { return (m_archive.hasEntry(path) ? m_archive.getEntry(path) : ""); } + +XLXmlData* XLDocument::getXmlDataByName(const std::string& name) const +{ + // for(auto objXmlData : m_XmlWorkbook->getChildNodes()) + for(auto& objXmlData : m_data) + if(objXmlData.getName() == name) + return &objXmlData; + + return nullptr; +} + +std::string XLDocument::getSheetRelsPath(const std::string& sheetName) const +{ + XLXmlData* wsItem = getXmlDataByName(sheetName); + if (wsItem == nullptr) // sheetName does not exist + return std::string(); + + std::string sheetPath = wsItem->getXmlPath(); + std::string::size_type n = sheetPath.find_last_of('/'); + + if(n>sheetPath.size()) // path does not contains '/' + return std::string(); + + // Some spreadsheets use absolute rather than relative paths in relationship items. + if (sheetPath.substr(0,4) == "/xl/") + sheetPath = "xl/" + sheetPath.substr(4); + + const std::string basePath = sheetPath.substr(0, n); + const std::string fileName = sheetPath.substr(n); + + if (fileName.substr(0, 6) != "/sheet") // This is not a sheet path + return std::string(); + + return basePath + "/_rels" + fileName + ".rels"; +} + +uint16_t XLDocument::availableFileID(XLContentType type) +{ + std::vector fileIndices; + + for (auto& wsItem : m_data) + if (wsItem.getXmlType() == type){ + std::string path = wsItem.getXmlPath(); + std::string::size_type n = path.find_last_of("/"); + // TODO this works because "sheet", "table" and "chart" have same length + fileIndices.push_back(std::stoi(path.substr(n+6,path.size()-n-10))); // removing "table and .xml" + } + + // Get the first available indice for file and id (filling missings) + std::sort (fileIndices.begin(), fileIndices.end()); + uint32_t nFile = 1; + for(uint32_t i : fileIndices) + if (i == nFile) + nFile +=1; + + return nFile; +} + +uint16_t XLDocument::availableSheetID() +{ + std::vector idIndices; + auto node = m_XmlWorkbook->getXmlDocument()->document_element().child("sheets"); + for (auto& wsItem : node.children()) + idIndices.push_back(std::stoi(wsItem.attribute("sheetId").value())); + + std::sort (idIndices.begin(), idIndices.end()); + uint16_t nId = 1; + for(uint32_t i : idIndices) + if (i == nId) + nId +=1; + + return nId; +} + +/** + * @todo check if the reference overlap an existing table + */ +void XLDocument::createTable(const std::string& sheetName, const std::string& tableName, const std::string& reference) +{ + XLXmlData* wks = getXmlDataByName(sheetName); + + std::vector idIndices; + + std::string basePath = "xl/tables/"; + std::string name = tableName; + // Loop through existing tables to find the available: + // - filename + // - index + // - check name + for (auto& wsItem : m_data) + if (wsItem.getXmlType() == XLContentType::Table){ + std::string path = wsItem.getXmlPath(); + std::string::size_type n = path.find_last_of("/"); + + XMLNode tableNode = wsItem.getXmlDocument()->child("table"); + idIndices.push_back(std::stoi(tableNode.attribute("id").value())); + if (( name == tableNode.attribute("name").value()) || + ( name == tableNode.attribute("displayName").value())) + name += INCREMENT_STRING; + } + + std::sort (idIndices.begin(), idIndices.end()); + uint32_t nId = 1; + for(uint32_t i : idIndices) + if (i == nId) + nId +=1; + + std::string fileTable = "table" + + std::to_string(availableFileID(XLContentType::Table)) + + ".xml"; + std::string filePath = basePath + fileTable; + + std::string sheetRelsPath = getSheetRelsPath(sheetName); + if (sheetRelsPath.empty()) // sheetname does not exist or error in the path + return; + + + if (!m_archive.hasEntry(sheetRelsPath)){ // if the file does not exists xl/worksheets/_rels/sheet{0}.xml.rels + m_archive.addEntry(sheetRelsPath, XLTemplate::emptySheetRels); // add the table{0}.xml file + m_data.emplace_back( + /* parentDoc */ this, + /* xmlPath */ sheetRelsPath, + /* xmlID */ std::string(), + /* name */ std::string(), + /* xmlType */ XLContentType::WorksheetRelations, + /* parentNode*/ wks); + wks->addChildNode(getXmlDataByPath(sheetRelsPath)); + } + + // adding Rels + auto sheetRels = XLRelationships(getXmlDataByPath(sheetRelsPath)); + auto relItem = sheetRels.addRelationship(XLRelationshipType::Table, "../tables/" + fileTable); + + // adding the file table in content, create it and register it + m_contentTypes.addOverride("/" + filePath, XLContentType::Table); // add to contentTypes + m_archive.addEntry(filePath, XLTemplate::emptyTable); // add the table{0}.xml file + + m_data.emplace_back( + /* parentDoc */ this, + /* xmlPath */ filePath, + /* xmlID */ relItem.id(), + /* name */ name, + /* xmlType */ XLContentType::Table, + /* parentNode*/ wks); + XLXmlData* tableXml = getXmlDataByName(name); + wks->addChildNode(tableXml); + + // add tablePart in sheet{0}.xml + XMLNode tableParts = wks->getXmlDocument()->document_element().child("tableParts"); + if(!tableParts){ + tableParts = wks->getXmlDocument()->child("worksheet").append_child("tableParts"); + tableParts.append_attribute("count").set_value("0"); + } + + tableParts.attribute("count") + .set_value(std::to_string(std::stoi( + tableParts.attribute("count").value()) + 1 ).c_str()); + XMLNode newPart = tableParts.append_child("tablePart"); + newPart.append_attribute("r:id").set_value(relItem.id().c_str()); + + // Prepare table{0}.xml + XMLDocument* tableDoc = tableXml->getXmlDocument(); + XMLNode tableNode = tableDoc->child("table"); + + // Get safe ref + auto pair = XLCellRange::topLeftBottomRight(reference); + auto topLeft = XLCellReference(pair.first); + auto bottomRight = XLCellReference(pair.second); + std::string ref = topLeft.address(false) + ":" + bottomRight.address(false); + + + tableNode.attribute("id").set_value(std::to_string(nId).c_str()); + tableNode.attribute("name").set_value(name.c_str()); + tableNode.attribute("displayName").set_value(name.c_str()); + tableNode.attribute("ref").set_value(ref.c_str()); + // set autofilter + tableNode.child("autoFilter").attribute("ref").set_value(ref.c_str()); + + // Set up the columns - create a temporary worksheet to access the shared string + const XLWorksheet* pWks = new XLWorksheet(wks); + auto topRight = XLCellReference(topLeft.row(),bottomRight.column()); + auto headerRange = XLCellRange(wks->getXmlDocument()->first_child().child("sheetData"), + topLeft, + topRight, + pWks); + + // Setup all the columns name + XMLNode columnsNode = tableNode.child("tableColumns"); + uint16_t colId = 1; + std::vector colNames; + + for(auto& cell : headerRange){ + std::string colName = cell.value(); + if (colName.empty()) + colName = "Column"; + bool notValid = true; + while (notValid){ + if (std::find(colNames.begin(), colNames.end(), colName) != colNames.end()) + colName += INCREMENT_STRING; + else + notValid = false; + } + colNames.push_back(colName); + cell.value() = colName; + + auto newNode = columnsNode.append_child("tableColumn"); + newNode.append_attribute("id").set_value(std::to_string(colId).c_str()); + newNode.append_attribute("name").set_value(colName.c_str()); + colId +=1; + } + // adding the table columns count + columnsNode.attribute("count").set_value(std::to_string(colId-1).c_str()); + + // TODEL + delete pWks; +} + +void XLDocument::deleteTable(const std::string& tableName) +{ + XLXmlData* pTable = getXmlDataByName(tableName); + if(pTable == nullptr){ + XLLogError("Table \"" + tableName + "\" does not exist, impossible to delete."); + return; // table doesn't exist + } + XLXmlData* pSheet = pTable->getParentNode(); + XLXmlData* pShtRls = getXmlDataByPath(getSheetRelsPath(pSheet->getName())); + std::string rId = pTable->getXmlID(); + + // Delete tablePart in sheet{0}.xml + auto& shtChildren = pSheet->getChildNodes(); + XMLNode tableParts = pSheet->getXmlDocument()->document_element().child("tableParts"); + if(tableParts){ + tableParts.remove_child( + tableParts.find_child_by_attribute("r:id", rId.c_str())); + + uint16_t n = 0; // count children + for (XMLNode child : tableParts.children()) n++; + + if (n>0) + tableParts.attribute("count") + .set_value(std::to_string(n).c_str()); + else + pSheet->getXmlDocument()->document_element().remove_child("tableParts"); + } + + // Delete Relathionship in sheet{0}.xml.rels + XMLNode rels = pShtRls->getXmlDocument()->document_element(); + if(rels){ + rels.remove_child( + rels.find_child_by_attribute("Id", rId.c_str())); + + uint16_t n = 0; // count children + for (XMLNode child : rels.children()) n++; + + if (n == 0){ + // No more relations, we can delete the file + std::string relsPath = pShtRls ->getXmlPath(); + m_archive.deleteEntry(relsPath); + shtChildren.erase(std::find_if(shtChildren.begin(), shtChildren.end(), [&](XLXmlData* item) { + return item->getXmlPath() == relsPath;})); + m_data.erase(std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { + return item.getXmlPath() == relsPath;})); + } + } // if rels + + // Delete table{0}.xml file and its reference + std::string tblPath = pTable->getXmlPath(); + m_archive.deleteEntry(tblPath); + m_contentTypes.deleteOverride("/" + tblPath); + shtChildren.erase(std::find_if(shtChildren.begin(), shtChildren.end(), [&](XLXmlData* item) { + return item->getName() == tableName;})); + m_data.erase(std::find_if(m_data.begin(), m_data.end(), [&](const XLXmlData& item) { + return item.getXmlPath() == tblPath;})); + +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLFormula.cpp b/OpenXLSX/sources/XLFormula.cpp index 4bb3454a..06bd1518 100644 --- a/OpenXLSX/sources/XLFormula.cpp +++ b/OpenXLSX/sources/XLFormula.cpp @@ -2,12 +2,14 @@ // Created by Kenneth Balslev on 27/08/2021. // +// ===== External Includes ===== // +#include +#include + // ===== OpenXLSX Includes ===== // #include "XLFormula.hpp" #include -#include - using namespace OpenXLSX; /** @@ -54,9 +56,51 @@ std::string XLFormula::get() const XLFormula& XLFormula::clear() { m_formulaString = ""; + m_isError = false; return *this; } +/** + * @details use a regex to replace the variable to be deleted by #REF! + */ +XLFormula XLFormula::updateDeleting(const std::string& toBeDeleted) +{ + + //std::string s = toBeDeleted + R"aka((?=([^"]*"[^"]*")*[^"]*$))aka"; + // Regex that will find all the occurence of toBeDeleted excepted inside quote + // TODO deal with nested quotes + const std::regex re(toBeDeleted + R"&((?=([^"]*"[^"]*")*[^"]*$))&"); + + std::string newFormula = std::regex_replace(m_formulaString, re, "#REF!"); + + if(m_formulaString == newFormula) + m_isError = false; + else + m_isError = true; + + m_formulaString = newFormula; + + return *this; +} + +bool XLFormula::hasError() const +{ + return m_isError; +} + +void XLFormula::checkIfError() +{ + //Check if Formula has error + // TODO deal with nested quotes + const std::regex re(R"&(#REF!(?=([^"]*"[^"]*")*[^"]*$))&"); + + std::smatch m; // unused + + if (std::regex_search(m_formulaString, m, re)) + m_isError = true; + +} + /** * @details */ @@ -68,7 +112,7 @@ XLFormula::operator std::string() const /** * @details Constructor. Set the m_cell and m_cellNode objects. */ -XLFormulaProxy::XLFormulaProxy(XLCell* cell, XMLNode* cellNode) : m_cell(cell), m_cellNode(cellNode) +XLFormulaProxy::XLFormulaProxy(XLCell* cell, std::shared_ptr cellNode) : m_cell(cell), m_cellNode(cellNode) { assert(cell); // NOLINT } @@ -143,6 +187,19 @@ XLFormulaProxy& XLFormulaProxy::clear() return *this; } +/** + * @details Set the m_formulaString member to an empty string. + */ +XLFormula XLFormulaProxy::updateDeleting(const std::string& toBeDeleted) +{ + return getFormula().updateDeleting(toBeDeleted); +} + +bool XLFormulaProxy::hasError() const +{ + return getFormula().hasError(); +} + /** * @details Convenience function for setting the formula. This method is called from the templated * string assignment operator. @@ -163,6 +220,15 @@ void XLFormulaProxy::setFormulaString(const char* formulaString) { // ===== Set the text of the value node. m_cellNode->child("f").text().set(formulaString); m_cellNode->child("v").text().set(0); + + // ===== Remove "t" attribute so that the cell shows the number we've just set + m_cellNode->remove_attribute("t"); + + // ===== Remove tag in case previous type was "inlineStr" + m_cellNode->remove_child("is"); + + // ===== Excel fails to load documents where comes after so make sure it is the first child + m_cellNode->prepend_move(m_cellNode->child("f")); } /** diff --git a/OpenXLSX/sources/XLNamedRange.cpp b/OpenXLSX/sources/XLNamedRange.cpp new file mode 100644 index 00000000..a85ca3bd --- /dev/null +++ b/OpenXLSX/sources/XLNamedRange.cpp @@ -0,0 +1,120 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +// ===== External Includes ===== // +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLNamedRange.hpp" + +using namespace OpenXLSX; + + +XLNamedRange::XLNamedRange(const std::string& name, + const std::string& reference, + uint32_t localSheetId, + const XLCellRange& rng): + m_name(name), + m_reference(reference), + m_localSheetId(localSheetId), + XLCellRange(rng) +{ +} + +XLNamedRange::~XLNamedRange() +{} + +XLNamedRange::XLNamedRange(const XLNamedRange& other): + m_name(other.m_name), + m_reference(other.m_reference), + m_localSheetId(other.m_localSheetId), + XLCellRange(other) +{} + + +XLNamedRange& XLNamedRange::operator=(const XLNamedRange& other) +{ + if (&other != this) { + XLCellRange::operator=(other); + m_localSheetId = other.m_localSheetId; + m_name = other.m_name; + m_reference = other.m_reference; + } + + return *this; +} + +XLNamedRange& XLNamedRange::operator=(XLNamedRange&& other) noexcept +{ + if (&other != this) { + XLCellRange::operator=(std::move(other)); + m_localSheetId = other.m_localSheetId; + m_name = std::string(other.m_name); + m_reference = other.m_reference; + } + + return *this; +} + +const std::string& XLNamedRange::name() const +{ + return m_name; +} + +const std::string& XLNamedRange::reference() const +{ + return m_reference; +} + +uint32_t XLNamedRange::localSheetId() const +{ + return m_localSheetId; +} + +XLCell XLNamedRange::firstCell() const +{ + return (*begin()); +} diff --git a/OpenXLSX/sources/XLRelationships.cpp b/OpenXLSX/sources/XLRelationships.cpp index e03742ae..aaba5a9a 100644 --- a/OpenXLSX/sources/XLRelationships.cpp +++ b/OpenXLSX/sources/XLRelationships.cpp @@ -93,6 +93,8 @@ namespace type = XLRelationshipType::Image; else if (typeString == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart") type = XLRelationshipType::Chart; + else if (typeString == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table") + type = XLRelationshipType::Table; else if (typeString == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath") type = XLRelationshipType::ExternalLinkPath; else if (typeString == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings") @@ -145,6 +147,8 @@ namespace typeString = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"; else if (type == XLRelationshipType::Chart) typeString = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"; + else if (type == XLRelationshipType::Table) + typeString = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"; else if (type == XLRelationshipType::ExternalLinkPath) typeString = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath"; else if (type == XLRelationshipType::PrinterSettings) @@ -159,19 +163,6 @@ namespace return typeString; } - uint32_t GetNewRelsID(XMLNode relationshipsNode) - { - return static_cast(stoi(std::string(std::max_element(relationshipsNode.children().begin(), - relationshipsNode.children().end(), - [](XMLNode a, XMLNode b) { - return stoi(std::string(a.attribute("Id").value()).substr(3)) < - stoi(std::string(b.attribute("Id").value()).substr(3)); - }) - ->attribute("Id") - .value()) - .substr(3)) + - 1); - } } // namespace XLRelationshipItem::XLRelationshipItem() : m_relationshipNode(std::make_unique()) {} @@ -269,11 +260,9 @@ XLRelationshipItem XLRelationships::addRelationship(XLRelationshipType type, con { std::string typeString = GetStringFromType(type); - std::string id = "rId" + std::to_string(GetNewRelsID(xmlDocument().document_element())); - // Create new node in the .rels file auto node = xmlDocument().document_element().append_child("Relationship"); - node.append_attribute("Id").set_value(id.c_str()); + node.append_attribute("Id").set_value(getAvailableRelsId().c_str()); node.append_attribute("Type").set_value(typeString.c_str()); node.append_attribute("Target").set_value(target.c_str()); @@ -299,3 +288,28 @@ bool XLRelationships::idExists(const std::string& id) const { return xmlDocument().document_element().find_child_by_attribute("Id", id.c_str()) != nullptr; } + +/** + * @details Look for hole in the rId field. First load a vector, + * order it and find the first available id. + */ +std::string XLRelationships::getAvailableRelsId() const +{ + std::vector rIdIndices; + + // Find available rId1 + for (auto pChild : xmlDocument().document_element().children()){ + auto test = pChild.attribute("Id").value(); + if(!std::string(pChild.attribute("Id").value()).empty()) + rIdIndices.push_back(std::stoi(std::string(pChild.attribute("Id").value()).substr(3))); + + } + + std::sort (rIdIndices.begin(), rIdIndices.end()); + uint32_t nrId = 1; + for(uint32_t i : rIdIndices) + if (i == nrId) + nrId +=1; + + return ("rId" + std::to_string(nrId)); +} diff --git a/OpenXLSX/sources/XLRow.cpp b/OpenXLSX/sources/XLRow.cpp index 08fa0952..a38ee096 100644 --- a/OpenXLSX/sources/XLRow.cpp +++ b/OpenXLSX/sources/XLRow.cpp @@ -62,6 +62,7 @@ namespace OpenXLSX * @post */ XLRow::XLRow() : m_rowNode(nullptr), + m_worksheet(nullptr), m_rowDataProxy(this, m_rowNode.get()) {} /** @@ -70,9 +71,9 @@ namespace OpenXLSX * @pre * @post */ - XLRow::XLRow(const XMLNode& rowNode, const XLSharedStrings& sharedStrings) + XLRow::XLRow(const XMLNode& rowNode, const XLWorksheet* wks) : m_rowNode(std::make_unique(rowNode)), - m_sharedStrings(sharedStrings), + m_worksheet(wks), m_rowDataProxy(this, m_rowNode.get()) {} @@ -83,7 +84,7 @@ namespace OpenXLSX */ XLRow::XLRow(const XLRow& other) : m_rowNode(other.m_rowNode ? std::make_unique(*other.m_rowNode) : nullptr), - m_sharedStrings(other.m_sharedStrings), + m_worksheet(other.m_worksheet), m_rowDataProxy(this, m_rowNode.get()) {} @@ -95,7 +96,7 @@ namespace OpenXLSX */ XLRow::XLRow(XLRow&& other) noexcept : m_rowNode(std::move(other.m_rowNode)), - m_sharedStrings(std::move(other.m_sharedStrings)), + m_worksheet(other.m_worksheet), m_rowDataProxy(this, m_rowNode.get()) {} @@ -130,7 +131,6 @@ namespace OpenXLSX { if (&other != this) { m_rowNode = std::move(other.m_rowNode); - m_sharedStrings = other.m_sharedStrings; m_rowDataProxy = XLRowDataProxy(this, m_rowNode.get()); } return *this; @@ -264,7 +264,8 @@ namespace OpenXLSX */ XLRowDataRange XLRow::cells() const { - return XLRowDataRange(*m_rowNode, 1, XLCellReference(m_rowNode->last_child().attribute("r").value()).column(), m_sharedStrings); + return XLRowDataRange(*m_rowNode, 1, + XLCellReference(m_rowNode->last_child().attribute("r").value()).column(), m_worksheet); } /** @@ -274,7 +275,7 @@ namespace OpenXLSX */ XLRowDataRange XLRow::cells(uint16_t cellCount) const { - return XLRowDataRange(*m_rowNode, 1, cellCount, m_sharedStrings); + return XLRowDataRange(*m_rowNode, 1, cellCount, m_worksheet); } /** @@ -284,7 +285,7 @@ namespace OpenXLSX */ XLRowDataRange XLRow::cells(uint16_t firstCell, uint16_t lastCell) const { - return XLRowDataRange(*m_rowNode, firstCell, lastCell, m_sharedStrings); + return XLRowDataRange(*m_rowNode, firstCell, lastCell, m_worksheet); } bool XLRow::isEqual(const XLRow& lhs, const XLRow& rhs) @@ -315,12 +316,12 @@ namespace OpenXLSX : m_dataNode(std::make_unique(*rowRange.m_dataNode)), m_firstRow(rowRange.m_firstRow), m_lastRow(rowRange.m_lastRow), - m_sharedStrings(rowRange.m_sharedStrings) + m_worksheet(rowRange.m_worksheet) { if (loc == XLIteratorLocation::End) m_currentRow = XLRow(); else { - m_currentRow = XLRow(getRowNode(*m_dataNode, m_firstRow), m_sharedStrings); + m_currentRow = XLRow(getRowNode(*m_dataNode, m_firstRow), m_worksheet); } } @@ -341,7 +342,7 @@ namespace OpenXLSX m_firstRow(other.m_firstRow), m_lastRow(other.m_lastRow), m_currentRow(other.m_currentRow), - m_sharedStrings(other.m_sharedStrings) + m_worksheet(other.m_worksheet) {} /** @@ -389,11 +390,11 @@ namespace OpenXLSX else if (!rowNode || rowNode.attribute("r").as_ullong() != rowNumber) { rowNode = m_dataNode->insert_child_after("row", *m_currentRow.m_rowNode); rowNode.append_attribute("r").set_value(rowNumber); - m_currentRow = XLRow(rowNode, m_sharedStrings); + m_currentRow = XLRow(rowNode, m_worksheet); } else - m_currentRow = XLRow(rowNode, m_sharedStrings); + m_currentRow = XLRow(rowNode, m_worksheet); return *this; } @@ -470,11 +471,12 @@ namespace OpenXLSX * @pre * @post */ - XLRowRange::XLRowRange(const XMLNode& dataNode, uint32_t first, uint32_t last, const OpenXLSX::XLSharedStrings& sharedStrings) + XLRowRange::XLRowRange(const XMLNode& dataNode, uint32_t first, + uint32_t last, const XLWorksheet* wks) : m_dataNode(std::make_unique(dataNode)), m_firstRow(first), m_lastRow(last), - m_sharedStrings(sharedStrings) + m_worksheet(wks) {} /** @@ -486,7 +488,7 @@ namespace OpenXLSX : m_dataNode(std::make_unique(*other.m_dataNode)), m_firstRow(other.m_firstRow), m_lastRow(other.m_lastRow), - m_sharedStrings(other.m_sharedStrings) + m_worksheet(other.m_worksheet) {} /** diff --git a/OpenXLSX/sources/XLRowData.cpp b/OpenXLSX/sources/XLRowData.cpp index c33a9960..731f5c93 100644 --- a/OpenXLSX/sources/XLRowData.cpp +++ b/OpenXLSX/sources/XLRowData.cpp @@ -64,7 +64,7 @@ namespace OpenXLSX XLRowDataIterator::XLRowDataIterator(const XLRowDataRange& rowDataRange, XLIteratorLocation loc) : m_dataRange(std::make_unique(rowDataRange)), m_cellNode(std::make_unique(getCellNode(*m_dataRange->m_rowNode, m_dataRange->m_firstCol))), - m_currentCell(loc == XLIteratorLocation::End ? XLCell() : XLCell(*m_cellNode, m_dataRange->m_sharedStrings)) + m_currentCell(loc == XLIteratorLocation::End ? XLCell() : XLCell(*m_cellNode, m_dataRange->m_worksheet)) {} /** @@ -140,13 +140,13 @@ namespace OpenXLSX XLCellReference( static_cast(m_dataRange->m_rowNode->attribute("r").as_ullong()), static_cast(cellNumber)).address().c_str()); - m_currentCell = XLCell(cellNode, m_dataRange->m_sharedStrings); + m_currentCell = XLCell(cellNode, m_dataRange->m_worksheet); } // ===== Otherwise, the cell node and the column number match. else { assert(XLCellReference(cellNode.attribute("r").value()).column() == cellNumber); - m_currentCell = XLCell(cellNode, m_dataRange->m_sharedStrings); + m_currentCell = XLCell(cellNode, m_dataRange->m_worksheet); } return *this; @@ -219,11 +219,12 @@ namespace OpenXLSX * @pre * @post */ - XLRowDataRange::XLRowDataRange(const XMLNode& rowNode, uint16_t firstColumn, uint16_t lastColumn, const XLSharedStrings& sharedStrings) + XLRowDataRange::XLRowDataRange(const XMLNode& rowNode, uint16_t firstColumn, + uint16_t lastColumn, const XLWorksheet* wks) : m_rowNode(std::make_unique(rowNode)), m_firstCol(firstColumn), m_lastCol(lastColumn), - m_sharedStrings(sharedStrings) + m_worksheet(wks) { if (lastColumn < firstColumn) { m_firstCol = 1; @@ -240,8 +241,8 @@ namespace OpenXLSX XLRowDataRange::XLRowDataRange(const XLRowDataRange& other) : m_rowNode(std::make_unique(*other.m_rowNode)), m_firstCol(other.m_firstCol), - m_lastCol(other.m_lastCol), - m_sharedStrings(other.m_sharedStrings) + m_lastCol(other.m_lastCol) + //m_sharedStrings(other.m_sharedStrings) {} @@ -391,7 +392,7 @@ namespace OpenXLSX curNode = m_rowNode->prepend_child("c"); curNode.append_attribute("r").set_value(XLCellReference(static_cast(m_row->rowNumber()), static_cast(colNo)).address().c_str()); - XLCell(curNode, m_row->m_sharedStrings).value() = *value; + XLCell(curNode, m_row->m_worksheet).value() = *value; --colNo; } @@ -411,7 +412,7 @@ namespace OpenXLSX if (values.size() > MAX_COLS) throw XLOverflowError("Container size exceeds maximum number of columns."); if (values.empty()) return *this; - auto range = XLRowDataRange(*m_rowNode, 1, static_cast(values.size()), getSharedStrings()); + auto range = XLRowDataRange(*m_rowNode, 1, static_cast(values.size()),m_row->m_worksheet); auto dst = range.begin(); auto src = values.begin(); @@ -470,23 +471,13 @@ namespace OpenXLSX // ===== If there are one or more cells in the current row, iterate through them and add the value to the container. if (numCells > 0) { for (auto& node : m_rowNode->children()) - result[XLCellReference(node.attribute("r").value()).column() - 1] = XLCell(node, m_row->m_sharedStrings).value(); + result[XLCellReference(node.attribute("r").value()).column() - 1] = XLCell(node, m_row->m_worksheet).value(); } // ===== Return the resulting container. return result; } - /** - * @details The function returns a pointer to an XLSharedStrings object embedded in the m_row member. - * This is required because the XLRow class internals is not visible in the header file. - * @pre - * @post - */ - XLSharedStrings XLRowDataProxy::getSharedStrings() const - { - return m_row->m_sharedStrings; - } /** * @details The deleteCellValues is a convenience function used solely by the templated operator= function. @@ -517,7 +508,7 @@ namespace OpenXLSX { auto curNode = m_rowNode->prepend_child("c"); curNode.append_attribute("r").set_value(XLCellReference(static_cast(m_row->rowNumber()), col).address().c_str()); - XLCell(curNode, m_row->m_sharedStrings).value() = value; + XLCell(curNode, m_row->m_worksheet).value() = value; } /** diff --git a/OpenXLSX/sources/XLSharedStrings.cpp b/OpenXLSX/sources/XLSharedStrings.cpp index c353d4f4..294ccab1 100644 --- a/OpenXLSX/sources/XLSharedStrings.cpp +++ b/OpenXLSX/sources/XLSharedStrings.cpp @@ -54,26 +54,45 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. using namespace OpenXLSX; /** - * @details Constructs a new XLSharedStrings object. Only one (common) object is allowed per XLDocument instance. - * A filepath to the underlying XML file must be provided. + * @details Constructs a new XLSharedStrings object. + * Only one (common) object is allowed per XLDocument instance. (stored in XLDocument) + * It shall be retrieve via worksheet -> Document query process + * A filepath to the underlying XML file must be provided. */ -XLSharedStrings::XLSharedStrings(XLXmlData* xmlData, std::deque *stringCache) : XLXmlFile(xmlData), m_stringCache(stringCache) +XLSharedStrings::XLSharedStrings(XLXmlData* xmlData) + : XLXmlFile(xmlData) { + + for (const auto& node : m_xmlData->getXmlDocument()->document_element().children()){ + if (std::string(node.first_child().name()) == "r") { + std::string result; + for (const auto& elem : node.children()) + result += elem.child("t").text().get(); + m_stringShared.push_back(result); + + } else { + m_stringShared.push_back(node.first_child().text().get()); + + } + + } } -/** - * @details - */ + XLSharedStrings::~XLSharedStrings() = default; /** * @details Look up a string index by the string content. If the string does not exist, the returned index is -1. */ -int32_t XLSharedStrings::getStringIndex(const std::string& str) const +uint32_t XLSharedStrings::getStringIndex(const std::string& str) const { - auto iter = std::find_if(m_stringCache->begin(), m_stringCache->end(), [&](const std::string& s) { return str == s; }); + auto it = std::find(m_stringShared.begin(), m_stringShared.end(), str); + if (it != m_stringShared.end()) + return (uint32_t)(it - m_stringShared.begin()); - return iter == m_stringCache->end() ? -1 : static_cast(std::distance(m_stringCache->begin(), iter)); + // Not found + return (uint32_t)(-1); + } /** @@ -81,7 +100,7 @@ int32_t XLSharedStrings::getStringIndex(const std::string& str) const */ bool XLSharedStrings::stringExists(const std::string& str) const { - return getStringIndex(str) >= 0; + return getStringIndex(str) != (uint32_t)(-1); } /** @@ -89,22 +108,37 @@ bool XLSharedStrings::stringExists(const std::string& str) const */ const char* XLSharedStrings::getString(uint32_t index) const { - return (*m_stringCache)[index].c_str(); + try{ + std::string value = m_stringShared.at(index); + return value.c_str(); + } catch (const std::out_of_range&) { + return std::string().c_str(); + } + } /** * @details Append a string by creating a new node in the XML file and adding the string to it. The index to the * shared string is returned */ -int32_t XLSharedStrings::appendString(const std::string& str) +uint32_t XLSharedStrings::appendString(const std::string& str) { auto textNode = xmlDocument().document_element().append_child("si").append_child("t"); - if (str.front() == ' ' || str.back() == ' ') textNode.append_attribute("xml:space").set_value("preserve"); + if ((!str.empty()) && (str.front() == ' ' || str.back() == ' ')) + textNode.append_attribute("xml:space").set_value("preserve"); textNode.text().set(str.c_str()); - m_stringCache->emplace_back(textNode.text().get()); - return static_cast(std::distance(m_stringCache->begin(), m_stringCache->end()) - 1); + m_stringShared.push_back(str); + + xmlDocument().child("sst").attribute("count") + .set_value(std::to_string(m_stringShared.size()).c_str()); + + // TODO deal with the uniqueCount + xmlDocument().child("sst").attribute("uniqueCount") + .set_value(std::to_string(m_stringShared.size()).c_str()); + return m_stringShared.size() - 1; + } /** @@ -113,8 +147,15 @@ int32_t XLSharedStrings::appendString(const std::string& str) */ void XLSharedStrings::clearString(uint64_t index) { - (*m_stringCache)[index] = ""; - auto iter = xmlDocument().document_element().children().begin(); + //(*m_stringCache)[index] = ""; + + try{ + m_stringShared.at(index) = std::string(); + } catch (const std::out_of_range&) { + return; + } + + auto iter = xmlDocument().document_element().children().begin(); std::advance(iter, index); iter->text().set(""); } diff --git a/OpenXLSX/sources/XLSheet.cpp b/OpenXLSX/sources/XLSheet.cpp index a654bd04..32dd1baf 100644 --- a/OpenXLSX/sources/XLSheet.cpp +++ b/OpenXLSX/sources/XLSheet.cpp @@ -46,6 +46,7 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. // ===== External Includes ===== // #include #include +#include // ===== OpenXLSX Includes ===== // #include "XLCellRange.hpp" @@ -65,7 +66,7 @@ namespace OpenXLSX * @param xmlDocument XMLDocument object * @param color Thr color to set */ - void setTabColor(const XMLDocument& xmlDocument, const XLColor& color) { + void setTabColorOnDoc(const XMLDocument& xmlDocument, const XLColor& color) { if (!xmlDocument.document_element().child("sheetPr")) xmlDocument.document_element().prepend_child("sheetPr"); @@ -164,9 +165,9 @@ XLColor XLSheet::color() const * @details This method sets the color of the sheet, by calling the setColor() * member function of the underlying sheet object (XLWorksheet or XLChartsheet). */ -void XLSheet::setColor(const XLColor& color) +void XLSheet::setTabColor(const XLColor& color) { - std::visit([&](auto&& arg) { return arg.setColor(color); }, m_sheet); + std::visit([&](auto&& arg) { return arg.setTabColor(color); }, m_sheet); } /** @@ -282,7 +283,7 @@ XLColor XLWorksheet::getColor_impl() const */ void XLWorksheet::setColor_impl(const XLColor& color) { - setTabColor(xmlDocument(), color); + setTabColorOnDoc(xmlDocument(), color); } /** @@ -369,7 +370,7 @@ XLCell XLWorksheet::cell(uint32_t rowNumber, uint16_t columnNumber) const } } - return XLCell{cellNode, parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()}; + return XLCell{cellNode, this}; } /** @@ -388,7 +389,13 @@ XLCellRange XLWorksheet::range(const XLCellReference& topLeft, const XLCellRefer return XLCellRange(xmlDocument().first_child().child("sheetData"), topLeft, bottomRight, - parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()); + this); +} + +XLCellRange XLWorksheet::range(const std::string& ref) const +{ + std::pair pair = XLCellRange::topLeftBottomRight(ref); + return range(XLCellReference(pair.first),XLCellReference(pair.second)); } /** @@ -404,7 +411,7 @@ XLRowRange XLWorksheet::rows() const (sheetDataNode.last_child() ? static_cast(sheetDataNode.last_child().attribute("r").as_ullong()) : 1), - parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()); + this); } /** @@ -417,7 +424,7 @@ XLRowRange XLWorksheet::rows(uint32_t rowCount) const return XLRowRange(xmlDocument().first_child().child("sheetData"), 1, rowCount, - parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()); + this); } /** @@ -430,7 +437,7 @@ XLRowRange XLWorksheet::rows(uint32_t firstRow, uint32_t lastRow) const return XLRowRange(xmlDocument().first_child().child("sheetData"), firstRow, lastRow, - parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()); + this); } /** @@ -440,8 +447,7 @@ XLRowRange XLWorksheet::rows(uint32_t firstRow, uint32_t lastRow) const */ XLRow XLWorksheet::row(uint32_t rowNumber) const { - return XLRow{getRowNode(xmlDocument().first_child().child("sheetData"), rowNumber), - parentDoc().execQuery(XLQuery(XLQueryType::QuerySharedStrings)).result()}; + return XLRow{getRowNode(xmlDocument().first_child().child("sheetData"), rowNumber), this}; } /** @@ -530,7 +536,7 @@ XLCellReference XLWorksheet::lastCell() const noexcept */ uint16_t XLWorksheet::columnCount() const noexcept { - std::vector counts; + std::vector counts; for (const auto& row : rows()) { counts.emplace_back(row.cellCount()); } @@ -567,9 +573,9 @@ void XLWorksheet::updateSheetName(const std::string& oldName, const std::string& // ===== Iterate through all defined names for (auto& row : xmlDocument().document_element().child("sheetData")) { for (auto& cell : row.children()) { - if (!XLCell(cell, XLSharedStrings()).hasFormula()) continue; + if (!XLCell(cell, this).hasFormula()) continue; - formula = XLCell(cell, XLSharedStrings()).formula().get(); + formula = XLCell(cell, this).formula().get(); // ===== Skip if formula contains a '[' and ']' (means that the defined refers to external workbook) if (formula.find('[') == std::string::npos && formula.find(']') == std::string::npos) { @@ -577,7 +583,7 @@ void XLWorksheet::updateSheetName(const std::string& oldName, const std::string& while (formula.find(oldNameTemp) != std::string::npos) { // NOLINT formula.replace(formula.find(oldNameTemp), oldNameTemp.length(), newNameTemp); } - XLCell(cell, XLSharedStrings()).formula() = formula; + XLCell(cell, this).formula() = formula; } } } @@ -606,7 +612,7 @@ XLColor XLChartsheet::getColor_impl() const */ void XLChartsheet::setColor_impl(const XLColor& color) { - setTabColor(xmlDocument(), color); + setTabColorOnDoc(xmlDocument(), color); } /** diff --git a/OpenXLSX/sources/XLStyles.cpp b/OpenXLSX/sources/XLStyles.cpp new file mode 100644 index 00000000..87aa4912 --- /dev/null +++ b/OpenXLSX/sources/XLStyles.cpp @@ -0,0 +1,471 @@ +/* + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + Copyright (c) 2018, Kenneth Troldal Balslev + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// ===== External Includes ===== // +#include +#include +#include +#include +#include + + +// ===== OpenXLSX Includes ===== // +#include "XLCell.hpp" +#include "XLDocument.hpp" +#include "XLStyles.hpp" +#include "XLXmlData.hpp" + +using namespace OpenXLSX; + +// ================================================================================ +// Helpers +// ================================================================================ +template +inline void splitA(const std::string& s, char delim, Out result) +{ + std::istringstream iss(s); + std::string item; + while (std::getline(iss, item, delim)) { + *result++ = item; + } +} + +inline void splitA(const std::string& s, char delim, std::vector& elems) +{ + splitA(s, delim, std::back_inserter(elems)); +} + +static pugi::xml_node findFontNode(const XMLDocument& xmlDoc, int fontId) +{ + //xmlDoc.find_child() ? how to use + pugi::xml_node node; + constexpr std::string_view nodeFontsName { "fonts" }; + constexpr std::string_view nodeFontName { "font" }; + for (const auto& nodeItem : xmlDoc.document_element().children()) { + if (nodeFontsName == nodeItem.name()) { + for (auto& xfNode : nodeItem.children()) { + if (nodeFontName == xfNode.name()) { + if (fontId == 0) { + return xfNode; + } + else { + int idx = 0; + while (idx < fontId) { + xfNode = xfNode.next_sibling(); + idx++; + } + return xfNode; + } + } + } + } + } + return node; +} + +// ================================================================================ +// XLStyles Class +// ================================================================================ + +XLStyles::XLStyles(XLXmlData* xmlData) : XLXmlFile(xmlData) +{ + init(xmlData); +} + +XLStyles::~XLStyles() = default; + +XLStyle XLStyles::style(const XLCell& cell) const +{ + XLStyle res(this->parentDoc()); + if (!cell) return res; + if (const auto* val = cell.m_cellNode->attribute("s").value(); val != nullptr) { + if (const size_t pos = std::atoi(val); m_VecStyle.size() > pos) { + res = m_VecStyle.at(pos); + } + } + return res; +} + +std::string XLStyles::formatString(int numFmtId) const +{ + constexpr const auto* snumFmts = "numFmts"; + constexpr const auto* snumFmtId = "numFmtId"; + constexpr const auto* sformatCode = "formatCode"; + + return xmlDocument() + .document_element() + .child(snumFmts) + .find_child_by_attribute(snumFmtId, std::to_string(numFmtId).c_str()) + .attribute(sformatCode) + .value(); +} + +void XLStyles::init(const XLXmlData* stylesData) +{ + // cache cellXfs + if (stylesData) { + constexpr const auto* numFmtId = "numFmtId"; + constexpr const auto* fontId = "fontId"; + constexpr const auto* fillId = "fillId"; + constexpr const auto* borderId = "borderId"; + constexpr const auto* xfId = "xfId"; + + constexpr std::string_view nodeNameCellXfs { "cellXfs" }; + constexpr std::string_view nodeNameXf { "xf" }; + + // TODO: check applyNumberFormat, reserve size from cellXfs count; + for (const auto& node : stylesData->getXmlDocument()->document_element().children()) { + if (nodeNameCellXfs == node.name()) { + for (const auto& xfNode : node.children()) { + if (nodeNameXf == xfNode.name()) { + XLStyle item(this->parentDoc()); + if (const auto* val = xfNode.attribute(numFmtId).value(); val != nullptr) item.m_numFmtId = std::stoi(val); + if (const auto* val = xfNode.attribute(fontId).value(); val != nullptr) item.m_fontId = std::stoi(val); + if (const auto* val = xfNode.attribute(fillId).value(); val != nullptr) item.m_fillId = std::stoi(val); + if (const auto* val = xfNode.attribute(borderId).value(); val != nullptr) item.m_borderId = std::stoi(val); + if (const auto* val = xfNode.attribute(xfId).value(); val != nullptr) item.m_xfId = std::stoi(val); + m_VecStyle.emplace_back(item); + } + } + break; + } + // TODO: Fonts + } + } +} + +// ================================================================================ +// XLStyle Class +// ================================================================================ + +XLStyle::XLStyle(const XLDocument& doc) : m_doc(doc) {} + +XLNumberFormat XLStyle::numberFormat() const +{ + XLNumberFormat fmt(*this); + return fmt; +} + +std::string XLStyle::formatString() const +{ + return m_doc.get().styles().formatString(m_numFmtId); +} + +int XLStyle::numFmtId() const +{ + return m_numFmtId; +} + +XLFont XLStyle::font() const +{ + return XLFont(*this, findFontNode(m_doc.get().styles().xmlDocument(), m_fontId)); +} + +// ================================================================================ +// XLFont Class +// ================================================================================ +// XLQuery? +XLFont::XLFont(const XLStyle& style, const XMLNode& node) : + m_style(style), m_node(new XMLNode(node)) {} + +XLFont::~XLFont() {} + +std::string XLFont::name() const +{ + std::string _name; + if (isValid()) { + _name = m_node->child("name").attribute("val").value(); + } + return _name; +} + +double XLFont::size() const +{ + double result = 0.0; + if (isValid()) { + result = std::stod(m_node->child("sz").attribute("val").value()); + } + return result; +} + +XLColor XLFont::color() const +{ + XLColor clr; + if (isValid()) { + const std::string val = m_node->child("color").attribute("rgb").value(); + if (val.size() != 0) { + clr.set(val); + } + } + return clr; +} + +int XLFont::colorIndex() const +{ + constexpr int xlColorIndexNone = -4142; + if (isValid()) { + const std::string val = m_node->child("color").attribute("theme").value(); + if (val.size() != 0) { + return std::atoi(val.c_str()); + } + } + return xlColorIndexNone; +} + + +int XLFont::underline() const { + //https://docs.microsoft.com/en-us/office/vba/api/excel.xlunderlinestyle + if (isValid()) { + constexpr std::string_view nodeName { "u" }; + constexpr std::string_view attDouble { "double" }; + for (const auto& node : m_node->children()) { + + if (nodeName == node.name()) { + if (auto attribute = node.attribute("val"); !attribute.empty() && attribute.value() == attDouble) { + return -4119; + } + else { + return 2; + } + + } + + } + } + return -4142; +} + +bool XLFont::strikethrough() const { + if (isValid()) { + constexpr std::string_view nodeName { "strike" }; + for (const auto& node : m_node->children()) { + if (nodeName == node.name()) { + return true; + } + } + } + return false; +} + + +bool XLFont::bold() const { + if (isValid()) { + constexpr std::string_view nodeName { "b" }; + for (const auto& node : m_node->children()) { + if (nodeName == node.name()) { + return true; + } + } + } + return false; +} + + +bool XLFont::italic() const{ + if (isValid()) { + constexpr std::string_view nodeName { "i" }; + for (const auto& node : m_node->children()) { + if (nodeName == node.name()) { + return true; + } + } + } + return false; +} + +bool XLFont::isValid() const +{ + constexpr std::string_view nodeFontName { "font" }; + if (m_style.get().m_fontId != -1 && m_node != nullptr && m_node->type() != pugi::xml_node_type::node_null && m_node->name() == nodeFontName){ + return true; + } + return false; +} + +// ================================================================================ +// XLNumberFormat Class +// ================================================================================ +XLNumberFormat::XLNumberFormat(const XLStyle& style) : m_style { style } {} + +XLNumberFormat::XLNumberType XLNumberFormat::type() +{ + return tryFindType(); +} + +std::string XLNumberFormat::currencySymbol() const +{ + return m_currencySymbol; +} + +// TODO: it may be possible to get precision and forrmatting in a single pass. +XLNumberFormat::XLNumberType XLNumberFormat::tryFindType() +{ + const auto _type = tryBuiltinType(); + if (_type != XLNumberType::kUnkown) return _type; + + const std::string fmt = m_style.get().formatString(); + if (fmt.size() == 0) return XLNumberType::kUnkown; + + std::vector bracketTextArray; + std::lconv* lc = std::localeconv(); + std::string bracketText; + bool isInBracket = false; + + auto foundType = XLNumberType::kUnkown; + + if (fmt.find("AM/PM") != std::string::npos) return XLNumberType::kDate; + + for (size_t idx = 0; idx < fmt.size(); idx++) { + if (fmt[idx] == '[') { + isInBracket = true; + } + else if (fmt[idx] == ']') { + isInBracket = false; + bracketTextArray.push_back(bracketText); + bracketText.clear(); + } + else if (isInBracket) { + bracketText += fmt[idx]; + } + else { + const char chr = fmt[idx]; + if (chr == '$') { + m_currencySymbol = chr; + return XLNumberType::kCurrency; + } + else if (std::string lc_curr = lc->currency_symbol; lc_curr.find(chr) != std::string::npos) { + m_currencySymbol = lc->currency_symbol; + return XLNumberType::kCurrency; + } + else if (chr == '%') { + return XLNumberType::kPercent; + } + else if (chr == 'y' || chr == 'M' || chr == 'm' || chr == 'd') { + return XLNumberType::kDate; + } + else if (chr == 'h' || chr == 's' || chr == ':') { + return XLNumberType::kDate; + } + else if (chr == '0' || chr == '#' || chr == '.') { + foundType = XLNumberType::kUnkown; + } + // use lc->int_curr_symbol ? how to get en_US? + //(iCompare(m_slocale, L"ja_jp") || iCompare(m_slocale, L"zh_tw")) && wchr == 'e' || wchr == 'g' || wchr == 'r') + } + } + for (const auto& item : bracketTextArray) { + if (item.find("F800") != std::string::npos) { + return XLNumberType::kDate; + } + else if (item.find("F400") != std::string::npos) { + return XLNumberType::kDate; + } + else { + if (size_t pos = item.find('-'); pos != std::wstring::npos) { + std::vector parts; + splitA(item, '-', parts); + if (parts.size() == 2) { + if (parts[0].size() > 1) { + m_currencySymbol = parts[0].substr(1, parts[0].size() - 1); + m_fmtLocal = parts[1]; + return XLNumberType::kCurrency; + } + } + } + else { + if (item[0] == '$') { + m_currencySymbol = item.substr(1, item.size() - 1); + return XLNumberType::kCurrency; + } + } + } + } + return foundType; +} + +XLNumberFormat::XLNumberType XLNumberFormat::tryBuiltinType() +{ + std::setlocale(LC_MONETARY, ""); + std::lconv* lc = std::localeconv(); + auto n = m_style.get().numFmtId(); // TO DEL + switch (m_style.get().numFmtId()) { + case 0: + case 1: + case 3: + return XLNumberType::kUnkown; + case 2: + case 4: + return XLNumberType::kUnkown; + case 5: + case 6: + case 7: + case 8: + m_currencySymbol = lc->currency_symbol; + return XLNumberType::kCurrency; + case 9: + case 10: + return XLNumberType::kPercent; + case 11: + case 12: + case 13: + return XLNumberType::kUnkown; + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + return XLNumberType::kDate; + case 37: + case 38: + case 39: + case 40: // accounting + return XLNumberType::kUnkown; + case 45: + case 46: + case 47: + case 48: + return XLNumberType::kDate; + } + return XLNumberType::kUnkown; +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLTable.cpp b/OpenXLSX/sources/XLTable.cpp new file mode 100644 index 00000000..e162fd72 --- /dev/null +++ b/OpenXLSX/sources/XLTable.cpp @@ -0,0 +1,729 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// ===== External Includes ===== // +#include +#include +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLSheet.hpp" +#include "XLTable.hpp" +#include "XLCellRange.hpp" +#include "XLCellReference.hpp" +#include "XLTemplates.hpp" + + +using namespace OpenXLSX; + + +XLTable::XLTable(XLXmlData* xmlData) + : m_pXmlData(xmlData), + m_sheet(XLWorksheet(xmlData->getParentNode())), + m_dataBodyRange(getWorksheet()->range()) // Fake range +{ + std::pair p = XLCellRange::topLeftBottomRight(ref()); + XLCellReference topLeft(p.first); + XLCellReference bottomRight(p.second); + + if (isHeaderVisible()) + topLeft.offset(1); + + if (isTotalVisible()) + bottomRight.offset(-1); + + m_dataBodyRange.setRangeCoordinates(topLeft, bottomRight); + + // ===== Deal with the columns + updateColumns(); + +} + +XLTable::~XLTable() +{} + +const std::string XLTable::name() const +{ + // Check if displayName is better + return (m_pXmlData->getXmlDocument()->child("table").attribute("name").value()); +} + +const std::string XLTable::ref() const +{ + return (m_pXmlData->getXmlDocument()->child("table").attribute("ref").value()); +} + +std::vector XLTable:: columnNames() const +{ + std::vector colNames; + for (auto& col : m_columns) + colNames.push_back(col.name()); + + return colNames; +} + +uint16_t XLTable::columnIndex(const std::string& name) const +{ + auto it = std::find_if(m_columns.begin(), + m_columns.end(), + [&](const auto& val){ return val.name() == name; } ); + + if (it !=m_columns.end()) + return (it - m_columns.begin()); + + return (uint16_t)(-1); +} + +const std::string XLTable::columnName(uint16_t index) const +{ + if(index > m_columns.size() - 1) + return std::string(); + + return m_columns.at(index).name(); +} + +XLTableColumn& XLTable::column(const std::string& name) +{ + uint16_t index = columnIndex(name); + if (index == (uint16_t)(-1)) + index = 0; + + return m_columns[index]; +} + +const std::vector& XLTable::columns() const +{ + return m_columns; +} + + +XLWorksheet* XLTable::getWorksheet() +{ + return &m_sheet; +} + +XLCellRange XLTable::tableRange() +{ + return getWorksheet()->range(ref()); +} + +XLTableRows XLTable::tableRows() +{ + return XLTableRows(m_pXmlData->getParentNode()->getXmlDocument()->first_child().child("sheetData"), + m_dataBodyRange.rangeCoordinates().first.row(), + m_dataBodyRange.rangeCoordinates().second.row(), + m_dataBodyRange.rangeCoordinates().first.column(), + m_dataBodyRange.rangeCoordinates().second.column(), + getWorksheet()); +} + +XLCellRange& XLTable::dataBodyRange() +{ + return m_dataBodyRange; +} + +const XLCellRange& XLTable::dataBodyRange() const +{ + return m_dataBodyRange; +} + + +bool XLTable::isHeaderVisible() const +{ + std::string header = m_pXmlData->getXmlDocument()->child("table").attribute("headerRowCount").value(); + if (header == "0") + return false; + return true; // if missing, visible +} + +bool XLTable::isTotalVisible() const +{ + std::string total = m_pXmlData->getXmlDocument()->child("table").attribute("totalsRowCount").value(); + if (total == "1") + return true; + return false; // if missing, not visible +} + + +void XLTable::setHeaderVisible(bool visible) +{ + if(visible){ // removing the attribute if visible + m_pXmlData->getXmlDocument()->child("table").remove_attribute("headerRowCount"); + setupAutofilter(); + } else { + auto node = m_pXmlData->getXmlDocument()->child("table").attribute("headerRowCount"); + if (!node) + node = m_pXmlData->getXmlDocument()->child("table").append_attribute("headerRowCount"); + node.set_value("0"); + removeAutofilter(); + } + + // TODO remove autofilter + + setHeaderLabels(); + adjustRef(); +} + + +void XLTable::setTotalVisible(bool visible) +{ + if(visible){ + m_pXmlData->getXmlDocument()->child("table").remove_attribute("totalsRowShown"); + + auto node = m_pXmlData->getXmlDocument()->child("table").attribute("totalsRowCount"); + if (!node) + node = m_pXmlData->getXmlDocument()->child("table").append_attribute("totalsRowCount"); + node.set_value("1"); + + } else { // removing the attribute if not visible + m_pXmlData->getXmlDocument()->child("table").remove_attribute("totalsRowCount"); + auto node = m_pXmlData->getXmlDocument()->child("table").attribute("totalsRowShown"); + if (!node) + node = m_pXmlData->getXmlDocument()->child("table").append_attribute("totalsRowShown"); + node.set_value("0"); + //TODO check if required to clear the worksheet when hiding total row + } + + setTotalFormulas(); + setTotalLabels(); + adjustRef(); + +} + +XLAutofilter XLTable::autofilter() +{ + + // TODO implement auto filter and adjust header masking accordingly + if(isHeaderVisible()) + setupAutofilter(); + else + setHeaderVisible(true); + + return XLAutofilter(m_pXmlData->getXmlDocument()->child("table").child("autoFilter"), + m_pXmlData); +} + +void XLTable::setupAutofilter() +{ + XMLNode node = m_pXmlData->getXmlDocument()->child("table").child("autoFilter"); + if (!node) + node = m_pXmlData->getXmlDocument()->child("table") + .insert_child_before("autoFilter", + m_pXmlData->getXmlDocument()->child("table").first_child()); + + if(!node.attribute("ref")) + node.append_attribute("ref"); + + auto p = m_dataBodyRange.rangeCoordinates(); + + node.attribute("ref").set_value(std::string(p.first.address() + + ":" + p.second.address() ).c_str()); +} + +void XLTable::removeAutofilter() +{ + m_pXmlData->getXmlDocument()->child("table").remove_child("autoFilter"); +} + +XLTableStyle XLTable::tableStyle() +{ + return XLTableStyle(m_pXmlData->getXmlDocument()->child("table").child("tableStyleInfo"), this); +} + +uint16_t XLTable::columnsCount() const +{ + return (std::stoi(m_pXmlData->getXmlDocument()->child("table") + .child("tableColumns").attribute("count").value())); +} + +uint32_t XLTable::rowsCount() const +{ + auto p = m_dataBodyRange.rangeCoordinates(); + + uint32_t firstRow = p.first.row(); + uint32_t lastRow = p.second.row(); + + return (lastRow - firstRow + 1); +} + + +void XLTable::setName(const std::string& tableName) +{ + XMLNode tableNode = m_pXmlData->getXmlDocument()->child("table"); + tableNode.attribute("name").set_value(tableName.c_str()); + tableNode.attribute("displayName").set_value(tableName.c_str()); + // TODO change the formulas in table.xml + // TODO change the formulas in the sheet + +} + + XLTableColumn& XLTable::insertColumn(const std::string& columnName, uint16_t index) + { + + // If index is not continuous, the column is appended at the last + if (index > m_columns.size()){ + XLLogError("Index " + std::to_string(index) + + " is out of column range, column will be appeneded considering " + + std::to_string(m_columns.size())); + index = m_columns.size(); + } + + + // Increment the name of the column if required + std::string colName = columnName; + bool notValid = true; + while (notValid){ + if (std::find_if(m_columns.begin(), m_columns.end(), + [&colName](const XLTableColumn& i){ return i.name() == colName; }) != m_columns.end()) + colName += INCREMENT_STRING; + else + notValid = false; + } + + // Copy the content of the bodyrange of the column, and re index tablecolumn in table.xml + for (uint16_t i = m_columns.size() - 1; i >= index; --i) { + auto& tblCol = m_columns[i]; + + // Change the index in table.xml + tblCol.setIndex(i+2); // 1 based index + + // Copy column n in n+1 in the sheet + XLCellRange src = tblCol.bodyRange(); + XLCellRange dest = src; + dest.offset(0,1); + for(uint32_t j = 0; j < src.numRows(); j++){ + if ( src[j].hasFormula()){ + dest[j].formula() = src[j].formula(); + dest[j].value().clear(); + } else { + dest[j].value() = src[j].value(); + dest[j].formula().clear(); + } + } + } + + // add new entry in table.xml + auto nextNode = m_pXmlData->getXmlDocument()->document_element() + .child("tableColumns").find_child_by_attribute("id",std::to_string(index+2).c_str()); + + XMLNode newNode; + if(nextNode) + newNode = m_pXmlData->getXmlDocument()->document_element() + .child("tableColumns").insert_child_before("tableColumn", nextNode); + else + newNode = m_pXmlData->getXmlDocument()->document_element() + .child("tableColumns").append_child("tableColumn"); + + // Add attributes to the new node + newNode.append_attribute("id").set_value(std::to_string(index + 1).c_str()); + newNode.append_attribute("name").set_value(colName.c_str()); + + // Update the range coordinates + auto p = m_dataBodyRange.rangeCoordinates(); + m_dataBodyRange.setRangeCoordinates(p.first, p.second.offset(0,1)); + + updateColumns(); // Update the member variable + + setHeaderLabels(); // relocate the labels + setTotalFormulas(); // relocate the formulas + setTotalLabels(); // reolcate the lables + adjustRef(); // update the ref + setupAutofilter(); // update the ref + + //Clear the content of the new column + for(auto& cell : m_columns[index].bodyRange()) + { + cell.value().clear(); + cell.formula().clear(); + } + + return m_columns[index]; + } + +XLTableColumn& XLTable::appendColumn(const std::string& columnName) +{ + return insertColumn(columnName, m_columns.size()); +} + +void XLTable::deleteColumn(const std::string& columnName) +{ + uint16_t index = columnIndex(columnName); + + // If index is not in the line don't do anything + if (index > m_columns.size() - 1){ + XLLogError("Index " + std::to_string(index) + + " is out of column range, no column wil be deleted"); + return; + } + + // Copy the content of the bodyrange of the column, and re index tablecolumn in table.xml + for (uint16_t i = index; i < m_columns.size(); ++i) { + auto& tblCol = m_columns[i]; + + // Change the index in table.xml + tblCol.setIndex(i); // 1 based index + tblCol.formulaUpdateDeleting(columnName); + + if (i != m_columns.size() -1){ + // Copy column n+1 in n in the sheet + XLCellRange dest = tblCol.bodyRange(); + XLCellRange src = dest; + src.offset(0,1); + for(uint32_t j = 0; j < src.numRows(); j++){ + if ( src[j].hasFormula()) { + dest[j].formula() = src[j].formula().updateDeleting(columnName); + if (dest[j].formula().hasError()){ + dest[j].formula().clear(); // shall be cleared for table formulas + dest[j].value() = "#REF!"; + } else + dest[j].value().clear(); + } else { + dest[j].formula().clear(); + dest[j].value() = src[j].value(); + } + } + } // If avoid de copy the column just outside the table + } + + // Delete the entry in table.xml + auto delNode = m_pXmlData->getXmlDocument()->document_element() + .child("tableColumns").find_child_by_attribute("name",columnName.c_str()); + m_pXmlData->getXmlDocument()->document_element() + .child("tableColumns").remove_child(delNode); + + // Update the range coordinates + auto p = m_dataBodyRange.rangeCoordinates(); + m_dataBodyRange.setRangeCoordinates(p.first, p.second.offset(0,-1)); + + updateColumns(); // Update the member variable + + setHeaderLabels(); // relocate the labels + setTotalFormulas(); // relocate the formulas + // TODO erase total value if any issue with the formula + + setTotalLabels(); // reolcate the lables + adjustRef(); // update the ref + setupAutofilter(); // update the ref + + //Clear the content of the last column, including header and total if required + XLCellRange lastCol = m_columns[ m_columns.size()-1].bodyRange(); + p = lastCol.rangeCoordinates(); + int t = 0, b = 0; + if (isHeaderVisible()) + t = -1; + if (isTotalVisible()) + b = 1; + lastCol.setRangeCoordinates(p.first.offset(t,1),p.second.offset(b,1)); + for(auto& cell : lastCol) + { + cell.value().clear(); + cell.formula().clear(); + } +} + + + XLCellRange XLTable::insertRow(uint16_t index) + { + // If index is not continuous, the column is appended at the last + if (index > rowsCount()){ + XLLogError("Index " + std::to_string(index) + + " is out of row range, row will be appeneded considering " + + std::to_string(rowsCount())); + index = rowsCount(); + } + + // Copy the content of the bodyrange of the row, index -1 to force one loop + // in case of appending + XLTableRows rows = tableRows(); + for (uint16_t i = rowsCount() - 1; i >= index - 1; --i) { + + // Copy column n in n+1 in the sheet + XLCellRange src = rows[i]; + XLCellRange dest = src; + dest.offset(1,0); + for(uint32_t j = 0; j < src.numColumns(); j++){ + if ( src[j].hasFormula()){ + dest[j].formula() = src[j].formula(); + dest[j].value().clear(); // shall be cleared for table formulas + } else { + dest[j].value() = src[j].value(); + dest[j].formula().clear(); + } + } + } + + // Update the range coordinates + auto p = m_dataBodyRange.rangeCoordinates(); + m_dataBodyRange.setRangeCoordinates(p.first, p.second.offset(1,0)); + + setHeaderLabels(); // relocate the labels + setTotalFormulas(); // relocate the formulas + setTotalLabels(); // reolcate the lables + adjustRef(); // update the ref + setupAutofilter(); // update the ref + + //Clear the content of the new column + // but keep the column formula if any + rows = tableRows(); + for(auto& cell : rows[index]) + { + cell.value().clear(); + } + + return rows[index]; + } + +XLCellRange XLTable::appendRow() +{ + return insertRow( rowsCount() ); +} + +void XLTable::deleteRow(uint32_t index ) +{ + // If index is not continuous, the column is appended at the last + if (index > rowsCount()){ + XLLogError("Index " + std::to_string(index) + + " is out of row range, no row will be deleted " + + std::to_string(rowsCount())); + return; + } + + // Copy the content of the bodyrange of the row + XLTableRows rows = tableRows(); + for (uint16_t i = index; i < rowsCount() - 2; ++i) { + + // Copy column n+1 in n in the sheet + XLCellRange dest = rows[i]; // 1 based index + XLCellRange src = dest; + src.offset(1,0); + for(uint32_t j = 0; j < src.numRows(); j++){ + if ( src[j].hasFormula()){ + dest[j].formula() = src[j].formula(); + dest[j].value().clear(); // shall be cleared for table formulas + } else { + dest[j].value() = src[j].value(); + dest[j].formula().clear(); + } + } + } + + //Clear the last row as it will be outside the table + for(auto& cell : rows[rowsCount()-1]) + { + cell.value().clear(); + cell.formula().clear(); + } + + + // Update the range coordinates + auto p = m_dataBodyRange.rangeCoordinates(); + m_dataBodyRange.setRangeCoordinates(p.first, p.second.offset(-1,0)); + + setHeaderLabels(); // relocate the labels + setTotalFormulas(); // relocate the formulas + setTotalLabels(); // reolcate the lables + adjustRef(); // update the ref + setupAutofilter(); // update the ref + +} +////////////////////////////////////////////////////////////////////// + +void XLTable::updateColumns() +{ + m_columns.clear(); + + XMLNode tblColumns = m_pXmlData->getXmlDocument()->child("table").child("tableColumns"); + + for (const XMLNode& col : tblColumns.children()) + m_columns.push_back(XLTableColumn(col, *this)); + + if (!tblColumns.attribute("count")) + tblColumns.append_attribute("count"); + + tblColumns.attribute("count").set_value(std::to_string(m_columns.size()).c_str()); +} + + +void XLTable::setColumnFormulas() const +{ + // TO be implemented + uint32_t firstRow = m_dataBodyRange.rangeCoordinates().first.row(); + uint16_t firstCol = m_dataBodyRange.rangeCoordinates().first.column(); + + for (auto& col: m_columns){ + std::string colFormula = col.columnFormula(); + if (colFormula.empty()) // if there is nothing, remove formula keep values + for (auto& cell: col.bodyRange()) + cell.formula().clear(); + else + for (auto& cell: col.bodyRange()){ + cell.formula() = colFormula; + cell.value().clear(); + } + } + +} + +void XLTable::setTotalFormulas() const +{ + uint32_t totalRow = m_dataBodyRange.rangeCoordinates().second.row() + 1; + uint16_t totalCol = m_dataBodyRange.rangeCoordinates().first.column(); + + // Hide everything if the total is hidded + if (!isTotalVisible()){ + for (auto& col: m_columns){ + m_sheet.cell(totalRow, totalCol).formula().clear(); + totalCol += 1; + } + return; + } + + for (auto& col: m_columns){ + std::string colFunc = col.totalsRowFormula(); + if (colFunc.empty()){ // if there is nothing, remove formula and values + m_sheet.cell(totalRow, totalCol).formula().clear(); + } + else + { + // Check if its exist in the list + auto it = std::find(std::begin(XLTemplate::totalsRowFunctionList), + std::end(XLTemplate::totalsRowFunctionList), colFunc); + if( it != std::end(XLTemplate::totalsRowFunctionList)){ + + std::string function = XLTemplate::sheetTotalFunctionList[ + std::distance(std::begin(XLTemplate::totalsRowFunctionList), it) + ]; + if (function != "none") + { + // get the equivalent formula in the sheet + std::string sheetFunction = "SUBTOTAL(" + function + + "," + name() + + "[" + col.name() + "])"; + m_sheet.cell(totalRow, totalCol).formula() = sheetFunction; + } else {// remove the formula if function is none + m_sheet.cell(totalRow, totalCol).formula().clear(); + } + + } + } // if col function not empty + totalCol +=1; + } + +} + +void XLTable::setTotalLabels() const +{ + uint32_t totalRow = m_dataBodyRange.rangeCoordinates().second.row() + 1; + uint16_t totalCol = m_dataBodyRange.rangeCoordinates().first.column(); + + // Hide everything if the total is hidded + if (!isTotalVisible()){ + for (auto& col: m_columns){ + m_sheet.cell(totalRow, totalCol).value().clear(); + totalCol += 1; + } + return; + } + + for (auto& col: m_columns){ + std::string colLabel = col.totalsRowLabel(); + if (colLabel.empty()){ // if there is nothing, remove values + m_sheet.cell(totalRow, totalCol).value().clear(); + } + else + m_sheet.cell(totalRow, totalCol).value() = colLabel; + + totalCol += 1; + } + +} + +void XLTable::setHeaderLabels() const +{ + uint32_t headerRow = m_dataBodyRange.rangeCoordinates().first.row() - 1; + uint16_t headerCol = m_dataBodyRange.rangeCoordinates().first.column(); + + // Hide everything if the total is hidded + if (!isHeaderVisible()){ + for (auto& col: m_columns){ + m_sheet.cell(headerRow, headerCol).value().clear(); + headerCol += 1; + } + return; + } + + for (auto& col: m_columns){ + auto test = col.name(); + m_sheet.cell(headerRow, headerCol).value() = col.name(); + + headerCol += 1; + } + +} + + +void XLTable::adjustRef() +{ + uint32_t firstRow = m_dataBodyRange.rangeCoordinates().first.row(); + uint32_t lastRow = m_dataBodyRange.rangeCoordinates().second.row(); + + uint16_t firstCol = m_dataBodyRange.rangeCoordinates().first.column(); + uint16_t lastCol = m_dataBodyRange.rangeCoordinates().second.column(); + + if(isHeaderVisible()) + firstRow -=1; + + if(isTotalVisible()) + lastRow +=1; + + std::string ref = XLCellReference::adressFromCoordinates(firstRow, firstCol, false); + ref += ":" + XLCellReference::adressFromCoordinates(lastRow, lastCol, false); + + m_pXmlData->getXmlDocument()->child("table") + .attribute("ref").set_value(ref.c_str()); + + // TODO adjust autofilter Move this elsewhere +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLTableColumn.cpp b/OpenXLSX/sources/XLTableColumn.cpp new file mode 100644 index 00000000..324c05cc --- /dev/null +++ b/OpenXLSX/sources/XLTableColumn.cpp @@ -0,0 +1,498 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +// ===== External Includes ===== // +#include +#include +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLTableColumn.hpp" +#include "XLTemplates.hpp" +#include "XLTable.hpp" +using namespace OpenXLSX; + +XLTableColumn::XLTableColumn(const XMLNode& dataNode, const XLTable& table): + m_dataNode(std::make_shared(dataNode)), + m_table(table), + m_proxyTotal(XLTableColumnTotalProxy(dataNode,"totalsRowFunction", table)), + m_proxyColumn(XLTableColumnFormulaProxy(dataNode,"calculatedColumnFormula", table)), + m_proxyLabel(XLTableColumnTotalLabelProxy(dataNode,"totalsRowLabel", table)) +{ + // TODO implement + // TODO implement == custom function + + /* TODO implement the followings + + + m_dataCellStyle = m_dataNode->attribute("dataCellStyle").value(); + m_headerRowCellStyle= m_dataNode->attribute("headerRowCellStyle").value(); + m_totalsRowFunction = m_dataNode->attribute("totalsRowFunction").value(); + m_totalsRowLabel = m_dataNode->attribute("totalsRowLabel").value(); + + m_Id = (m_dataNode->attribute("id")) ? std::stoi(m_dataNode->attribute("id").value()) : (uint32_t) -1; + m_dataDxfId = (m_dataNode->attribute("dataDxfId")) ? std::stoi(m_dataNode->attribute("dataDxfId").value()) : (uint32_t) -1; + m_headerRowDxfId = (m_dataNode->attribute("headerRowDxfId")) ? std::stoi(m_dataNode->attribute("headerRowDxfId").value()) : (uint32_t) -1; + m_totalsRowDxfId = (m_dataNode->attribute("totalsRowDxfId")) ? std::stoi(m_dataNode->attribute("totalsRowDxfId").value()) : (uint32_t) -1; + + if (m_dataNode->child("calculatedColumnFormula")) + m_calculatedColumnFormula = m_dataNode->child("calculatedColumnFormula").child_value(); + if (m_dataNode->child("totalsRowFormula")) + m_totalsRowFormula = m_dataNode->child("totalsRowFormula").child_value(); + */ + +} + +XLTableColumn::XLTableColumn(const XLTableColumn& other) + : m_dataNode(other.m_dataNode ? std::make_shared(*other.m_dataNode) : nullptr), + m_table(other.m_table), + m_proxyTotal(XLTableColumnTotalProxy((*other.m_dataNode),"totalsRowFunction", other.m_table)), + m_proxyColumn(XLTableColumnFormulaProxy((*other.m_dataNode),"calculatedColumnFormula", other.m_table)), + m_proxyLabel(XLTableColumnTotalLabelProxy(*other.m_dataNode,"totalsRowLabel", other.m_table)) +{} + +XLTableColumn::XLTableColumn(XLTableColumn&& other) noexcept + : m_dataNode(std::move(other.m_dataNode)), + m_table(other.m_table), + m_proxyTotal(std::move(other.m_proxyTotal)), + m_proxyColumn( std::move(other.m_proxyColumn)), + m_proxyLabel( std::move(other.m_proxyLabel)) +{} + + +XLTableColumn::~XLTableColumn() = default; + +XLTableColumn& XLTableColumn::operator=(const XLTableColumn& other) +{ + if (&other != this) { + auto temp = XLTableColumn(other); + std::swap(*this, temp); + } + return *this; +} + +XLTableColumn& XLTableColumn::operator=(XLTableColumn&& other) noexcept +{ + if (&other != this) { + m_dataNode = std::move(other.m_dataNode); + } + return *this; +} + + +std::string XLTableColumn::name() const +{ + return std::string(m_dataNode->attribute("name").value()); +} + +void XLTableColumn::setName(const std::string& columnName) const +{ + m_dataNode->attribute("name").set_value(columnName.c_str()); + // TODO change the formulas in table.xml + // TODO change the formulas in the sheet +} + +XLTableColumnProxy& XLTableColumn::totalsRowFormula() +{ + return m_proxyTotal; +} + +const XLTableColumnProxy& XLTableColumn::totalsRowFormula() const +{ + return m_proxyTotal; +} + +void XLTableColumn::clearTotalsRowFormula() +{ + m_proxyTotal.clear(); +} + +XLTableColumnFormulaProxy& XLTableColumn::columnFormula() +{ + return m_proxyColumn; +} + +const XLTableColumnFormulaProxy& XLTableColumn::columnFormula() const +{ + return m_proxyColumn; +} + +void XLTableColumn::clearColumnFormula() +{ + m_proxyColumn.clear(); +} + +XLTableColumnProxy& XLTableColumn::totalsRowLabel() +{ + return m_proxyLabel; +} + +const XLTableColumnProxy& XLTableColumn::totalsRowLabel() const +{ + return m_proxyLabel; +} + +void XLTableColumn::clearTotalsRowLabel() +{ + m_proxyLabel.clear(); +} + +XLCellRange XLTableColumn::bodyRange() const +{ + uint16_t colIndex = m_table.columnIndex(name()); + XLCellRange tableRange = m_table.dataBodyRange(); + auto p = tableRange.rangeCoordinates(); + uint16_t firstCol = p.first.column(); + XLCellReference tl = p.first; + XLCellReference br = p.second; + + tl.setColumn(firstCol + colIndex); + br.setColumn(firstCol + colIndex); + + tableRange.setRangeCoordinates(tl, br); + return tableRange; +} + +uint16_t XLTableColumn::index() const +{ + return std::stoi(std::string(m_dataNode->attribute("id").value())); +} + +void XLTableColumn::setIndex(uint16_t index) +{ + m_dataNode->attribute("id").set_value(std::to_string(index).c_str()); +} + +void XLTableColumn::formulaUpdateDeleting(const std::string& toBeDeleted) +{ + std::string formula = columnFormula(); + + if(formula.empty()) + return; + // Regex that will find all the occurence of toBeDeleted excepted inside quote + // TODO deal with nested quotes + const std::regex re(toBeDeleted + R"&((?=([^"]*"[^"]*")*[^"]*$))&"); + std::smatch m; // unused + + if (std::regex_search(formula, m, re)) + m_dataNode->remove_child("calculatedColumnFormula"); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// XLTableColumnProxy +// +///////////////////////////////////////////////////////////////////////////////////////// + + +/** + * @details Constructor + * @pre The attr pointer must not be nullptr and must point to valid objects. + * @post A valid XLCellValueProxy has been created. + */ +XLTableColumnProxy::XLTableColumnProxy(const XMLNode& node, const std::string& attr, const XLTable& table ) + : m_node(std::make_shared(node)), + m_attribute(attr), + m_table(table) +{ + assert(node); // NOLINT +} + +/** + * @details Destructor. Default implementation has been used. + * @pre + * @post + */ +XLTableColumnProxy::~XLTableColumnProxy() = default; + +/** + * @details Copy constructor. Default implementation has been used. + * @pre + * @post + */ +XLTableColumnProxy::XLTableColumnProxy(const XLTableColumnProxy& other) + : m_node(other.m_node ? std::make_shared(*other.m_node) : nullptr), + m_attribute(other.m_attribute), + m_table(other.m_table) +{} + +/** + * @details Move constructor. Default implementation has been used. + * @pre + * @post + */ +XLTableColumnProxy::XLTableColumnProxy(XLTableColumnProxy&& other) noexcept + : m_node(std::move(other.m_node)), + m_attribute(std::move(other.m_attribute)), + m_table(std::move(other.m_table)) +{} + +/** + * @details Copy assignment operator. The function is implemented in terms of the templated + * value assignment operators, i.e. it is the XLCellValue that is that is copied, + * not the object itself. + * @pre + * @post + */ +XLTableColumnProxy& XLTableColumnProxy::operator=(const XLTableColumnProxy& other) +{ + if (&other != this) { + *this = other.getFormula(); + } + + return *this; +} + + +/** + * @details Move assignment operator. Default implementation has been used. + * @pre + * @post + */ +XLTableColumnProxy& XLTableColumnProxy::operator=(XLTableColumnProxy&& other) noexcept +{ + if (&other != this) { + m_node = std::move(other.m_node); + m_attribute = std::move(other.m_attribute); + } + + return *this; +} + + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// XLTableColumnTotalProxy +// +///////////////////////////////////////////////////////////////////////////////////////// + +XLTableColumnProxy& XLTableColumnTotalProxy::operator=(const std::string& formula) +{ + setFormula(formula); + return *this; +} + +/** + * @details Clear the contents of the cell. This removes all children of the cell node. + * @pre The m_cellNode must not be null, and must point to a valid XML cell node object. + * @post The cell node must be valid, but empty. + */ +void XLTableColumnTotalProxy::clear() +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + m_node->remove_attribute(m_attribute.c_str()); + m_table.setTotalFormulas(); + +} + +/** + * @details Set the the formula in the corresponding attribute. + * This is private helper function for setting the attr + * directly in the underlying XML file. + * @pre The m_attribute must not be null, and must point to a valid XMLNode object. + * @post The underlying attribute has been updated correctly, representing a string value. + */ +void XLTableColumnTotalProxy::setFormula(const std::string& formula) +{ + // ===== Check that the m_cellNode is valid. + assert(m_node); // NOLINT + auto node = m_node->attribute(m_attribute.c_str()); + if (!node) + node = m_node->append_attribute(m_attribute.c_str()); + + // If empty string, we remove the formula + if(formula.empty()){ + clear(); + return; + } + + auto it = std::find(std::begin(XLTemplate::totalsRowFunctionList), + std::end(XLTemplate::totalsRowFunctionList), formula); + if( it == std::end(XLTemplate::totalsRowFunctionList)){ + XLLogError("The formula \"" + formula + "\" is not available for total Row function"); + return; + } + + m_node->attribute(m_attribute.c_str()).set_value(formula.c_str()); + m_table.setTotalFormulas(); +} + +/** + * @details Get a copy of the XLCellValue object for the cell. This is private helper function for returning an + * XLCellValue object corresponding to the cell value. + * @pre The m_cellNode must not be null, and must point to a valid XMLNode object. + * @post No changes should be made. + */ +std::string XLTableColumnTotalProxy::getFormula() const +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + auto node = m_node->attribute(m_attribute.c_str()); + if (!node) + return std::string(); + + return std::string(m_node->attribute(m_attribute.c_str()).value()); +} +///////////////////////////////////////////////////////////////////////////////////////// +// +// XLTableColumnFormulaProxy +// +///////////////////////////////////////////////////////////////////////////////////////// + +XLTableColumnProxy& XLTableColumnFormulaProxy::operator=(const std::string& formula) +{ + setFormula(formula); + return *this; +} + +void XLTableColumnFormulaProxy::clear() +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + m_node->remove_child(m_attribute.c_str()); + m_table.setColumnFormulas(); + +} + +void XLTableColumnFormulaProxy::setFormula(const std::string& formula) +{ + // ===== Check that the m_cellNode is valid. + assert(m_node); // NOLINT + auto node = m_node->child(m_attribute.c_str()); + if (!node) + node = m_node->append_child(m_attribute.c_str()); + + // If empty string, we remove the formula + if(formula.empty()){ + clear(); + return; + } + + m_node->child(m_attribute.c_str()).text().set(formula.c_str()); + m_table.setColumnFormulas(); +} + +std::string XLTableColumnFormulaProxy::getFormula() const +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + auto node = m_node->child(m_attribute.c_str()); + if (!node) + return std::string(); + + return std::string(m_node->child(m_attribute.c_str()).text().get()); + +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// XLTableColumnTotalLabelProxy +// +///////////////////////////////////////////////////////////////////////////////////////// + +XLTableColumnProxy& XLTableColumnTotalLabelProxy::operator=(const std::string& formula) +{ + setFormula(formula); + return *this; +} + +/** + * @details Clear the contents of the cell. This removes all children of the cell node. + * @pre The m_cellNode must not be null, and must point to a valid XML cell node object. + * @post The cell node must be valid, but empty. + */ +void XLTableColumnTotalLabelProxy::clear() +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + m_node->remove_attribute(m_attribute.c_str()); + m_table.setTotalLabels(); + +} + +/** + * @details Set the the formula in the corresponding attribute. + * This is private helper function for setting the attr + * directly in the underlying XML file. + * @pre The m_attribute must not be null, and must point to a valid XMLNode object. + * @post The underlying attribute has been updated correctly, representing a string value. + */ +void XLTableColumnTotalLabelProxy::setFormula(const std::string& formula) +{ + // ===== Check that the m_cellNode is valid. + assert(m_node); // NOLINT + auto node = m_node->attribute(m_attribute.c_str()); + if (!node) + node = m_node->append_attribute(m_attribute.c_str()); + + // If empty string, we remove the formula + if(formula.empty()){ + clear(); + return; + } + + m_node->attribute(m_attribute.c_str()).set_value(formula.c_str()); + m_table.setTotalLabels(); +} + +/** + * @details Get a copy of the XLCellValue object for the cell. This is private helper function for returning an + * XLCellValue object corresponding to the cell value. + * @pre The m_cellNode must not be null, and must point to a valid XMLNode object. + * @post No changes should be made. + */ +std::string XLTableColumnTotalLabelProxy::getFormula() const +{ + // ===== Check that the m_attribute is valid. + assert(m_node); // NOLINT + auto node = m_node->attribute(m_attribute.c_str()); + if (!node) + return std::string(); + + return std::string(m_node->attribute(m_attribute.c_str()).value()); +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLTableRows.cpp b/OpenXLSX/sources/XLTableRows.cpp new file mode 100644 index 00000000..629b9e2e --- /dev/null +++ b/OpenXLSX/sources/XLTableRows.cpp @@ -0,0 +1,261 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +// ===== External Includes ===== // +#include +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLTableRows.hpp" + +using namespace OpenXLSX; + + + +// ========== XLTableRowIterator ================================================ // + + + XLTableRowIterator:: XLTableRowIterator(const XLTableRows& tableRows, XLIteratorLocation loc): + m_range (XLCellRange( *tableRows.m_dataNode, + XLCellReference (tableRows.m_firstRow, tableRows.m_firstCol), + XLCellReference (tableRows.m_firstRow, tableRows.m_lastCol), + tableRows.m_worksheet)), + m_firstIterRow(tableRows.m_firstRow), + m_lastIterRow(tableRows.m_lastRow) +{ + if (loc == XLIteratorLocation::End) + m_range = XLCellRange( *tableRows.m_dataNode, + XLCellReference (tableRows.m_lastRow+1, tableRows.m_firstCol), + XLCellReference (tableRows.m_lastRow+1, tableRows.m_lastCol), + tableRows.m_worksheet); +} + + XLTableRowIterator::~ XLTableRowIterator() = default; + + XLTableRowIterator::XLTableRowIterator(const XLTableRowIterator& other) + : m_range(other.m_range) +{ } + +XLTableRowIterator::XLTableRowIterator(XLTableRowIterator&& other) noexcept = default; + + +XLTableRowIterator& XLTableRowIterator::operator=(const XLTableRowIterator& other) +{ + if (&other != this) { + auto temp = XLTableRowIterator(other); + std::swap(*this, temp); + } + return *this; +} + +XLTableRowIterator& XLTableRowIterator::operator=(XLTableRowIterator&& other) noexcept = default; + + + +XLTableRowIterator& XLTableRowIterator::operator++() +{ + m_range.offset(1); + + return *this; +} + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator XLTableRowIterator::operator++(int) // NOLINT +{ + auto oldIter(*this); + ++(*this); + return oldIter; +} + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator::reference XLTableRowIterator::operator*() +{ + return m_range; +} + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator::pointer XLTableRowIterator::operator->() +{ + return &m_range; +} + + +/** + * @details + * @pre + * @post + */ +bool XLTableRowIterator::operator==(const XLTableRowIterator& rhs) const +{ + return m_range == rhs.m_range; +} + +/** + * @details + * @pre + * @post + */ +bool XLTableRowIterator::operator!=(const XLTableRowIterator& rhs) const +{ + return !(*this == rhs); +} + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator::operator bool() const +{ + return false; +} + + +// ========== XLTableRows =================================================== // + +XLTableRows::XLTableRows(const XMLNode& dataNode, + uint32_t firstRow, + uint32_t lastRow, + uint16_t firstCol, + uint16_t lastCol, + const XLWorksheet* wks): + XLRowRange(dataNode, firstRow, lastRow, wks), + m_firstCol(firstCol), + m_lastCol(lastCol) +{} + +/** + * @brief Copy Constructor + */ +XLTableRows::XLTableRows(const XLTableRows& other): + XLRowRange(other), + m_firstCol(other.m_firstCol), + m_lastCol(other.m_lastCol) +{} + +/** + * @brief Move Constructor + */ +XLTableRows::XLTableRows(XLTableRows&& other) noexcept = default; + +XLTableRows::~XLTableRows() = default; + +/** + * @brief Copy assignment operator. + */ +XLTableRows& XLTableRows::operator=(const XLTableRows& other) +{ + if (&other != this) { + auto temp = XLTableRows(other); + std::swap(*this, temp); + } + return *this; +} + +/** + * @brief Move assignment operator. + */ +XLTableRows& XLTableRows::operator=(XLTableRows&& other) noexcept = default; + + +XLCellRange XLTableRows::operator[](uint32_t index) const +{ + uint32_t row = m_firstRow + index; + + if (row > m_lastRow) + return XLCellRange(*m_dataNode, + XLCellReference (m_firstRow, m_firstCol), + XLCellReference (m_lastRow, m_lastCol), + m_worksheet); + + return XLCellRange(*m_dataNode, + XLCellReference (row, m_firstCol), + XLCellReference (row, m_lastCol), + m_worksheet); +} + + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator XLTableRows::begin() +{ + return XLTableRowIterator(*this, XLIteratorLocation::Begin); +} + +/** + * @details + * @pre + * @post + */ +XLTableRowIterator XLTableRows::end() +{ + return XLTableRowIterator(*this, XLIteratorLocation::End); +} + +/** + * @details + * @pre + * @post + */ +uint32_t XLTableRows::numRows() const +{ + return m_firstRow + 1 - m_lastRow; +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLTableStyle.cpp b/OpenXLSX/sources/XLTableStyle.cpp new file mode 100644 index 00000000..6ca9cba5 --- /dev/null +++ b/OpenXLSX/sources/XLTableStyle.cpp @@ -0,0 +1,223 @@ +/* + + ____ ____ ___ ____ ____ ____ ___ + 6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M' + 8P Y8 `MM. d' MM 6M' ` `MM. d' +6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d' +MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d' +MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd +MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM. +MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM. +YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. + 8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM. + YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_ + MM + MM + _MM_ + + Written by Akira SHIMAHARA + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of the author nor the + names of any contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +// ===== External Includes ===== // +#include +#include + +// ===== OpenXLSX Includes ===== // +#include "XLTableStyle.hpp" +#include "XLTemplates.hpp" +#include "XLTable.hpp" +#include "XLCommandQuery.hpp" + +using namespace OpenXLSX; + +XLTableStyle::XLTableStyle(const XMLNode& dataNode, XLTable* table): + m_dataNode(std::make_unique(dataNode)), + m_table(table) +{ + /* TODO implement the custom table style (with external xml file if required) + */ + +} + +XLTableStyle::XLTableStyle(const XLTableStyle& other) + : m_dataNode(other.m_dataNode ? std::make_unique(*other.m_dataNode) : nullptr), + m_table(other.m_table) +{} + +XLTableStyle::XLTableStyle(XLTableStyle&& other) noexcept + : m_dataNode(std::move(other.m_dataNode)), + m_table(other.m_table) +{} + + +XLTableStyle::~XLTableStyle() = default; + +XLTableStyle& XLTableStyle::operator=(const XLTableStyle& other) +{ + if (&other != this) { + auto temp = XLTableStyle(other); + std::swap(*this, temp); + } + return *this; +} + +XLTableStyle& XLTableStyle::operator=(XLTableStyle&& other) noexcept +{ + if (&other != this) { + m_dataNode = std::move(other.m_dataNode); + m_table = other.m_table; + } + return *this; +} + +bool XLTableStyle::columnStripes() const +{ + if(std::string(m_dataNode->attribute("showColumnStripes").value()) == "1") + return true; + + return false; +} + +void XLTableStyle::showColumnStripes(bool show) const +{ + auto node = m_dataNode->attribute("showColumnStripes"); + if(!node) + node = m_dataNode->append_attribute("showColumnStripes"); + + if(show) + node.set_value("1"); + else + node.set_value("0"); +} + + +bool XLTableStyle::rowStripes() const +{ + if(std::string(m_dataNode->attribute("showRowStripes").value()) == "1") + return true; + + return false; +} + +void XLTableStyle::showRowStripes(bool show) const +{ + auto node = m_dataNode->attribute("showRowStripes"); + if(!node) + node = m_dataNode->append_attribute("showRowStripes"); + + if(show) + node.set_value("1"); + else + + + node.set_value("0"); +} + +bool XLTableStyle::firstColumnHighlighted() const +{ + if(std::string(m_dataNode->attribute("showFirstColumn").value()) == "1") + return true; + + return false; +} + +void XLTableStyle::showFirstColumnHighlighted(bool show) const +{ + auto node = m_dataNode->attribute("showFirstColumn"); + if(!node) + node = m_dataNode->append_attribute("showFirstColumn"); + + if(show) + node.set_value("1"); + else + + + node.set_value("0"); +} + +bool XLTableStyle::lastColumnHighlighted() const +{ + if(std::string(m_dataNode->attribute("showLastColumn").value()) == "1") + return true; + + return false; +} + +void XLTableStyle::showLastColumnHighlighted(bool show) const +{ + auto node = m_dataNode->attribute("showLastColumn"); + if(!node) + node = m_dataNode->append_attribute("showLastColumn"); + + if(show) + node.set_value("1"); + else + + + node.set_value("0"); +} + + +const std::string XLTableStyle::style() const +{ + return std::string(m_dataNode->attribute("name").value()); +} + +void XLTableStyle::setStyle(const std::string& style) const +{ + if (!isTableStyleAvailable(style)){ + XLLogError("Specified style \"" + style + "\" is not available in this file"); + return; + } + + m_dataNode->attribute("name").set_value(style.c_str()); +} + +bool XLTableStyle::isTableStyleAvailable(const std::string& style) const +{ + // check if the style is in the standard default list + auto it = std::find(std::begin(XLTemplate::defaultTableStyle), + std::end(XLTemplate::defaultTableStyle), style); + if( it != std::end(XLTemplate::defaultTableStyle)) + return true; + + // If not check also in the custom styles in the file + m_table->m_pXmlData->getParentDoc(); + + XLQuery query(XLQueryType::QueryXmlData); + query.setParam("xmlPath", "xl/styles.xml"); + + XLXmlData* p = m_table->m_pXmlData->getParentDoc()->execQuery(query).template result(); + if (p) + for (const auto& node : p->getXmlDocument()->child("styleSheet").child("tableStyles")) + if( style == std::string(node.attribute("name").value())) + return true; + + return false; + +} \ No newline at end of file diff --git a/OpenXLSX/sources/XLWorkbook.cpp b/OpenXLSX/sources/XLWorkbook.cpp index e47bc5ad..13093f36 100644 --- a/OpenXLSX/sources/XLWorkbook.cpp +++ b/OpenXLSX/sources/XLWorkbook.cpp @@ -50,9 +50,14 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM. #include // ===== OpenXLSX Includes ===== // +#include "XLCellReference.hpp" #include "XLDocument.hpp" +#include "XLNamedRange.hpp" #include "XLSheet.hpp" +#include "XLTable.hpp" #include "XLWorkbook.hpp" +#include "XLCellRange.hpp" + using namespace OpenXLSX; @@ -67,6 +72,7 @@ namespace { return doc.document_element().child("sheets"); } + } // namespace /** @@ -83,25 +89,10 @@ XLWorkbook::~XLWorkbook() = default; /** * @details */ -XLSheet XLWorkbook::sheet(const std::string& sheetName) +XLSheet XLWorkbook::sheet(const std::string& sheetName) const { - // ===== First determine if the sheet exists. - if (xmlDocument().document_element().child("sheets").find_child_by_attribute("name", sheetName.c_str()) == nullptr) - throw XLInputError("Sheet \"" + sheetName + "\" does not exist"); - - // ===== Find the sheet data corresponding to the sheet with the requested name - std::string xmlID = - xmlDocument().document_element().child("sheets").find_child_by_attribute("name", sheetName.c_str()).attribute("r:id").value(); - - XLQuery pathQuery(XLQueryType::QuerySheetRelsTarget); - pathQuery.setParam("sheetID", xmlID); - auto xmlPath = parentDoc().execQuery(pathQuery).result(); - - // Some spreadsheets use absolute rather than relative paths in relationship items. - if (xmlPath.substr(0,4) == "/xl/") xmlPath = xmlPath.substr(4); - - XLQuery xmlQuery(XLQueryType::QueryXmlData); - xmlQuery.setParam("xmlPath", "xl/" + xmlPath); + XLQuery xmlQuery(XLQueryType::QuerySheetFromName); + xmlQuery.setParam("sheetName", sheetName); return XLSheet(parentDoc().execQuery(xmlQuery).result()); } @@ -109,7 +100,7 @@ XLSheet XLWorkbook::sheet(const std::string& sheetName) * @details Create a vector with sheet nodes, retrieve the node at the requested index, get sheet name and return the * corresponding sheet object. */ -XLSheet XLWorkbook::sheet(uint16_t index) +XLSheet XLWorkbook::sheet(uint16_t index) const { if (index < 1 || index > sheetCount()) throw XLInputError("Sheet index is out of bounds"); return sheet( @@ -119,36 +110,73 @@ XLSheet XLWorkbook::sheet(uint16_t index) /** * @details */ -XLWorksheet XLWorkbook::worksheet(const std::string& sheetName) +XLWorksheet XLWorkbook::worksheet(const std::string& sheetName) const { return sheet(sheetName).get(); } -/** - * @details - */ -XLChartsheet XLWorkbook::chartsheet(const std::string& sheetName) +XLTable XLWorkbook::table(const std::string& tableName) const { - return sheet(sheetName).get(); + // Throw an exception if table not exist + XLQuery xmlQuery(XLQueryType::QueryTableFromName); + xmlQuery.setParam("tableName", tableName); + + XLXmlData* tableItem = (parentDoc().execQuery(xmlQuery).result()); + + return XLTable(tableItem); } -/** - * @details - */ -bool XLWorkbook::hasSharedStrings() const +XLTable XLWorkbook::table(uint16_t index) const { - return true;//parentDoc().executeQuery(XLQuerySharedStrings()).sharedStrings() != nullptr; + XLQuery query(XLQueryType::QueryTableFromIndex); + query.setParam("index", index); + + return XLTable(parentDoc().execQuery(query).result()); +} + + +XLNamedRange XLWorkbook::namedRange(const std::string& rangeName) const +{ + auto ElmtNode = xmlDocument().document_element().child("definedNames").find_child_by_attribute("name", rangeName.c_str()); + + // ===== First determine if the named range exists. + if (ElmtNode == nullptr) + throw XLInputError("Defined Name \"" + rangeName + "\" does not exist"); + + std::string reference = ElmtNode.child_value(); + std::string test = reference.substr(0, 4); + if (reference.substr(0, 4) == "#REF") + throw XLInputError("Defined Name \"" + rangeName + "\" is pointing to an invalid reference"); + + // ===== Find the sheet where defineName is declared (but not the reference sheet). Could be empty if global name + // seems to be sheetId - 1. it will be set to 0 if no sheet is specified (global) + uint32_t localSheetId = 0; + std::string strLocalSheetId = ElmtNode.attribute("localSheetId").value(); + if (strLocalSheetId.size() > 0) + localSheetId = static_cast(std::stoul(strLocalSheetId)) + 1; + + // Retrieve the worksheet name and top left - bottom right reference + // TODO deal with non continuous range =Feuil1!$J$10:$K$15;Feuil1!$J$20:$K$24 + // TODO to be moved elsewhere (Utils ?) + std::string::size_type n = reference.find("!"); + const std::string sheetName = reference.substr(0, n); + std::string ref = reference.substr(n+1); + + auto pair = XLCellRange::topLeftBottomRight(ref); + + return XLNamedRange(rangeName, reference, localSheetId, + this->worksheet(sheetName).range(XLCellReference(pair.first), XLCellReference(pair.second))); } /** * @details */ -XLSharedStrings XLWorkbook::sharedStrings() +XLChartsheet XLWorkbook::chartsheet(const std::string& sheetName) const { - XLQuery query(XLQueryType::QuerySharedStrings); - return parentDoc().execQuery(query).result(); + return sheet(sheetName).get(); } + /** * @details */ @@ -177,8 +205,26 @@ void XLWorkbook::deleteSheet(const std::string& sheetName) }); // ===== If this is the last worksheet in the workbook, throw an exception. - if (worksheetCount == 1 && sheetType == XLContentType::Worksheet) - throw XLInputError("Invalid operation. There must be at least one worksheet in the workbook."); + if (worksheetCount == 1 && sheetType == XLContentType::Worksheet){ + //throw XLInputError("Invalid operation. There must be at least one worksheet in the workbook."); + XLLogError("Unable to delete the worksheet \""+ sheetName + "\". There must be at least one worksheet in the workbook."); + return; + } + + // ===== eliminate the tables contained in the worksheet. + if (sheetType == XLContentType::Worksheet){ + XLQuery queryXml(XLQueryType::QuerySheetFromName); + queryXml.setParam("sheetName", sheetName); + XLXmlData* pSheet = parentDoc().execQuery(queryXml).result(); + std::vector tblNames; + for (XLXmlData* item : pSheet->getChildNodes()){ + if (item->getXmlType() == XLContentType::Table) + tblNames.emplace_back(item->getName()); + } + + for (std::string& tblName : tblNames) + deleteTable(tblName); + } // ===== Delete the sheet data as well as the sheet node from Workbook.xml parentDoc().execCommand(XLCommand(XLCommandType::DeleteSheet) @@ -193,48 +239,129 @@ void XLWorkbook::deleteSheet(const std::string& sheetName) /** * @details */ -void XLWorkbook::addWorksheet(const std::string& sheetName) +XLWorksheet XLWorkbook::addWorksheet(const std::string& sheetName) { // ===== If a sheet with the given name already exists, throw an exception. if (xmlDocument().document_element().child("sheets").find_child_by_attribute("name", sheetName.c_str())) throw XLInputError("Sheet named \"" + sheetName + "\" already exists."); - // ===== Create new internal (workbook) ID for the sheet - auto internalID = createInternalSheetID(); - // ===== Create xml file for new worksheet and add metadata to the workbook file. parentDoc().execCommand(XLCommand(XLCommandType::AddWorksheet) - .setParam("sheetName", sheetName) - .setParam("sheetPath", "/xl/worksheets/sheet" + std::to_string(internalID) + ".xml")); - prepareSheetMetadata(sheetName, internalID); + .setParam("sheetName", sheetName)); + + return worksheet(sheetName); +} + +void XLWorkbook::deleteNamedRange(const std::string& rangeName, + uint32_t localSheetId) +{ + auto ElmtNode = xmlDocument().document_element().child("definedNames").find_child_by_attribute("name", rangeName.c_str()); + + // ===== First determine if the named range exists. + if (ElmtNode == nullptr) + throw XLInputError("Defined Name \"" + rangeName + "\" does not exist"); + + xmlDocument().document_element().child("definedNames"). + remove_child(xmlDocument().document_element().child("definedNames"). + find_child_by_attribute("name", rangeName.c_str())); + } /** * @details - * @todo If the original sheet's tabSelected attribute is set, ensure it is un-set in the clone. + * @todo check that the name fit excel limitation (no space) XLNamedRange INCREMENT_STRING */ -void XLWorkbook::cloneSheet(const std::string& existingName, const std::string& newName) +XLNamedRange XLWorkbook::addNamedRange(const std::string& rangeName, + const std::string& reference, + uint32_t localSheetId) { - parentDoc().execCommand(XLCommand(XLCommandType::CloneSheet) - .setParam("sheetID", sheetID(existingName)) - .setParam("cloneName", newName)); + auto ElmtNode = xmlDocument().document_element().child("definedNames").find_child_by_attribute("name", rangeName.c_str()); + + // ===== First determine if the named range exists. + if (ElmtNode) + throw XLInputError("Defined Name \"" + rangeName + "\" already exists."); + + // Retrieve the worksheet name and top left - bottom right reference and make it safe + // TODO deal with non continuous range =Feuil1!$J$10:$K$15;Feuil1!$J$20:$K$24 + // TODO to be moved elsewhere (Utils ?) + std::string::size_type n = reference.find("!"); + if (n == reference.size()) + throw XLInputError("Invalid reference: \"" + rangeName + "\" no sheet name was found."); + + const std::string sheetName = reference.substr(0, n); + + if (!sheetExists(sheetName)) + throw XLInputError("Sheet with name \"" + sheetName + "\" doesn't exist."); + + std::string ref = reference.substr(n+1); + n = ref.find(":"); + std::string topLeft = ref.substr(0, n); + + // Construct a safe reference that shall be absolute + XLCellReference cellRef(topLeft); + std::string safeReference = sheetName + "!" + cellRef.address(true); + if (n + auto shtNode = xmlDocument().document_element().child("sheets"); + if(!shtNode) + throw XLInputError("Unable to find tag in \"" + sheetName + "\"."); + + xmlDocument().document_element().insert_child_after("definedNames", shtNode); + } + + auto node = xmlDocument().document_element().child("definedNames").append_child("definedName"); + + // ===== append the required attributes to the newly created sheet node. + node.append_attribute("name") = rangeName.c_str(); + if (localSheetId) + node.append_attribute("localSheetId") = localSheetId - 1; + + node.text().set(safeReference.c_str()); + + return namedRange(rangeName); +} + +/** + * @details ref shall be "B1:K12" + * @todo check that the name fit excel limitation (no space) + */ +XLTable XLWorkbook::addTable(const std::string& sheetName, const std::string& tableName, + const std::string& reference) +{ + // ===== Create xml file for new worksheet and add metadata to the workbook file. + parentDoc().execCommand(XLCommand(XLCommandType::AddTable) + .setParam("worksheet", sheetName) + .setParam("tableName", tableName) + .setParam("reference", reference)); + return table(tableName); +} + +void XLWorkbook::deleteTable(const std::string& tableName) +{ + // ===== Delete the sheet data as well as the sheet node from Workbook.xml + parentDoc().execCommand(XLCommand(XLCommandType::DeleteTable) + .setParam("tableName", tableName)); + } + /** * @details + * @todo If the original sheet's tabSelected attribute is set, ensure it is un-set in the clone. */ -uint16_t XLWorkbook::createInternalSheetID() +void XLWorkbook::cloneSheet(const std::string& existingName, const std::string& newName) { - return static_cast(std::max_element(xmlDocument().document_element().child("sheets").children().begin(), - xmlDocument().document_element().child("sheets").children().end(), - [](const XMLNode& a, const XMLNode& b) { - return a.attribute("sheetId").as_uint() < b.attribute("sheetId").as_uint(); - }) - ->attribute("sheetId") - .as_uint() + - 1); + parentDoc().execCommand(XLCommand(XLCommandType::CloneSheet) + .setParam("sheetID", sheetID(existingName)) + .setParam("cloneName", newName)); } + /** * @details */ @@ -262,7 +389,9 @@ std::string XLWorkbook::sheetVisibility(const std::string& sheetID) const /** * @details */ -void XLWorkbook::prepareSheetMetadata(const std::string& sheetName, uint16_t internalID) +void XLWorkbook::prepareSheetMetadata(const std::string& sheetName, + uint16_t internalID, + uint16_t sheetID) { // ===== Add new child node to the "sheets" node. auto node = sheetsNode(xmlDocument()).append_child("sheet"); @@ -270,7 +399,7 @@ void XLWorkbook::prepareSheetMetadata(const std::string& sheetName, uint16_t int // ===== append the required attributes to the newly created sheet node. std::string sheetPath = "/xl/worksheets/sheet" + std::to_string(internalID) + ".xml"; node.append_attribute("name") = sheetName.c_str(); - node.append_attribute("sheetId") = std::to_string(internalID).c_str(); + node.append_attribute("sheetId") = std::to_string(sheetID).c_str(); XLQuery query(XLQueryType::QuerySheetRelsID); query.setParam("sheetPath", sheetPath); @@ -451,6 +580,12 @@ unsigned int XLWorkbook::chartsheetCount() const return static_cast(chartsheetNames().size()); } +unsigned int XLWorkbook::tableCount() const +{ + return parentDoc().execQuery(XLQuery(XLQueryType::QueryTableCount)). + result(); +} + /** * @details */ @@ -497,6 +632,22 @@ std::vector XLWorkbook::chartsheetNames() const return results; } + +std::vector XLWorkbook::tableNames() const +{ + std::vector results; + + for (unsigned int i = 0 ; i < tableCount(); ++i){ + XLQuery query(XLQueryType::QueryTableFromIndex); + query.setParam("index", i); + results.emplace_back(parentDoc() + .execQuery(query).result()->getName()); + } + + return results; + +} + /** * @details */ @@ -523,6 +674,12 @@ bool XLWorkbook::chartsheetExists(const std::string& sheetName) const return std::find(chsNames.begin(), chsNames.end(), sheetName) != chsNames.end(); } +bool XLWorkbook::tableExists(const std::string& tableName) const +{ + auto tblNames = tableNames(); + return std::find(tblNames.begin(), tblNames.end(), tableName) != tblNames.end(); +} + /** * @details The UpdateSheetName member function searches throug the usages of the old name and replaces with the * new sheet name. diff --git a/OpenXLSX/sources/XLXmlData.cpp b/OpenXLSX/sources/XLXmlData.cpp index 04bb54f4..3f07350e 100644 --- a/OpenXLSX/sources/XLXmlData.cpp +++ b/OpenXLSX/sources/XLXmlData.cpp @@ -55,11 +55,19 @@ using namespace OpenXLSX; /** * @details */ -XLXmlData::XLXmlData(OpenXLSX::XLDocument* parentDoc, const std::string& xmlPath, const std::string& xmlId, OpenXLSX::XLContentType xmlType) +XLXmlData::XLXmlData(OpenXLSX::XLDocument* parentDoc, + const std::string& xmlPath, + const std::string& xmlId, + const std::string& name, + OpenXLSX::XLContentType xmlType, + OpenXLSX::XLXmlData* parentNode) : m_parentDoc(parentDoc), m_xmlPath(xmlPath), m_xmlID(xmlId), m_xmlType(xmlType), + m_name(name), + m_parentNode(parentNode), + m_childNodes(), m_xmlDoc(std::make_unique()) { m_xmlDoc->reset(); @@ -75,19 +83,52 @@ XLXmlData::~XLXmlData() = default; */ void XLXmlData::setRawData(const std::string& data) { + //pugi::parse_ws_pcdata pugi::format_raw m_xmlDoc->load_string(data.c_str(), pugi::parse_default | pugi::parse_ws_pcdata); } +void XLXmlData::setXmlID(const std::string& xmlID) +{ + m_xmlID = xmlID; +} + +void XLXmlData::setName(const std::string& name) +{ + m_name = name; +} + + +void XLXmlData::setParentNode(XLXmlData* parentNode) +{ + m_parentNode = parentNode; +} + +void XLXmlData::addChildNode(XLXmlData* childNode) +{ + m_childNodes.push_back(childNode); +} + /** * @details */ std::string XLXmlData::getRawData() const { std::ostringstream ostr; - getXmlDocument()->save(ostr, "", pugi::format_raw); + + XMLNode decl = m_xmlDoc->prepend_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; + decl.append_attribute("encoding") = "UTF-8"; + decl.append_attribute("standalone") = "yes"; + + getXmlDocument()->save(ostr, "", pugi::format_raw , pugi::encoding_utf8); + + //pugi::parse_ws_pcdata pugi::format_raw + std::string test = ostr.str(); return ostr.str(); } + + /** * @details */ @@ -120,6 +161,11 @@ std::string XLXmlData::getXmlID() const return m_xmlID; } +const std::string& XLXmlData::getName() const +{ + return m_name; +} + /** * @details */ @@ -128,6 +174,16 @@ XLContentType XLXmlData::getXmlType() const return m_xmlType; } +XLXmlData* XLXmlData::getParentNode() const +{ + return m_parentNode; +} + +std::vector& XLXmlData::getChildNodes() +{ + return m_childNodes; +} + /** * @details */ diff --git a/README.md b/README.md index 11f0ad45..4c36b003 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,8 @@ which have been implemented and should be working properly: - Copy worksheets - Cell ranges and iterators - Row ranges and iterators +- Named range +- Basis of table support (work in progress) Features related to formatting, plots and figures have not been implemented, and are not planned to be in the near future.