#include #include #include using namespace std; using namespace OpenXLSX; /** * @brief demo how to iterate over all worksheet comments by sequence in XML - for few comments, this is substantially faster than testing each cell for a non-empty comment * @param doc the XLDocument * @return n/a */ void printAllDocumentComments(XLDocument const & doc) { for( size_t i = 1; i <= doc.workbook().worksheetCount(); ++i ) { auto wks = doc.workbook().worksheet(i); if( wks.hasComments() ) { std::cout << "worksheet(" << i << ") with name \"" << wks.name() << "\" has comments" << std::endl; XLComments wksComments = wks.comments(); size_t commentCount = wksComments.count(); for( size_t idx = 0; idx < commentCount; ++idx ) { XLComment com = wksComments.get(idx); std::cout << "comment in " << com.ref() << ": \"" << com.text() << "\" (author: " << wksComments.author(com.authorId()) << ")" << std::endl; } } } } int main() { cout << "********************************************************************************\n"; cout << "DEMO PROGRAM #01: Basic Usage and XLWorksheet protection\n"; cout << "********************************************************************************\n"; // This example program illustrates basic usage of OpenXLSX, for example creation of a new workbook, and read/write // of cell values. // First, create a new document and access the sheet named 'Sheet1'. // New documents contain a single worksheet named 'Sheet1' XLDocument doc; doc.create("./Demo01.xlsx", XLForceOverwrite); auto wks = doc.workbook().worksheet("Sheet1"); // The individual cells can be accessed by using the .cell() method on the worksheet object. // The .cell() method can take the cell address as a string, or alternatively take a XLCellReference // object. By using an XLCellReference object, the cells can be accessed by row/column coordinates. // The .cell() method returns an XLCell object. // The .value() method of an XLCell object can be used for both getting and setting the cell value. // Setting the value of a cell can be done by using the assignment operator on the .value() method // as shown below. Alternatively, a .set() can be used. The cell values can be floating point numbers, // integers, strings, and booleans. It can also accept XLDateTime objects, but this requires special // handling (see later). wks.cell("A1").value() = 3.14159265358979323846; wks.cell("B1").value() = 42; wks.cell("C1").value() = " Hello OpenXLSX! "; wks.cell("D1").value() = true; wks.cell("E1").value() = std::sqrt(-2); // Result is NAN, resulting in an error value in the Excel spreadsheet. // As mentioned, the .value() method can also be used for getting tha value of a cell. // The .value() method returns a proxy object that cannot be copied or assigned, but // it can be implicitly converted to an XLCellValue object, as shown below. // Unfortunately, it is not possible to use the 'auto' keyword, so the XLCellValue // type has to be explicitly stated. XLCellValue A1 = wks.cell("A1").value(); XLCellValue B1 = wks.cell("B1").value(); XLCellValue C1 = wks.cell("C1").value(); XLCellValue D1 = wks.cell("D1").value(); XLCellValue E1 = wks.cell("E1").value(); // The cell value can be implicitly converted to a basic c++ type. However, if the type does not // match the type contained in the XLCellValue object (if, for example, floating point value is // assigned to a std::string), then an XLValueTypeError exception will be thrown. // To check which type is contained, use the .type() method, which will return a XLValueType enum // representing the type. As a convenience, the .typeAsString() method returns the type as a string, // which can be useful when printing to console. double vA1 = wks.cell("A1").value(); int vB1 = wks.cell("B1").value(); std::string vC1 = wks.cell("C1").value(); bool vD1 = wks.cell("D1").value(); double vE1 = wks.cell("E1").value(); cout << "Cell A1: (" << A1.typeAsString() << ") " << vA1 << endl; cout << "Cell B1: (" << B1.typeAsString() << ") " << vB1 << endl; cout << "Cell C1: (" << C1.typeAsString() << ") " << vC1 << endl; cout << "Cell D1: (" << D1.typeAsString() << ") " << vD1 << endl; cout << "Cell E1: (" << E1.typeAsString() << ") " << vE1 << endl << endl; // Instead of using implicit (or explicit) conversion, the underlying value can also be retrieved // using the .get() method. This is a templated member function, which takes the desired type // as a template argument. cout << "Cell A1: (" << A1.typeAsString() << ") " << A1.get() << endl; cout << "Cell B1: (" << B1.typeAsString() << ") " << B1.get() << endl; cout << "Cell C1: (" << C1.typeAsString() << ") " << C1.get() << endl; cout << "Cell D1: (" << D1.typeAsString() << ") " << D1.get() << endl; cout << "Cell E1: (" << E1.typeAsString() << ") " << E1.get() << endl << endl; // XLCellValue objects can also be copied and assigned to other cells. This following line // will copy and assign the value of cell C1 to cell E1. Note that only the value is copied; // other cell properties of the target cell remain unchanged. wks.cell("F1").value() = wks.cell(XLCellReference("C1")).value(); XLCellValue F1 = wks.cell("F1").value(); cout << "Cell F1: (" << F1.typeAsString() << ") " << F1.get() << endl << endl; // Date/time values is a special case. In Excel, date/time values are essentially just a // 64-bit floating point value, that is rendered as a date/time string using special // formatting. When retrieving the cell value, it is just a floating point value, // and there is no way to identify it as a date/time value. // If, however, you know it to be a date time value, or if you want to assign a date/time // value to a cell, you can use the XLDateTime class, which falilitates conversion between // Excel date/time serial numbers, and the std::tm struct, that is used to store // date/time data. See https://en.cppreference.com/w/cpp/chrono/c/tm for more information. // An XLDateTime object can be created from a std::tm object: std::tm tm; tm.tm_year = 121; tm.tm_mon = 8; tm.tm_mday = 1; tm.tm_hour = 12; tm.tm_min = 0; tm.tm_sec = 0; XLDateTime dt (tm); // XLDateTime dt (43791.583333333299); // The std::tm object can be assigned to a cell value in the same way as shown previously. wks.cell("G1").value() = dt; // And as seen previously, an XLCellValue object can be retrieved. However, the object // will just contain a floating point value; there is no way to identify it as a date/time value. XLCellValue G1 = wks.cell("G1").value(); cout << "Cell G1: (" << G1.typeAsString() << ") " << G1.get() << endl; // If it is known to be a date/time value, the cell value can be converted to an XLDateTime object. auto result = G1.get(); // The Excel date/time serial number can be retrieved using the .serial() method. cout << "Cell G1: (" << G1.typeAsString() << ") " << result.serial() << endl; // Using the .tm() method, the corresponding std::tm object can be retrieved. auto tmo = result.tm(); cout << "Cell G1: (" << G1.typeAsString() << ") " << std::asctime(&tmo); std::cout << std::endl; std::cout << "XLWorksheet comments demo" << std::endl; std::cout << "=========================" << std::endl; std::cout << "wks.hasComments() is " << (wks.hasComments() ? " TRUE" : "FALSE") << ", wks.hasVmlDrawing() is " << (wks.hasVmlDrawing() ? " TRUE" : "FALSE") << ", wks.hasTables() is " << (wks.hasTables() ? " TRUE" : "FALSE") << std::endl; XLComments &wksComments = wks.comments(); // fetch comments (and create if missing) std::cout << "wks.comments() is " << (wksComments.valid() ? "valid" : "not valid") << std::endl; XLTables &wksTables = wks.tables(); // fetch tables (and create if missing) std::cout << "wks.tables() is " << (wksTables.valid() ? "valid" : "not valid") << std::endl; std::cout << "wks.hasComments() is " << (wks.hasComments() ? " TRUE" : "FALSE") << ", wks.hasVmlDrawing() is " << (wks.hasVmlDrawing() ? " TRUE" : "FALSE") << ", wks.hasTables() is " << (wks.hasTables() ? " TRUE" : "FALSE") << std::endl; // 2025-01-17: LibreOffice displays all comments with the same, LibreOffice, author - regardless of author id - to be tested with MS Office std::string authors[10] { "Edgar Allan Poe", "Agatha Christie", "J.R.R. Tolkien", "David Eddings", "Daniel Suarez", /**/ "Mary Shelley", "George Orwell", "Stanislaw Lem", "Ray Bradbury", "William Shakespeare" }; for( std::string author : authors ) { wksComments.addAuthor( author ); uint16_t authorCount = wksComments.authorCount(); std::cout << "wksComments.authorCount is " << authorCount << std::endl; std::cout << "wksComments.author(" << (authorCount - 1) << ") is " << wksComments.author(authorCount - 1) << std::endl; } wksComments.deleteAuthor(7); wksComments.deleteAuthor(5); wksComments.deleteAuthor(3); uint16_t authorCount = wksComments.authorCount(); std::cout << "wksComments.authorCount is " << authorCount << " after deleting authors 7, 5 and 3" << std::endl; for( uint16_t index = 0; index < authorCount; ++index ) std::cout << "wksComments.author(" << (index) << ") is " << wksComments.author(index) << std::endl; wksComments.set("A1", "this comment is for author #0", 0); wksComments.set("B2", "this comment is for author #1", 1); wksComments.shape("B2").style().show(); // bit cumbersome to access, but it reflects the XML: [..] wksComments.set("C3", "this comment is for author #2", 2); wksComments.set("C3", "this is an updated comment for author #2", 2); // overwrite a comment wksComments.shape("C3").style().show(); wksComments.set("D1", "this comment is for author #4", 4); wksComments.set("E2", "this comment is also for author #4", 4); bool deleteSuccess = wksComments.deleteComment("E2"); std::cout << "Deleting the comment in cell E2 " << ( deleteSuccess ? "succeeded" : "did not succeed" ) << "." << std::endl; std::cout << std::endl; std::cout << "the comment in cell C4 is \"" << wksComments.get("C4") << "\"" << std::endl; std::cout << "the comment in cell B2 is \"" << wksComments.get("B2") << "\"" << std::endl; std::cout << std::endl; printAllDocumentComments(doc); std::cout << std::endl; std::cout << "XLWorksheet protection settings demo" << std::endl; std::cout << "====================================" << std::endl; std::cout << "Default worksheet " << wks.name() << " protection settings: " << std::endl /**/ << wks.sheetProtectionSummary() << std::endl; std::string password = "test"; std::string pwHash = ExcelPasswordHashAsString(password); wks.setPassword(password); // set password directly wks.setPasswordHash(pwHash); // set password via hash wks.protectSheet(); // protects all cells that have protected=true // protectObjects(bool set = false); // not sure what this would do // protectScenarios(bool set = false); // not sure what this would do wks.allowSelectUnlockedCells(); // user can select unlocked cells (default) wks.denySelectLockedCells(); // user can not select any cells except those that are explicitly unlocked // wks.denySelectUnlockedCells(); // somehow this setting REALLY does not make any sense // wks.clearPassword(); // clear a password - this will not(!) disable sheet protection but will no longer require a password to unprotect the sheet in a GUI // wks.protectSheet(false); // remove sheet protection - this will disable the other protect settings // wks.clearSheetProtection(); // this will ALL protection settings, including the password hash std::cout << "---" << std::endl; std::cout << "Configured worksheet " << wks.name() << " protection settings: " << std::endl /**/ << wks.sheetProtectionSummary() << std::endl; // Create a new cell format that unlocks cells XLCellFormats & cellFormats = doc.styles().cellFormats(); XLStyleIndex newCellFormatIndex = cellFormats.create( cellFormats[ wks.cell("C1").cellFormat() ] ); // cellFormats[ newCellFormatIndex ].setApplyProtection( true ); // seems to be irrelevant // cellFormats[ newCellFormatIndex ].setLocked( true ); // locked seems to be default behavior if attribute is not set cellFormats[ newCellFormatIndex ].setLocked( false ); // unprotect a cell // wks.cell("D1").setCellFormat(newCellFormatIndex); wks.range("C1:D1").setFormat(newCellFormatIndex); // unlock two cells std::cout << "====================================" << std::endl; std::cout << "==> the worksheet " << wks.name() << " is now protected with the password " << password << std::endl; std::cout << std::endl; std::cout << std::endl; std::cout << "XLWorksheet testing for empty cells demo.." << std::endl; std::cout << "==========================================" << std::endl; std::cout << " using an XLCellIterator (recommended)" << std::endl; std::cout << " -----------------------------------" << std::endl; XLCellRange cellRange = wks.range("A1:G2"); for (XLCellIterator cellIt = cellRange.begin(); cellIt != cellRange.end(); ++cellIt) { if (!cellIt.cellExists()) continue; // prevent cell creation by access for non-existing cells std::cout << "cell " << cellIt.address() << " exists!" << std::endl; } std::cout << std::endl; std::cout << " using XLSheet::findCell (lazy method, not recommended)" << std::endl; std::cout << " NOTE: this method should only be used for tests of individual cells when performance is not an issue" << std::endl; std::cout << " ------------------------------------------------------" << std::endl; wks.cell("D2").formula().set("=A1+B1"); wks.cell("E2").formula().set(""); // setting an empty (zero-length) formula string will delete the formula node for (int row = 1; row < 4; ++row) { for (int col = 1; col < 8; ++col) { XLCellReference ref(row, col); XLCellAssignable c = wks.findCell(ref); if (c.empty()) continue; // It is up to the implementation to decide which tests shall be performed on a cell that exists - here are a few exemplary tests std::string valueAsString = c.getString(); if (valueAsString == "" && !c.hasFormula()) cout << "cell " << ref.address() << " exists but has no value or formula" << std::endl; else { // cout << "cell " << ref.address() << " value is |" << c.value() << "|" << std::endl; if (valueAsString.length() > 0) cout << "cell " << ref.address() << " value is |" << valueAsString << "|" << std::endl; if (c.hasFormula()) { if (c.formula().get().length() > 0) cout << "cell " << ref.address() << " formula is |" << c.formula().get() << "|" << std::endl; else // NOTE: this state can not be created with OpenXLSX setter functions, but possibly by other libraries / programs cout << "cell " << ref.address() << " has an empty formula" << std::endl; } } } } doc.save(); doc.close(); return 0; }