start making reader/writer actually use package structure

This commit is contained in:
Thomas Fussell 2014-06-13 17:06:23 -04:00
parent ff84734e2d
commit 65962951f1
15 changed files with 289 additions and 105 deletions

View File

@ -0,0 +1,98 @@
// Copyright (c) 2014 Thomas Fussell
// Copyright (c) 2010-2014 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include "../styles/style.hpp"
#include "../common/types.hpp"
namespace xlnt {
struct date;
struct datetime;
struct time;
class cell_value
{
public:
enum class type
{
null,
numeric,
string,
formula,
boolean,
error
};
std::string to_string() const;
type get_data_type() const;
void set_null();
cell_value &operator=(const cell_value &rhs);
cell_value &operator=(bool value);
cell_value &operator=(int value);
cell_value &operator=(double value);
cell_value &operator=(long int value);
cell_value &operator=(long long value);
cell_value &operator=(long double value);
cell_value &operator=(const std::string &value);
cell_value &operator=(const char *value);
cell_value &operator=(const date &value);
cell_value &operator=(const time &value);
cell_value &operator=(const datetime &value);
bool operator==(const cell_value &comparand) const;
bool operator==(std::nullptr_t) const;
bool operator==(bool comparand) const;
bool operator==(int comparand) const;
bool operator==(double comparand) const;
bool operator==(const std::string &comparand) const;
bool operator==(const char *comparand) const;
bool operator==(const date &comparand) const;
bool operator==(const time &comparand) const;
bool operator==(const datetime &comparand) const;
friend bool operator==(std::nullptr_t, const cell_value &cell);
friend bool operator==(bool comparand, const cell_value &cell);
friend bool operator==(int comparand, const cell_value &cell);
friend bool operator==(double comparand, const cell_value &cell);
friend bool operator==(const std::string &comparand, const cell_value &cell);
friend bool operator==(const char *comparand, const cell_value &cell);
friend bool operator==(const date &comparand, const cell_value &cell);
friend bool operator==(const time &comparand, const cell_value &cell);
friend bool operator==(const datetime &comparand, const cell_value &cell);
};
inline std::ostream &operator<<(std::ostream &stream, const xlnt::cell_value &value)
{
return stream << value.to_string();
}
} // namespace xlnt

View File

