mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
fixed some more tests
This commit is contained in:
parent
99d609ce3a
commit
14cb4e88a4
|
@ -3,6 +3,7 @@
|
|||
#include <iostream>
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "TemporaryFile.h"
|
||||
#include "../xlnt.h"
|
||||
|
||||
class DumpTestSuite : public CxxTest::TestSuite
|
||||
|
@ -13,34 +14,38 @@ public:
|
|||
|
||||
}
|
||||
|
||||
void _get_test_filename()
|
||||
{
|
||||
NamedTemporaryFile test_file("w", "xlnt.", ".xlsx", false);
|
||||
test_file.close();
|
||||
return test_file.name;
|
||||
}
|
||||
|
||||
_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in range(1, 18279));
|
||||
//_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in range(1, 18279));
|
||||
|
||||
void test_dump_sheet_title()
|
||||
{
|
||||
test_filename = _get_test_filename();
|
||||
wb = Workbook(optimized_write = True);
|
||||
ws = wb.create_sheet(title = "Test1");
|
||||
wb.save(test_filename);
|
||||
wb2 = load_workbook(test_filename, True);
|
||||
xlnt::workbook wb;
|
||||
wb.optimized_write(true);
|
||||
auto ws = wb.create_sheet("Test1");
|
||||
wb.save(temp_file.GetFilename());
|
||||
xlnt::workbook wb2;
|
||||
wb2.load(temp_file.GetFilename());
|
||||
ws = wb2.get_sheet_by_name("Test1");
|
||||
TS_ASSERT_EQUALS("Test1", ws.title);
|
||||
TS_ASSERT_EQUALS("Test1", ws.get_title());
|
||||
}
|
||||
|
||||
void test_dump_sheet()
|
||||
{
|
||||
test_filename = _get_test_filename();
|
||||
wb = Workbook(optimized_write = True);
|
||||
ws = wb.create_sheet();
|
||||
letters = [get_column_letter(x + 1) for x in range(20)];
|
||||
expected_rows = [];
|
||||
for(auto row : range(20))
|
||||
auto test_filename = temp_file.GetFilename();
|
||||
|
||||
xlnt::workbook wb;
|
||||
wb.optimized_write(true);
|
||||
auto ws = wb.create_sheet();
|
||||
|
||||
std::vector<std::string> letters;
|
||||
|
||||
for(int i = 0; i < 20; i++)
|
||||
{
|
||||
letters.push_back(xlnt::cell::get_column_letter(i + 1));
|
||||
}
|
||||
|
||||
std::vector<xlnt::cell> expected_rows;
|
||||
|
||||
for(int row = 0; row < 20; row++)
|
||||
{
|
||||
expected_rows.append(["%s%d" % (letter, row + 1) for letter in letters]);
|
||||
for(auto row in range(20))
|
||||
|
@ -80,41 +85,43 @@ public:
|
|||
|
||||
void test_table_builder()
|
||||
{
|
||||
sb = StringTableBuilder();
|
||||
StringTableBuilder sb;
|
||||
|
||||
result = {"a":0, "b" : 1, "c" : 2, "d" : 3};
|
||||
std::unordered_map<std::string, int> result = {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}};
|
||||
|
||||
for(auto letter in sorted(result.keys()))
|
||||
for(auto pair : result)
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
for x in range(5)
|
||||
sb.add(pair.first);
|
||||
|
||||
auto table = sb.get_table();
|
||||
|
||||
try
|
||||
{
|
||||
sb.add(letter)
|
||||
result_items = result.items();
|
||||
}
|
||||
|
||||
table = dict(sb.get_table())
|
||||
|
||||
try
|
||||
{
|
||||
result_items = result.items()
|
||||
}
|
||||
|
||||
for key, idx in result_items
|
||||
{
|
||||
TS_ASSERT_EQUALS(idx, table[key])
|
||||
}
|
||||
for key, idx in result_items
|
||||
{
|
||||
TS_ASSERT_EQUALS(idx, table[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_open_too_many_files()
|
||||
{
|
||||
test_filename = _get_test_filename();
|
||||
wb = Workbook(optimized_write = True);
|
||||
auto test_filename = temp_file.GetFilename();
|
||||
|
||||
for i in range(200) over 200 worksheets should raise an OSError("too many open files")
|
||||
xlnt::workbook wb;
|
||||
wb.optimized_write(true);
|
||||
|
||||
for(int i = 0; i < 200; i++) // over 200 worksheets should raise an OSError("too many open files")
|
||||
{
|
||||
wb.create_sheet();
|
||||
wb.save(test_filename);
|
||||
os.remove(test_filename);
|
||||
unlink(test_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,29 +137,37 @@ public:
|
|||
|
||||
void test_dump_twice()
|
||||
{
|
||||
test_filename = _get_test_filename();
|
||||
auto test_filename = temp_file.GetFilename();
|
||||
|
||||
wb = Workbook(optimized_write = True);
|
||||
ws = wb.create_sheet();
|
||||
ws.append(["hello"]);
|
||||
xlnt::workbook wb;
|
||||
wb.optimized_write(true);
|
||||
auto ws = wb.create_sheet();
|
||||
|
||||
std::vector<std::string> to_append = {"hello"};
|
||||
ws.append(to_append);
|
||||
|
||||
wb.save(test_filename);
|
||||
os.remove(test_filename);
|
||||
|
||||
unlink(test_filename.c_str());
|
||||
wb.save(test_filename);
|
||||
}
|
||||
|
||||
void test_append_after_save()
|
||||
{
|
||||
test_filename = _get_test_filename();
|
||||
xlnt::workbook wb;
|
||||
wb.optimized_write(true);
|
||||
auto ws = wb.create_sheet();
|
||||
|
||||
wb = Workbook(optimized_write = True);
|
||||
ws = wb.create_sheet();
|
||||
ws.append(["hello"]);
|
||||
std::vector<std::string> to_append = {"hello"};
|
||||
ws.append(to_append);
|
||||
|
||||
wb.save(test_filename);
|
||||
os.remove(test_filename);
|
||||
{
|
||||
TemporaryFile temp2;
|
||||
wb.save(temp2.GetFilename());
|
||||
}
|
||||
|
||||
ws.append(["hello"]);
|
||||
ws.append(to_append);
|
||||
}
|
||||
|
||||
private:
|
||||
TemporaryFile temp_file;
|
||||
};
|
||||
|
|
|
@ -13,17 +13,13 @@ public:
|
|||
|
||||
}
|
||||
|
||||
void test_1()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void test_get_dimensions()
|
||||
{
|
||||
expected = ["A1:G5", "D1:K30", "D2:D2", "A1:C1"];
|
||||
auto expected = {"A1:G5", "D1:K30", "D2:D2", "A1:C1"};
|
||||
|
||||
wb = _open_wb();
|
||||
for i, sheetn in enumerate(wb.get_sheet_names())
|
||||
|
||||
for(i, sheetn : enumerate(wb.get_sheet_names()))
|
||||
{
|
||||
ws = wb.get_sheet_by_name(name = sheetn);
|
||||
TS_ASSERT_EQUALS(ws._dimensions, expected[i]);
|
||||
|
@ -32,68 +28,68 @@ public:
|
|||
|
||||
void test_read_fast_integrated()
|
||||
{
|
||||
sheet_name = "Sheet1 - Text"
|
||||
std::string sheet_name = "Sheet1 - Text";
|
||||
|
||||
expected = [["This is cell A1 in Sheet 1", None, None, None, None, None, None],
|
||||
[None, None, None, None, None, None, None],
|
||||
[None, None, None, None, None, None, None],
|
||||
[None, None, None, None, None, None, None],
|
||||
[None, None, None, None, None, None, "This is cell G5"], ]
|
||||
std::vector<std::vector<char *>> expected = {{"This is cell A1 in Sheet 1", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "This is cell G5"}};
|
||||
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True)
|
||||
ws = wb.get_sheet_by_name(name = sheet_name)
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True);
|
||||
ws = wb.get_sheet_by_name(name = sheet_name);
|
||||
|
||||
for row, expected_row in zip(ws.iter_rows(), expected) :
|
||||
|
||||
row_values = [x.internal_value for x in row]
|
||||
|
||||
TS_ASSERT_EQUALS(row_values, expected_row)
|
||||
for(row, expected_row : zip(ws.iter_rows(), expected)
|
||||
{
|
||||
row_values = [x.internal_value for x in row];
|
||||
TS_ASSERT_EQUALS(row_values, expected_row);
|
||||
}
|
||||
}
|
||||
|
||||
void test_get_boundaries_range()
|
||||
{
|
||||
TS_ASSERT_EQUALS(get_range_boundaries("C1:C4"), (3, 1, 3, 4))
|
||||
TS_ASSERT_EQUALS(get_range_boundaries("C1:C4"), (3, 1, 3, 4));
|
||||
}
|
||||
|
||||
void test_get_boundaries_one()
|
||||
{
|
||||
TS_ASSERT_EQUALS(get_range_boundaries("C1"), (3, 1, 4, 1))
|
||||
TS_ASSERT_EQUALS(get_range_boundaries("C1"), (3, 1, 4, 1));
|
||||
}
|
||||
|
||||
void test_read_single_cell_range()
|
||||
{
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True)
|
||||
ws = wb.get_sheet_by_name(name = sheet_name)
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True);
|
||||
ws = wb.get_sheet_by_name(name = sheet_name);
|
||||
|
||||
TS_ASSERT_EQUALS("This is cell A1 in Sheet 1", list(ws.iter_rows("A1"))[0][0].internal_value)
|
||||
TS_ASSERT_EQUALS("This is cell A1 in Sheet 1", list(ws.iter_rows("A1"))[0][0].internal_value);
|
||||
}
|
||||
|
||||
void test_read_fast_integrated2()
|
||||
{
|
||||
sheet_name = "Sheet2 - Numbers"
|
||||
sheet_name = "Sheet2 - Numbers";
|
||||
|
||||
expected = [[x + 1] for x in range(30)]
|
||||
expected = [[x + 1] for x in range(30)];
|
||||
|
||||
query_range = "D1:E30"
|
||||
query_range = "D1:E30";
|
||||
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True)
|
||||
ws = wb.get_sheet_by_name(name = sheet_name)
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True);
|
||||
ws = wb.get_sheet_by_name(name = sheet_name);
|
||||
|
||||
for row, expected_row in zip(ws.iter_rows(query_range), expected) :
|
||||
|
||||
row_values = [x.internal_value for x in row]
|
||||
|
||||
TS_ASSERT_EQUALS(row_values, expected_row)
|
||||
for(row, expected_row : zip(ws.iter_rows(query_range), expected))
|
||||
{
|
||||
row_values = [x.internal_value for x in row];
|
||||
TS_ASSERT_EQUALS(row_values, expected_row);
|
||||
}
|
||||
}
|
||||
|
||||
void test_read_single_cell_date()
|
||||
{
|
||||
sheet_name = "Sheet4 - Dates"
|
||||
sheet_name = "Sheet4 - Dates";
|
||||
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True)
|
||||
ws = wb.get_sheet_by_name(name = sheet_name)
|
||||
wb = load_workbook(filename = workbook_name, use_iterators = True);
|
||||
ws = wb.get_sheet_by_name(name = sheet_name);
|
||||
|
||||
TS_ASSERT_EQUALS(datetime.datetime(1973, 5, 20), list(ws.iter_rows("A1"))[0][0].internal_value)
|
||||
TS_ASSERT_EQUALS(datetime.datetime(1973, 5, 20, 9, 15, 2), list(ws.iter_rows("C1"))[0][0].internal_value)
|
||||
TS_ASSERT_EQUALS(datetime.datetime(1973, 5, 20), list(ws.iter_rows("A1"))[0][0].internal_value);
|
||||
TS_ASSERT_EQUALS(datetime.datetime(1973, 5, 20, 9, 15, 2), list(ws.iter_rows("C1"))[0][0].internal_value);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,20 +15,20 @@ public:
|
|||
|
||||
void test_write_content_types()
|
||||
{
|
||||
wb = Workbook();
|
||||
xlnt::workbook wb;
|
||||
wb.create_sheet();
|
||||
wb.create_sheet();
|
||||
content = write_content_types(wb);
|
||||
reference_file = os.path.join(DATADIR, "writer", "expected",
|
||||
"[Content_Types].xml");
|
||||
auto content = xlnt::workbook::write_content_types(wb);
|
||||
std::string reference_file = DATADIR + "/writer/expected/[Content_Types].xml";
|
||||
assert_equals_file_content(reference_file, content);
|
||||
}
|
||||
|
||||
void test_write_root_rels()
|
||||
{
|
||||
wb = Workbook();
|
||||
content = write_root_rels(wb);
|
||||
reference_file = os.path.join(DATADIR, "writer", "expected", ".rels");
|
||||
xlnt::workbook wb;
|
||||
wb.create_sheet();
|
||||
auto content = xlnt::workbook::write_root_rels(wb);
|
||||
std::string reference_file = DATADIR + "/writer/expected/.rels";
|
||||
assert_equals_file_content(reference_file, content);
|
||||
}
|
||||
};
|
||||
|
|
31
source/tests/TemporaryDirectory.h
Normal file
31
source/tests/TemporaryDirectory.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
class TemporaryDirectory
|
||||
{
|
||||
public:
|
||||
static std::string CreateTemporaryFilename()
|
||||
{
|
||||
std::array<char, L_tmpnam> buffer;
|
||||
tmpnam(buffer.data());
|
||||
return std::string(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
TemporaryDirectory() : filename_(CreateTemporaryFilename())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~TemporaryDirectory()
|
||||
{
|
||||
remove(filename_.c_str());
|
||||
}
|
||||
|
||||
std::string GetFilename() const { return filename_; }
|
||||
|
||||
private:
|
||||
const std::string filename_;
|
||||
};
|
31
source/tests/TemporaryFile.h
Normal file
31
source/tests/TemporaryFile.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
class TemporaryFile
|
||||
{
|
||||
public:
|
||||
static std::string CreateTemporaryFilename()
|
||||
{
|
||||
std::array<char, L_tmpnam> buffer;
|
||||
tmpnam(buffer.data());
|
||||
return std::string(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
TemporaryFile() : filename_(CreateTemporaryFilename())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~TemporaryFile()
|
||||
{
|
||||
remove(filename_.c_str());
|
||||
}
|
||||
|
||||
std::string GetFilename() const { return filename_; }
|
||||
|
||||
private:
|
||||
const std::string filename_;
|
||||
};
|
|
@ -34,7 +34,9 @@ public:
|
|||
|
||||
void test_set_bad_title()
|
||||
{
|
||||
Worksheet(wb, "X" * 50);
|
||||
std::string title(50, 'X');
|
||||
xlnt::workbook wb;
|
||||
wb.create_sheet(title);
|
||||
}
|
||||
|
||||
void test_set_bad_title_character()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "../xlnt.h"
|
||||
#include "TemporaryDirectory.h"
|
||||
|
||||
class WriteTestSuite : public CxxTest::TestSuite
|
||||
{
|
||||
|
@ -15,12 +16,11 @@ public:
|
|||
|
||||
void test_write_empty_workbook()
|
||||
{
|
||||
make_tmpdir();
|
||||
TemporaryDirectory temp_dir;
|
||||
wb = Workbook();
|
||||
dest_filename = os.path.join(TMPDIR, "empty_book.xlsx");
|
||||
save_workbook(wb, dest_filename);
|
||||
assert os.path.isfile(dest_filename);
|
||||
clean_tmpdir();
|
||||
}
|
||||
|
||||
void test_write_virtual_workbook()
|
||||
|
|
167
source/xlnt.cpp
167
source/xlnt.cpp
|
@ -6,6 +6,7 @@
|
|||
#include <sstream>
|
||||
|
||||
#include "xlnt.h"
|
||||
#include "../third-party/pugixml/src/pugixml.hpp"
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
|
@ -971,6 +972,8 @@ struct cell_struct
|
|||
bool bool_value;
|
||||
};
|
||||
|
||||
|
||||
std::string error_value;
|
||||
tm date_value;
|
||||
std::string string_value;
|
||||
std::string formula_value;
|
||||
|
@ -1015,12 +1018,27 @@ cell::cell(cell_struct *root) : root_(root)
|
|||
|
||||
cell::type cell::data_type_for_value(const std::string &value)
|
||||
{
|
||||
if(value[0] == '=')
|
||||
{
|
||||
return type::formula;
|
||||
}
|
||||
|
||||
return type::null;
|
||||
}
|
||||
|
||||
void cell::set_explicit_value(const std::string &value, type data_type)
|
||||
{
|
||||
|
||||
root_->type = data_type;
|
||||
switch(data_type)
|
||||
{
|
||||
case type::formula: root_->formula_value = value; return;
|
||||
case type::date: root_->date_value.tm_hour = std::stoi(value); return;
|
||||
case type::error: root_->error_value = value; return;
|
||||
case type::boolean: root_->bool_value = value == "true"; return;
|
||||
case type::null: return;
|
||||
case type::numeric: root_->numeric_value = std::stod(value); return;
|
||||
case type::string: root_->string_value = value; return;
|
||||
}
|
||||
}
|
||||
|
||||
bool cell::bind_value()
|
||||
|
@ -1620,17 +1638,17 @@ void worksheet::unmerge_cells(int start_row, int start_column, int end_row, int
|
|||
root_->unmerge_cells(start_row, start_column, end_row, end_column);
|
||||
}
|
||||
|
||||
void worksheet::append(const std::vector<xlnt::cell> &cells)
|
||||
void worksheet::append(const std::vector<std::string> &cells)
|
||||
{
|
||||
root_->append(cells);
|
||||
}
|
||||
|
||||
void worksheet::append(const std::unordered_map<std::string, xlnt::cell> &cells)
|
||||
void worksheet::append(const std::unordered_map<std::string, std::string> &cells)
|
||||
{
|
||||
root_->append(cells);
|
||||
}
|
||||
|
||||
void worksheet::append(const std::unordered_map<int, xlnt::cell> &cells)
|
||||
void worksheet::append(const std::unordered_map<int, std::string> &cells)
|
||||
{
|
||||
root_->append(cells);
|
||||
}
|
||||
|
@ -1676,11 +1694,146 @@ cell worksheet::operator[](const std::string &address)
|
|||
return cell(address);
|
||||
}
|
||||
|
||||
workbook::workbook() : active_worksheet_(nullptr)
|
||||
std::string workbook::write_content_types(workbook &wb)
|
||||
{
|
||||
std::set<std::string> seen;
|
||||
|
||||
pugi::xml_node root;
|
||||
|
||||
if(wb.has_vba_archive())
|
||||
{
|
||||
root = fromstring(wb.get_vba_archive().read(ARC_CONTENT_TYPES));
|
||||
|
||||
for(auto elem : root.findall("{" + CONTYPES_NS + "}Override"))
|
||||
{
|
||||
seen.insert(elem.attrib["PartName"]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
root = Element("{" + CONTYPES_NS + "}Types");
|
||||
|
||||
for(auto content_type : static_content_types_config)
|
||||
{
|
||||
if(setting_type == "Override")
|
||||
{
|
||||
tag = "{" + CONTYPES_NS + "}Override";
|
||||
attrib = {"PartName": "/" + name};
|
||||
}
|
||||
else
|
||||
{
|
||||
tag = "{" + CONTYPES_NS + "}Default";
|
||||
attrib = {"Extension": name};
|
||||
}
|
||||
|
||||
attrib["ContentType"] = content_type;
|
||||
SubElement(root, tag, attrib);
|
||||
}
|
||||
}
|
||||
|
||||
int drawing_id = 1;
|
||||
int chart_id = 1;
|
||||
int comments_id = 1;
|
||||
int sheet_id = 0;
|
||||
|
||||
for(auto sheet : wb)
|
||||
{
|
||||
std::string name = "/xl/worksheets/sheet" + std::to_string(sheet_id) + ".xml";
|
||||
|
||||
if(seen.find(name) == seen.end())
|
||||
{
|
||||
SubElement(root, "{" + CONTYPES_NS + "}Override", {{"PartName", name},
|
||||
{"ContentType", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"}});
|
||||
}
|
||||
|
||||
if(sheet._charts || sheet._images)
|
||||
{
|
||||
name = "/xl/drawings/drawing" + drawing_id + ".xml";
|
||||
|
||||
if(seen.find(name) == seen.end())
|
||||
{
|
||||
SubElement(root, "{%s}Override" % CONTYPES_NS, {"PartName" : name,
|
||||
"ContentType" : "application/vnd.openxmlformats-officedocument.drawing+xml"});
|
||||
}
|
||||
|
||||
drawing_id += 1;
|
||||
|
||||
for(auto chart : sheet._charts)
|
||||
{
|
||||
name = "/xl/charts/chart%d.xml" % chart_id;
|
||||
|
||||
if(seen.find(name) == seen.end())
|
||||
{
|
||||
SubElement(root, "{%s}Override" % CONTYPES_NS, {"PartName" : name,
|
||||
"ContentType" : "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"});
|
||||
}
|
||||
|
||||
chart_id += 1;
|
||||
|
||||
if(chart._shapes)
|
||||
{
|
||||
name = "/xl/drawings/drawing%d.xml" % drawing_id;
|
||||
|
||||
if(seen.find(name) == seen.end())
|
||||
{
|
||||
SubElement(root, "{%s}Override" % CONTYPES_NS, {"PartName" : name,
|
||||
"ContentType" : "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"});
|
||||
}
|
||||
|
||||
drawing_id += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(sheet.get_comment_count() > 0)
|
||||
{
|
||||
SubElement(root, "{%s}Override" % CONTYPES_NS,
|
||||
{"PartName": "/xl/comments%d.xml" % comments_id,
|
||||
"ContentType" : "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"});
|
||||
comments_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return get_document_content(root);
|
||||
}
|
||||
|
||||
std::string workbook::write_root_rels(workbook wb)
|
||||
{
|
||||
root = Element("{%s}Relationships" % PKG_REL_NS);
|
||||
relation_tag = "{%s}Relationship" % PKG_REL_NS;
|
||||
SubElement(root, relation_tag, {"Id": "rId1", "Target" : ARC_WORKBOOK,
|
||||
"Type" : "%s/officeDocument" % REL_NS});
|
||||
SubElement(root, relation_tag, {"Id": "rId2", "Target" : ARC_CORE,
|
||||
"Type" : "%s/metadata/core-properties" % PKG_REL_NS});
|
||||
SubElement(root, relation_tag, {"Id": "rId3", "Target" : ARC_APP,
|
||||
"Type" : "%s/extended-properties" % REL_NS});
|
||||
if(wb.has_vba_archive())
|
||||
{
|
||||
// See if there was a customUI relation and reuse its id
|
||||
arc = fromstring(workbook.vba_archive.read(ARC_ROOT_RELS));
|
||||
rels = arc.findall(relation_tag);
|
||||
rId = None;
|
||||
for(rel in rels)
|
||||
{
|
||||
if(rel.get("Target") == ARC_CUSTOM_UI)
|
||||
{
|
||||
rId = rel.get("Id");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rId is not None)
|
||||
{
|
||||
SubElement(root, relation_tag, {"Id": rId, "Target" : ARC_CUSTOM_UI,
|
||||
"Type" : "%s" % CUSTOMUI_NS});
|
||||
}
|
||||
}
|
||||
|
||||
return get_document_content(root);
|
||||
}
|
||||
|
||||
workbook::workbook() : active_sheet_index_(0)
|
||||
{
|
||||
auto ws = create_sheet();
|
||||
ws.set_title("Sheet1");
|
||||
active_worksheet_ = ws;
|
||||
}
|
||||
|
||||
worksheet workbook::get_sheet_by_name(const std::string &name)
|
||||
|
@ -1695,7 +1848,7 @@ worksheet workbook::get_sheet_by_name(const std::string &name)
|
|||
|
||||
worksheet workbook::get_active_sheet()
|
||||
{
|
||||
return active_worksheet_;
|
||||
return worksheets_[active_sheet_index_];
|
||||
}
|
||||
|
||||
worksheet workbook::create_sheet()
|
||||
|
|
464
source/xlnt.h
464
source/xlnt.h
|
@ -12,17 +12,21 @@ struct tm;
|
|||
|
||||
namespace xlnt {
|
||||
|
||||
struct cell_struct;
|
||||
struct worksheet_struct;
|
||||
struct package_impl;
|
||||
|
||||
class style;
|
||||
class worksheet;
|
||||
class worksheet;
|
||||
class cell;
|
||||
class relationship;
|
||||
class workbook;
|
||||
class comment;
|
||||
class drawing;
|
||||
class named_range;
|
||||
class package;
|
||||
class relationship;
|
||||
class style;
|
||||
class workbook;
|
||||
class worksheet;
|
||||
|
||||
struct cell_struct;
|
||||
struct drawing_struct;
|
||||
struct named_range_struct;
|
||||
struct package_impl;
|
||||
struct worksheet_struct;
|
||||
|
||||
const int MIN_ROW = 0;
|
||||
const int MIN_COLUMN = 0;
|
||||
|
@ -192,6 +196,12 @@ enum class target_mode
|
|||
Internal
|
||||
};
|
||||
|
||||
enum class encoding_type
|
||||
{
|
||||
utf8,
|
||||
latin1
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a value type that may or may not have an assigned value.
|
||||
/// </summary>
|
||||
|
@ -798,25 +808,278 @@ struct coordinate
|
|||
int row;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Alignment options for use in styles.
|
||||
/// </summary>
|
||||
struct alignment
|
||||
{
|
||||
enum class horizontal_alignment
|
||||
{
|
||||
general,
|
||||
left,
|
||||
right,
|
||||
center,
|
||||
center_continuous,
|
||||
justify
|
||||
};
|
||||
|
||||
enum class vertical_alignment
|
||||
{
|
||||
bottom,
|
||||
top,
|
||||
center,
|
||||
justify
|
||||
};
|
||||
|
||||
horizontal_alignment horizontal = horizontal_alignment::general;
|
||||
vertical_alignment vertical = vertical_alignment::bottom;
|
||||
int text_rotation = 0;
|
||||
bool wrap_text = false;
|
||||
bool shrink_to_fit = false;
|
||||
int indent = 0;
|
||||
};
|
||||
|
||||
class number_format
|
||||
{
|
||||
public:
|
||||
void set_format_code(const std::string &format_code) { format_code_ = format_code; }
|
||||
enum class format
|
||||
{
|
||||
general,
|
||||
text,
|
||||
number,
|
||||
number00,
|
||||
number_comma_separated1,
|
||||
number_comma_separated2,
|
||||
percentage,
|
||||
percentage00,
|
||||
date_yyyymmdd2,
|
||||
date_yyyymmdd,
|
||||
date_ddmmyyyy,
|
||||
date_dmyslash,
|
||||
date_dmyminus,
|
||||
date_dmminus,
|
||||
date_myminus,
|
||||
date_xlsx14,
|
||||
date_xlsx15,
|
||||
date_xlsx16,
|
||||
date_xlsx17,
|
||||
date_xlsx22,
|
||||
date_datetime,
|
||||
date_time1,
|
||||
date_time2,
|
||||
date_time3,
|
||||
date_time4,
|
||||
date_time5,
|
||||
date_time6,
|
||||
date_time7,
|
||||
date_time8,
|
||||
date_timedelta,
|
||||
date_yyyymmddslash,
|
||||
currency_usd_simple,
|
||||
currency_usd,
|
||||
currency_eur_simple
|
||||
};
|
||||
|
||||
static const std::unordered_map<int, std::string> builtin_formats;
|
||||
|
||||
static std::string builtin_format_code(int index);
|
||||
|
||||
static bool is_date_format(const std::string &format);
|
||||
static bool is_builtin(const std::string &format);
|
||||
|
||||
format get_format_code() const { return format_code_; }
|
||||
void set_format_code(format format_code) { format_code_ = format_code; }
|
||||
|
||||
private:
|
||||
std::string format_code_;
|
||||
format format_code_ = format::general;
|
||||
int format_index_ = 0;
|
||||
};
|
||||
|
||||
struct color
|
||||
{
|
||||
static const color black;
|
||||
static const color white;
|
||||
static const color red;
|
||||
static const color darkred;
|
||||
static const color blue;
|
||||
static const color darkblue;
|
||||
static const color green;
|
||||
static const color darkgreen;
|
||||
static const color yellow;
|
||||
static const color darkyellow;
|
||||
|
||||
color(int index)
|
||||
{
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
int index;
|
||||
};
|
||||
|
||||
class font
|
||||
{
|
||||
enum class underline
|
||||
{
|
||||
none,
|
||||
double_,
|
||||
double_accounting,
|
||||
single,
|
||||
single_accounting
|
||||
};
|
||||
|
||||
std::string name = "Calibri";
|
||||
int size = 11;
|
||||
bool bold = false;
|
||||
bool italic = false;
|
||||
bool superscript = false;
|
||||
bool subscript = false;
|
||||
underline underline = underline::none;
|
||||
bool strikethrough = false;
|
||||
color color = color::black;
|
||||
};
|
||||
|
||||
class fill
|
||||
{
|
||||
public:
|
||||
enum class type
|
||||
{
|
||||
none,
|
||||
solid,
|
||||
gradient_linear,
|
||||
gradient_path,
|
||||
pattern_darkdown,
|
||||
pattern_darkgray,
|
||||
pattern_darkgrid,
|
||||
pattern_darkhorizontal,
|
||||
pattern_darktrellis,
|
||||
pattern_darkup,
|
||||
pattern_darkvertical,
|
||||
pattern_gray0625,
|
||||
pattern_gray125,
|
||||
pattern_lightdown,
|
||||
pattern_lightgray,
|
||||
pattern_lightgrid,
|
||||
pattern_lighthorizontal,
|
||||
pattern_lighttrellis,
|
||||
pattern_lightup,
|
||||
pattern_lightvertical,
|
||||
pattern_mediumgray,
|
||||
};
|
||||
|
||||
type type = type::none;
|
||||
int rotation = 0;
|
||||
color start_color = color::white;
|
||||
color end_color = color::black;
|
||||
};
|
||||
|
||||
class borders
|
||||
{
|
||||
struct border
|
||||
{
|
||||
enum class style
|
||||
{
|
||||
none,
|
||||
dashdot,
|
||||
dashdotdot,
|
||||
dashed,
|
||||
dotted,
|
||||
double_,
|
||||
hair,
|
||||
medium,
|
||||
mediumdashdot,
|
||||
mediumdashdotdot,
|
||||
mediumdashed,
|
||||
slantdashdot,
|
||||
thick,
|
||||
thin
|
||||
};
|
||||
|
||||
style style = style::none;
|
||||
color color = color::black;
|
||||
};
|
||||
|
||||
enum class diagonal_direction
|
||||
{
|
||||
none,
|
||||
up,
|
||||
down,
|
||||
both
|
||||
};
|
||||
|
||||
border left;
|
||||
border right;
|
||||
border top;
|
||||
border bottom;
|
||||
border diagonal;
|
||||
diagonal_direction diagonal_direction = diagonal_direction::none;
|
||||
border all_borders;
|
||||
border outline;
|
||||
border inside;
|
||||
border vertical;
|
||||
border horizontal;
|
||||
};
|
||||
|
||||
class protection
|
||||
{
|
||||
public:
|
||||
enum class type
|
||||
{
|
||||
inherit,
|
||||
protected_,
|
||||
unprotected
|
||||
};
|
||||
|
||||
type locked;
|
||||
type hidden;
|
||||
};
|
||||
|
||||
class style
|
||||
{
|
||||
public:
|
||||
style(bool static_ = false) : static_(static_) {}
|
||||
|
||||
style copy() const;
|
||||
|
||||
font get_font() const;
|
||||
void set_font(font font);
|
||||
|
||||
fill get_fill() const;
|
||||
void set_fill(fill fill);
|
||||
|
||||
borders get_borders() const;
|
||||
void set_borders(borders borders);
|
||||
|
||||
alignment get_alignment() const;
|
||||
void set_alignment(alignment alignment);
|
||||
|
||||
number_format &get_number_format() { return number_format_; }
|
||||
const number_format &get_number_format() const { return number_format_; }
|
||||
void set_number_format(number_format number_format);
|
||||
|
||||
protection get_protection() const;
|
||||
void set_protection(protection protection);
|
||||
|
||||
private:
|
||||
style(const style &rhs);
|
||||
|
||||
bool static_ = false;
|
||||
font font_;
|
||||
fill fill_;
|
||||
borders borders_;
|
||||
alignment alignment_;
|
||||
number_format number_format_;
|
||||
protection protection_;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Describes cell associated properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Properties of interest include style, type, value, and address.
|
||||
/// The Cell class is required to know its value and type, display options,
|
||||
/// and any other features of an Excel cell.Utilities for referencing
|
||||
/// cells using Excel's 'A1' column/row nomenclature are also provided.
|
||||
/// </remarks>
|
||||
class cell
|
||||
{
|
||||
public:
|
||||
|
@ -833,15 +1096,46 @@ public:
|
|||
|
||||
static const std::unordered_map<std::string, int> ErrorCodes;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a coordinate string like 'B12' to a tuple ('B', 12)
|
||||
/// </summary>
|
||||
static coordinate coordinate_from_string(const std::string &address);
|
||||
static int column_index_from_string(const std::string &column_string);
|
||||
static std::string get_column_letter(int column_index);
|
||||
|
||||
/// <summary>
|
||||
/// Convert a coordinate to an absolute coordinate string (B12 -> $B$12)
|
||||
/// </summary>
|
||||
static std::string absolute_coordinate(const std::string &absolute_address);
|
||||
|
||||
/// <summary>
|
||||
/// Convert a column letter into a column number (e.g. B -> 2)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Excel only supports 1 - 3 letter column names from A->ZZZ, so we
|
||||
/// restrict our column names to 1 - 3 characters, each in the range A - Z.
|
||||
/// </remarks>
|
||||
static int column_index_from_string(const std::string &column_string);
|
||||
|
||||
/// <summary>
|
||||
/// Convert a column number into a column letter (3 -> 'C')
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Right shift the column col_idx by 26 to find column letters in reverse
|
||||
/// order.These numbers are 1 - based, and can be converted to ASCII
|
||||
/// ordinals by adding 64.
|
||||
/// </remarks>
|
||||
static std::string get_column_letter(int column_index);
|
||||
|
||||
static std::string check_string(const std::string &value);
|
||||
static std::string check_numeric(const std::string &value);
|
||||
static std::string check_error(const std::string &value);
|
||||
|
||||
cell();
|
||||
cell(worksheet &ws, const std::string &column, int row);
|
||||
cell(worksheet &ws, const std::string &column, int row, const std::string &initial_value);
|
||||
|
||||
encoding_type get_encoding() const;
|
||||
std::string to_string() const;
|
||||
|
||||
void set_explicit_value(const std::string &value, type data_type);
|
||||
type data_type_for_value(const std::string &value);
|
||||
|
||||
|
@ -853,32 +1147,59 @@ public:
|
|||
bool bind_value(bool value);
|
||||
bool bind_value(const tm &value);
|
||||
|
||||
std::string get_hyperlink() const;
|
||||
void set_hyperlink(const std::string &value);
|
||||
|
||||
std::string get_hyperlink_rel_id() const;
|
||||
|
||||
void set_number_format(const std::string &format_code);
|
||||
|
||||
bool has_style() const;
|
||||
|
||||
style &get_style();
|
||||
const style &get_style() const;
|
||||
|
||||
type get_data_type() const;
|
||||
|
||||
std::string get_coordinate() const;
|
||||
std::string get_address() const;
|
||||
|
||||
cell get_offset(int row, int column);
|
||||
|
||||
bool is_date() const;
|
||||
|
||||
std::pair<int, int> get_anchor() const;
|
||||
|
||||
comment get_comment() const;
|
||||
void set_comment(comment comment);
|
||||
|
||||
cell &operator=(const cell &rhs);
|
||||
cell &operator=(bool value);
|
||||
cell &operator=(int value);
|
||||
cell &operator=(double value);
|
||||
cell &operator=(const std::string &value);
|
||||
cell &operator=(const char *value);
|
||||
cell &operator=(bool value);
|
||||
cell &operator=(const tm &value);
|
||||
|
||||
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 tm &comparand) const;
|
||||
|
||||
friend bool operator==(std::nullptr_t, const cell &cell);
|
||||
friend bool operator==(bool comparand, const cell &cell);
|
||||
friend bool operator==(int comparand, const cell &cell);
|
||||
friend bool operator==(double comparand, const cell &cell);
|
||||
friend bool operator==(const std::string &comparand, const cell &cell);
|
||||
friend bool operator==(const char *comparand, const cell &cell);
|
||||
friend bool operator==(const tm &comparand, const cell &cell);
|
||||
|
||||
std::string to_string() const;
|
||||
bool is_date() const;
|
||||
style &get_style();
|
||||
const style &get_style() const;
|
||||
type get_data_type() const;
|
||||
|
||||
private:
|
||||
friend struct worksheet_struct;
|
||||
|
||||
cell(cell_struct *root);
|
||||
|
||||
cell_struct *root_;
|
||||
};
|
||||
|
||||
|
@ -951,9 +1272,9 @@ public:
|
|||
void merge_cells(int start_row, int start_column, int end_row, int end_column);
|
||||
void unmerge_cells(const std::string &range_string);
|
||||
void unmerge_cells(int start_row, int start_column, int end_row, int end_column);
|
||||
void append(const std::vector<xlnt::cell> &cells);
|
||||
void append(const std::unordered_map<std::string, xlnt::cell> &cells);
|
||||
void append(const std::unordered_map<int, xlnt::cell> &cells);
|
||||
void append(const std::vector<std::string> &cells);
|
||||
void append(const std::unordered_map<std::string, std::string> &cells);
|
||||
void append(const std::unordered_map<int, std::string> &cells);
|
||||
xlnt::range rows() const;
|
||||
xlnt::range columns() const;
|
||||
bool operator==(const worksheet &other) const;
|
||||
|
@ -967,26 +1288,111 @@ private:
|
|||
worksheet_struct *root_;
|
||||
};
|
||||
|
||||
class named_range
|
||||
{
|
||||
public:
|
||||
named_range(const std::string &name);
|
||||
|
||||
private:
|
||||
friend class worksheet;
|
||||
named_range(named_range_struct *root);
|
||||
named_range_struct *root_;
|
||||
};
|
||||
|
||||
class drawing
|
||||
{
|
||||
public:
|
||||
drawing();
|
||||
|
||||
private:
|
||||
friend class worksheet;
|
||||
drawing(drawing_struct *root);
|
||||
drawing_struct *root_;
|
||||
};
|
||||
|
||||
class workbook
|
||||
{
|
||||
public:
|
||||
static std::string write_content_types(workbook &wb);
|
||||
static std::string write_root_rels(workbook &wb);
|
||||
|
||||
//constructors
|
||||
workbook();
|
||||
|
||||
//prevent copy and assignment
|
||||
workbook(const workbook &) = delete;
|
||||
const workbook &operator=(const workbook &) = delete;
|
||||
|
||||
worksheet get_sheet_by_name(const std::string &sheet_name);
|
||||
//named parameters
|
||||
workbook &optimized_write(bool value);
|
||||
workbook &encoding(encoding_type value);
|
||||
workbook &guess_types(bool value);
|
||||
workbook &data_only(bool value);
|
||||
|
||||
void read_workbook_settings(const std::string &xml_source);
|
||||
|
||||
//getters
|
||||
worksheet get_active_sheet();
|
||||
bool get_optimized_write() const { return optimized_write_; }
|
||||
encoding_type get_encoding() const { return encoding_; }
|
||||
bool get_guess_types() const { return guess_types_; }
|
||||
bool get_data_only() const { return data_only_; }
|
||||
|
||||
//create
|
||||
worksheet create_sheet();
|
||||
worksheet create_sheet(std::size_t index);
|
||||
std::vector<std::string> get_sheet_names() const;
|
||||
worksheet create_sheet(const std::string &title);
|
||||
worksheet create_sheet(std::size_t index, const std::string &title);
|
||||
|
||||
//add
|
||||
void add_sheet(worksheet worksheet);
|
||||
void add_sheet(worksheet worksheet, std::size_t index);
|
||||
|
||||
//remove
|
||||
void remove_sheet(worksheet worksheet);
|
||||
|
||||
//container operations
|
||||
worksheet get_sheet_by_name(const std::string &sheet_name);
|
||||
|
||||
bool contains(const std::string &key) const;
|
||||
|
||||
int get_index(worksheet worksheet);
|
||||
|
||||
worksheet operator[](const std::string &name);
|
||||
|
||||
std::vector<worksheet>::iterator begin();
|
||||
std::vector<worksheet>::iterator end();
|
||||
worksheet operator[](const std::string &name);
|
||||
|
||||
std::vector<worksheet>::const_iterator begin() const;
|
||||
std::vector<worksheet>::const_iterator end() const;
|
||||
|
||||
std::vector<worksheet>::const_iterator cbegin() const;
|
||||
std::vector<worksheet>::const_iterator cend() const;
|
||||
|
||||
std::vector<std::string> get_sheet_names() const;
|
||||
|
||||
//named ranges
|
||||
void create_named_range(const std::string &name, worksheet worksheet, const std::string &range_string);
|
||||
std::vector<named_range> get_named_ranges();
|
||||
void add_named_range(named_range named_range);
|
||||
void get_named_range(const std::string &name);
|
||||
void remove_named_range(named_range named_range);
|
||||
|
||||
//serialization
|
||||
void save(const std::string &filename);
|
||||
void load(const std::string &filename);
|
||||
|
||||
private:
|
||||
worksheet active_worksheet_;
|
||||
bool optimized_write_;
|
||||
bool optimized_read_;
|
||||
bool guess_types_;
|
||||
bool data_only_;
|
||||
int active_sheet_index_;
|
||||
encoding_type encoding_;
|
||||
std::vector<worksheet> worksheets_;
|
||||
std::vector<named_range> named_ranges_;
|
||||
std::vector<relationship> relationships_;
|
||||
std::vector<drawing> drawings_;
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
||||
|
|
Loading…
Reference in New Issue
Block a user