@ -26,6 +26,12 @@
namespace xlnt {
enum calendar
{
windows_1900,
mac_1904
};
struct date
{
static date today();

View File

@ -96,7 +96,7 @@ public:
~zip_file();
std::string get_file_contents(const std::string &filename);
std::string get_file_contents(const std::string &filename) const;
void set_file_contents(const std::string &filename, const std::string &contents);

View File

@ -32,6 +32,8 @@ namespace xlnt {
class workbook;
class worksheet;
class document_properties;
class zip_file;
class reader
{
@ -44,6 +46,8 @@ public:
static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table);
static std::vector<std::string> read_shared_string(const std::string &xml_string);
static std::string read_dimension(const std::string &xml_string);
static document_properties read_properties_core(const std::string &xml_string);
static std::vector<std::pair<std::string,std::string>> read_sheets(const zip_file &archive);
};
} // namespace xlnt

View File

@ -63,6 +63,9 @@ public:
void read_workbook_settings(const std::string &xml_source);
void create_relationship(const std::string &id, const std::string &target, const std::string &type);
relationship get_relationship(const std::string &id) const;
//getters
worksheet get_active_sheet();
bool get_optimized_write() const;

View File

@ -24,12 +24,15 @@
#pragma once
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace xlnt {
class workbook;
class worksheet;
class document_properties;
class writer
{
@ -44,6 +47,8 @@ public:
static std::string write_workbook_rels(const workbook &wb);
static std::string write_worksheet_rels(worksheet ws, int drawing_id, int comments_id);
static std::string write_string_table(const std::vector<std::string> &string_table);
static std::string write_properties_core(const document_properties &prop);
static std::string write_properties_app(const workbook &wb);
};
} // namespace xlnt

View File

@ -44,3 +44,5 @@ const std::string download_url = "https://github.com/tfussell/xlnt/archive/maste
#include "common/exceptions.hpp"
#include "reader/reader.hpp"
#include "common/string_table.hpp"
#include "common/zip_file.hpp"
#include "workbook/document_properties.hpp"

View File

@ -0,0 +1,10 @@
#include "workbook/document_properties.hpp"
namespace xlnt {
document_properties::document_properties() : created(1900, 1, 1), modified(1900, 1, 1)
{
}
} // namespace xlnt

View File

@ -7,9 +7,36 @@
#include "worksheet/range_reference.hpp"
#include "workbook/workbook.hpp"
#include "worksheet/worksheet.hpp"
#include "workbook/document_properties.hpp"
#include "common/zip_file.hpp"
namespace xlnt {
std::vector<std::pair<std::string, std::string>> reader::read_sheets(const zip_file &archive)
{
auto xml_source = archive.get_file_contents("xl/workbook.xml");
pugi::xml_document doc;
doc.load(xml_source.c_str());
std::vector<std::pair<std::string, std::string>> sheets;
for(auto sheet_node : doc.child("workbook").child("sheets").children("sheet"))
{
std::string id = sheet_node.attribute("r:id").as_string();
std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name));
}
return sheets;
}
document_properties reader::read_properties_core(const std::string &xml_string)
{
document_properties props;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("cp:coreProperties");
props.creator = root_node.child("dc:creator").text().as_string();
return props;
}
std::string reader::read_dimension(const std::string &xml_string)
{
pugi::xml_document doc;

View File

@ -314,6 +314,11 @@ bool workbook::load(const std::string &filename)
auto workbook_relationships = reader::read_relationships(f.get_file_contents("xl/_rels/workbook.xml.rels"));
for(auto relationship : workbook_relationships)
{
create_relationship(relationship.first, relationship.second.first, relationship.second.second);
}
pugi::xml_document doc;
doc.load(f.get_file_contents("xl/workbook.xml").c_str());
@ -334,16 +339,34 @@ bool workbook::load(const std::string &filename)
for(auto sheet_node : sheets_node.children("sheet"))
{
auto relation_id = sheet_node.attribute("r:id").as_string();
std::string relation_id = sheet_node.attribute("r:id").as_string();
auto ws = create_sheet(sheet_node.attribute("name").as_string());
std::string sheet_filename("xl/");
sheet_filename += workbook_relationships[relation_id].first;
sheet_filename += get_relationship(relation_id).get_target_uri();
xlnt::reader::read_worksheet(ws, f.get_file_contents(sheet_filename).c_str(), shared_strings);
}
return true;
}
void workbook::create_relationship(const std::string &id, const std::string &target, const std::string &type)
{
d_->relationships_.push_back(relationship(type, id, target));
}
relationship workbook::get_relationship(const std::string &id) const
{
for(auto &rel : d_->relationships_)
{
if(rel.get_id() == id)
{
return rel;
}
}
throw std::runtime_error("");
}
int workbook::get_base_year() const
{
return d_->date_1904_ ? 1904 : 1900;
@ -497,12 +520,18 @@ bool workbook::save(const std::string &filename)
{"/xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"},
{"/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"},
{"/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"},
{"/xl/worksheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"},
{"/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"},
{"/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"}
}
};
int ws_index = 1;
for(auto ws : *this)
{
auto sheet_filename = "/xl/worksheets/sheet" + std::to_string(ws_index++) + ".xml";
content_types.second[sheet_filename] = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
}
f.set_file_contents("[Content_Types].xml", writer::write_content_types(content_types));
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> root_rels =
@ -511,38 +540,35 @@ bool workbook::save(const std::string &filename)
{"rId2", {"docProps/core.xml", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"}},
{"rId1", {"xl/workbook.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"}}
};
f.set_file_contents("_rels/.rels", writer::write_relationships(root_rels));
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> workbook_rels =
{
{"rId1", {"worksheets/sheet1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"}},
{"rId1", {"sharedStrings.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"}},
{"rId2", {"styles.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"}},
{"rId3", {"theme/theme1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"}}
};
f.set_file_contents("xl/_rels/workbook.xml.rels", writer::write_relationships(workbook_rels));
pugi::xml_document doc;
auto root_node = doc.append_child("workbook");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto sheets_node = root_node.append_child("sheets");
int i = 0;
ws_index = 2;
for(auto ws : *this)
{
auto sheet_filename = "worksheets/sheet" + std::to_string(ws_index++) + ".xml";
workbook_rels.push_back({"rId" + std::to_string(ws_index + 1), {sheet_filename, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"}});
}
f.set_file_contents("xl/_rels/workbook.xml.rels", writer::write_relationships(workbook_rels));
int i = 0;
for(auto ws : *this)
{
auto sheet_node = sheets_node.append_child("sheet");
sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
sheet_node.append_attribute("sheetId").set_value(std::to_string(i + 1).c_str());
sheet_node.append_attribute("r:id").set_value((std::string("rId") + std::to_string(i + 1)).c_str());
std::string filename = "xl/worksheets/sheet";
f.set_file_contents(filename + std::to_string(i + 1) + ".xml", xlnt::writer::write_worksheet(ws));
i++;
}
std::stringstream ss;
doc.save(ss);
f.set_file_contents("xl/workbook.xml", ss.str());
f.set_file_contents("xl/workbook.xml", writer::write_workbook(*this));
return true;
}

View File

@ -35,16 +35,32 @@ std::string writer::write_string_table(const std::vector<std::string> &string_ta
return ss.str();
}
std::string writer::write_workbook_rels(const workbook &/*wb*/)
std::string writer::write_properties_core(const document_properties &/*prop*/)
{
static const std::vector<std::pair<std::string, std::pair<std::string, std::string>>> rels =
return "";
}
std::string writer::write_properties_app(const workbook &/*wb*/)
{
return "";
}
std::string writer::write_workbook_rels(const workbook &wb)
{
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> rels =
{
{"rId1", {"worksheets/sheet1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"}},
{"rId2", {"sharedStrings.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"}},
{"rId3", {"styles.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"}},
{"rId4", {"theme/theme1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"}}
{"rId1", {"sharedStrings.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"}},
{"rId2", {"styles.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"}},
{"rId3", {"theme/theme1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"}}
};
int i = 0;
for(auto worksheet : wb)
{
rels.push_back({"rId" + std::to_string(i + 4), {"worksheets/sheet1.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"}});
i++;
}
return write_relationships(rels);
}

View File

@ -57,9 +57,9 @@ zip_file::~zip_file()
change_state(state::closed);
}
std::string zip_file::get_file_contents(const std::string &filename)
std::string zip_file::get_file_contents(const std::string &filename) const
{
return files_[filename];
return files_.at(filename);
}
void zip_file::set_file_contents(const std::string &filename, const std::string &contents)

View File

@ -565,41 +565,41 @@ public:
static test_props suite_test_props;
static CxxTest::List Tests_test_props = { 0, 0 };
CxxTest::StaticSuiteDescription suiteDescription_test_props( "../../tests/test_props.hpp", 8, "test_props", suite_test_props, Tests_test_props );
CxxTest::StaticSuiteDescription suiteDescription_test_props( "../../tests/test_props.hpp", 10, "test_props", suite_test_props, Tests_test_props );
static class TestDescription_suite_test_props_test_read_properties_core : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_read_properties_core() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 25, "test_read_properties_core" ) {}
TestDescription_suite_test_props_test_read_properties_core() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 13, "test_read_properties_core" ) {}
void runTest() { suite_test_props.test_read_properties_core(); }
} testDescription_suite_test_props_test_read_properties_core;
static class TestDescription_suite_test_props_test_read_sheets_titles : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_read_sheets_titles() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 36, "test_read_sheets_titles" ) {}
TestDescription_suite_test_props_test_read_sheets_titles() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 30, "test_read_sheets_titles" ) {}
void runTest() { suite_test_props.test_read_sheets_titles(); }
} testDescription_suite_test_props_test_read_sheets_titles;
static class TestDescription_suite_test_props_test_read_properties_core2 : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_read_properties_core2() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 57, "test_read_properties_core2" ) {}
TestDescription_suite_test_props_test_read_properties_core2() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 42, "test_read_properties_core2" ) {}
void runTest() { suite_test_props.test_read_properties_core2(); }
} testDescription_suite_test_props_test_read_properties_core2;
static class TestDescription_suite_test_props_test_read_sheets_titles2 : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_read_sheets_titles2() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 64, "test_read_sheets_titles2" ) {}
TestDescription_suite_test_props_test_read_sheets_titles2() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 50, "test_read_sheets_titles2" ) {}
void runTest() { suite_test_props.test_read_sheets_titles2(); }
} testDescription_suite_test_props_test_read_sheets_titles2;
static class TestDescription_suite_test_props_test_write_properties_core : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_write_properties_core() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 72, "test_write_properties_core" ) {}
TestDescription_suite_test_props_test_write_properties_core() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 62, "test_write_properties_core" ) {}
void runTest() { suite_test_props.test_write_properties_core(); }
} testDescription_suite_test_props_test_write_properties_core;
static class TestDescription_suite_test_props_test_write_properties_app : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_props_test_write_properties_app() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 84, "test_write_properties_app" ) {}
TestDescription_suite_test_props_test_write_properties_app() : CxxTest::RealTestDescription( Tests_test_props, suiteDescription_test_props, 73, "test_write_properties_app" ) {}
void runTest() { suite_test_props.test_write_properties_app(); }
} testDescription_suite_test_props_test_write_properties_app;

View File

@ -4,91 +4,78 @@
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include "helpers/path_helper.hpp"
#include "helpers/helper.hpp"
class test_props : public CxxTest::TestSuite
{
public:
class TestReaderProps
{
void setup_class(int cls)
{
//cls.genuine_filename = os.path.join(DATADIR, "genuine", "empty.xlsx");
//cls.archive = ZipFile(cls.genuine_filename, "r", ZIP_DEFLATED);
}
void teardown_class(int cls)
{
//cls.archive.close();
}
};
void test_read_properties_core()
{
//content = archive.read(ARC_CORE)
// prop = read_properties_core(content)
// TS_ASSERT_EQUALS(prop.creator, "*.*")
// eacute = chr(233)
// TS_ASSERT_EQUALS(prop.last_modified_by, "Aur" + eacute + "lien Camp" + eacute + "as")
// TS_ASSERT_EQUALS(prop.created, datetime(2010, 4, 9, 20, 43, 12))
// TS_ASSERT_EQUALS(prop.modified, datetime(2011, 2, 9, 13, 49, 32))
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
TS_ASSERT_EQUALS(prop.creator, "*.*");
unsigned char eacute = 233;
std::string modified_by = "Aur";
modified_by.append(1, eacute);
modified_by += "lien Camp";
modified_by.append(1, eacute);
modified_by += "as";
TS_ASSERT_EQUALS(prop.last_modified_by, modified_by);
TS_ASSERT_EQUALS(prop.created, xlnt::datetime(2010, 4, 9, 20, 43, 12));
TS_ASSERT_EQUALS(prop.modified, xlnt::datetime(2011, 2, 9, 13, 49, 32));
}
void test_read_sheets_titles()
{
//content = archive.read(ARC_WORKBOOK);
//sheet_titles = read_sheets_titles(content);
//TS_ASSERT_EQUALS(sheet_titles, \
// ["Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"]);
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
int i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))
{
TS_ASSERT_EQUALS(sheet.second, expected_titles[i++]);
}
}
// Just tests that the correct date / time format is returned from LibreOffice saved version
void test_read_properties_core2()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
TS_ASSERT_EQUALS(prop.excel_base_date, xlnt::calendar::windows_1900);
}
void setup_class(int cls)
void test_read_sheets_titles2()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
int i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))
{
//cls.genuine_filename = os.path.join(DATADIR, "genuine", "empty_libre.xlsx")
// cls.archive = ZipFile(cls.genuine_filename, "r", ZIP_DEFLATED)
}
void teardown_class(int cls)
{
//cls.archive.close()
}
void test_read_properties_core2()
{
// content = archive.read(ARC_CORE)
// prop = read_properties_core(content)
// TS_ASSERT_EQUALS(prop.excel_base_date, CALENDAR_WINDOWS_1900)
}
void test_read_sheets_titles2()
{
//content = archive.read(ARC_WORKBOOK)
// sheet_titles = read_sheets_titles(content)
// TS_ASSERT_EQUALS(sheet_titles, \
// ["Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"])
TS_ASSERT_EQUALS(sheet.second, expected_titles[i++]);
}
}
void test_write_properties_core()
{
//prop.creator = "TEST_USER"
// prop.last_modified_by = "SOMEBODY"
// prop.created = datetime(2010, 4, 1, 20, 30, 00)
// prop.modified = datetime(2010, 4, 5, 14, 5, 30)
// content = write_properties_core(prop)
// assert_equals_file_content(
// os.path.join(DATADIR, "writer", "expected", "core.xml"),
// content)
xlnt::document_properties prop;
prop.creator = "TEST_USER";
prop.last_modified_by = "SOMEBODY";
prop.created = xlnt::datetime(2010, 4, 1, 20, 30, 00);
prop.modified = xlnt::datetime(2010, 4, 5, 14, 5, 30);
auto content = xlnt::writer::write_properties_core(prop);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/core.xml", content));
}
void test_write_properties_app()
{
//wb = Workbook()
// wb.create_sheet()
// wb.create_sheet()
// content = write_properties_app(wb)
// assert_equals_file_content(
// os.path.join(DATADIR, "writer", "expected", "app.xml"),
// content)
xlnt::workbook wb;
wb.create_sheet();
wb.create_sheet();
auto content = xlnt::writer::write_properties_app(wb);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/app.xml", content));
}
};

View File

@ -38,7 +38,7 @@ public:
void test_read_standard_workbook_from_fileobj()
{
auto path = PathHelper::GetDataDirectory() + "/genuine/empty.xlsx";
std::ifstream fo(path);
std::ifstream fo(path, std::ios::binary);
xlnt::workbook wb;
wb.load(fo);
}