diff --git a/include/xlnt/cell/cell.hpp b/include/xlnt/cell/cell.hpp
index 18161adb..c8cd953e 100644
--- a/include/xlnt/cell/cell.hpp
+++ b/include/xlnt/cell/cell.hpp
@@ -44,8 +44,17 @@ namespace detail {
struct cell_impl;
} // namespace detail
-typedef std::string comment;
-
+class comment
+{
+public:
+ comment(const std::string &type, const std::string &value) : type_(type), value_(value) {}
+ std::string get_type() const { return type_; }
+ std::string get_value() const { return value_; }
+private:
+ std::string type_;
+ std::string value_;
+};
+
///
/// Describes cell associated properties.
///
@@ -116,7 +125,8 @@ public:
//std::pair get_anchor() const;
comment get_comment() const;
- void set_comment(comment comment);
+ void set_comment(const comment &comment);
+ void clear_comment();
std::string get_formula() const;
void set_formula(const std::string &formula);
@@ -126,6 +136,10 @@ public:
void set_null();
+ cell offset(row_t row, column_t column);
+
+ worksheet get_parent();
+
cell &operator=(const cell &rhs);
cell &operator=(bool value);
cell &operator=(int value);
diff --git a/include/xlnt/common/relationship.hpp b/include/xlnt/common/relationship.hpp
index 9d498b02..06112f45 100644
--- a/include/xlnt/common/relationship.hpp
+++ b/include/xlnt/common/relationship.hpp
@@ -47,9 +47,78 @@ enum class target_mode
///
class relationship
{
-public:
+public:
+ enum class type
+ {
+ invalid,
+ hyperlink,
+ drawing,
+ worksheet,
+ shared_strings,
+ styles,
+ theme,
+ extended_properties,
+ core_properties,
+ office_document
+ };
+
+ static type type_from_string(const std::string &type_string)
+ {
+ if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties")
+ {
+ return type::extended_properties;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties")
+ {
+ return type::core_properties;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
+ {
+ return type::office_document;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet")
+ {
+ return type::worksheet;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings")
+ {
+ return type::shared_strings;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles")
+ {
+ return type::styles;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme")
+ {
+ return type::theme;
+ }
+ else if(type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
+ {
+ return type::hyperlink;
+ }
+
+ return type::invalid;
+ }
+
+ static std::string type_to_string(type t)
+ {
+ switch(t)
+ {
+ case type::extended_properties: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";
+ case type::core_properties: return "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
+ case type::office_document: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
+ case type::worksheet: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
+ case type::shared_strings: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
+ case type::styles: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
+ case type::theme: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
+ case type::hyperlink: return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
+ default: return "??";
+ }
+ }
+
relationship();
- relationship(const std::string &type, const std::string &r_id = "", const std::string &target_uri = "");
+ relationship(const std::string &t, const std::string &r_id = "", const std::string &target_uri = "") : relationship(type_from_string(t), r_id, target_uri) {}
+ relationship(type t, const std::string &r_id = "", const std::string &target_uri = "");
///
/// gets a string that identifies the relationship.
@@ -71,20 +140,10 @@ public:
///
std::string get_target_uri() const { return target_uri_; }
-private:
- relationship(const std::string &id, const std::string &relationship_type_, const std::string &source_uri, target_mode target_mode, const std::string &target_uri);
- //relationship &operator=(const relationship &rhs) = delete;
+ type get_type() const { return type_; }
+ std::string get_type_string() const { return type_to_string(type_); }
- enum class type
- {
- hyperlink,
- drawing,
- worksheet,
- sharedStrings,
- styles,
- theme
- };
-
+private:
type type_;
std::string id_;
std::string source_uri_;
diff --git a/include/xlnt/reader/reader.hpp b/include/xlnt/reader/reader.hpp
index f4aaff82..2cc38486 100644
--- a/include/xlnt/reader/reader.hpp
+++ b/include/xlnt/reader/reader.hpp
@@ -30,6 +30,7 @@
namespace xlnt {
+class relationship;
class workbook;
class worksheet;
class document_properties;
@@ -38,10 +39,9 @@ class zip_file;
class reader
{
public:
- static std::unordered_map> read_relationships(const std::string &content);
+ static std::vector read_relationships(const std::string &content);
static std::pair, std::unordered_map> read_content_types(const std::string &content);
- static std::string determine_document_type(const std::unordered_map> &root_relationships,
- const std::unordered_map &override_types);
+ static std::string determine_document_type(const std::unordered_map &override_types);
static worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector &string_table);
static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector &string_table, const std::vector &number_format_ids);
static std::vector read_shared_string(const std::string &xml_string);
diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp
index 9cd83647..3d8f43a8 100644
--- a/include/xlnt/workbook/workbook.hpp
+++ b/include/xlnt/workbook/workbook.hpp
@@ -29,6 +29,8 @@
#include
#include
+#include "../common/relationship.hpp"
+
namespace xlnt {
class drawing;
@@ -159,7 +161,7 @@ public:
std::vector get_content_types() const;
- void create_relationship(const std::string &id, const std::string &target, const std::string &type);
+ void create_relationship(const std::string &id, const std::string &target, relationship::type type);
relationship get_relationship(const std::string &id) const;
std::vector get_relationships() const;
diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp
index 3598a3c4..70262a4a 100644
--- a/include/xlnt/worksheet/worksheet.hpp
+++ b/include/xlnt/worksheet/worksheet.hpp
@@ -30,11 +30,13 @@
#include
#include "../common/types.hpp"
+#include "../common/relationship.hpp"
namespace xlnt {
class cell;
class cell_reference;
+class comment;
class range;
class range_reference;
class relationship;
@@ -203,7 +205,7 @@ public:
range_reference calculate_dimension() const;
// relationships
- relationship create_relationship(const std::string &relationship_type, const std::string &target_uri);
+ relationship create_relationship(relationship::type type, const std::string &target_uri);
std::vector get_relationships();
// charts
@@ -244,6 +246,11 @@ public:
void auto_filter(const range_reference &reference);
void unset_auto_filter();
bool has_auto_filter() const;
+
+ // comments
+ void add_comment(const comment &c);
+ void remove_comment(const comment &c);
+ std::size_t get_comment_count() const;
void reserve(std::size_t n);
diff --git a/include/xlnt/xlnt.hpp b/include/xlnt/xlnt.hpp
index 31d0bef7..01b1df1c 100644
--- a/include/xlnt/xlnt.hpp
+++ b/include/xlnt/xlnt.hpp
@@ -23,6 +23,8 @@
// @author: see AUTHORS file
#pragma once
+#include
+
const std::string xlnt_version = "0.1.0";
const std::string author = "Thomas Fussell";
diff --git a/source/cell.cpp b/source/cell.cpp
index e62d0cbb..5c8820f7 100644
--- a/source/cell.cpp
+++ b/source/cell.cpp
@@ -560,7 +560,7 @@ void cell::set_hyperlink(const std::string &hyperlink)
}
d_->has_hyperlink_ = true;
- d_->hyperlink_ = worksheet(d_->parent_).create_relationship("hyperlink", hyperlink);
+ d_->hyperlink_ = worksheet(d_->parent_).create_relationship(relationship::type::hyperlink, hyperlink);
if(d_->type_ == type::null)
{
@@ -584,6 +584,26 @@ void cell::set_formula(const std::string &formula)
d_->string_value = formula;
}
+void cell::set_comment(const xlnt::comment &comment)
+{
+ if(d_->comment_.get_value() == "")
+ {
+ get_parent().add_comment(comment);
+ }
+
+ d_->comment_ = comment;
+}
+
+void cell::clear_comment()
+{
+ if(d_->comment_.get_value() != "")
+ {
+ get_parent().remove_comment(d_->comment_);
+ }
+
+ d_->comment_ = comment("", "");
+}
+
void cell::set_error(const std::string &error)
{
if(error.length() == 0 || error[0] != '#')
@@ -595,4 +615,19 @@ void cell::set_error(const std::string &error)
d_->string_value = error;
}
+cell cell::offset(column_t column, row_t row)
+{
+ return get_parent().get_cell(cell_reference(d_->column + column, d_->row + row));
+}
+
+worksheet cell::get_parent()
+{
+ return worksheet(d_->parent_);
+}
+
+comment cell::get_comment() const
+{
+ return d_->comment_;
+}
+
} // namespace xlnt
diff --git a/source/detail/cell_impl.cpp b/source/detail/cell_impl.cpp
index 1d7299e5..b4454ba6 100644
--- a/source/detail/cell_impl.cpp
+++ b/source/detail/cell_impl.cpp
@@ -4,15 +4,15 @@
namespace xlnt {
namespace detail {
- cell_impl::cell_impl() : parent_(nullptr), type_(cell::type::null), column(0), row(0), style_(nullptr), merged(false), has_hyperlink_(false)
+ cell_impl::cell_impl() : parent_(nullptr), type_(cell::type::null), column(0), row(0), style_(nullptr), merged(false), has_hyperlink_(false), comment_("", "")
{
}
-cell_impl::cell_impl(worksheet_impl *parent, int column_index, int row_index) : parent_(parent), type_(cell::type::null), column(column_index), row(row_index), style_(nullptr), merged(false), has_hyperlink_(false)
+cell_impl::cell_impl(worksheet_impl *parent, int column_index, int row_index) : parent_(parent), type_(cell::type::null), column(column_index), row(row_index), style_(nullptr), merged(false), has_hyperlink_(false), comment_("", "")
{
}
-cell_impl::cell_impl(const cell_impl &rhs)
+cell_impl::cell_impl(const cell_impl &rhs) : comment_("", "")
{
*this = rhs;
}
diff --git a/source/detail/cell_impl.hpp b/source/detail/cell_impl.hpp
index ce075e9e..755324ca 100644
--- a/source/detail/cell_impl.hpp
+++ b/source/detail/cell_impl.hpp
@@ -30,6 +30,7 @@ struct cell_impl
bool merged;
bool is_date_;
bool has_hyperlink_;
+ comment comment_;
};
} // namespace detail
diff --git a/source/detail/worksheet_impl.hpp b/source/detail/worksheet_impl.hpp
index 2d76464e..58a1779f 100644
--- a/source/detail/worksheet_impl.hpp
+++ b/source/detail/worksheet_impl.hpp
@@ -60,6 +60,7 @@ struct worksheet_impl
margins page_margins_;
std::vector merged_cells_;
std::unordered_map named_ranges_;
+ std::vector comments_;
};
} // namespace detail
diff --git a/source/reader.cpp b/source/reader.cpp
index d255ff5d..0837c581 100644
--- a/source/reader.cpp
+++ b/source/reader.cpp
@@ -8,6 +8,7 @@
#include "workbook/workbook.hpp"
#include "worksheet/worksheet.hpp"
#include "workbook/document_properties.hpp"
+#include "common/relationship.hpp"
#include "common/zip_file.hpp"
namespace xlnt {
@@ -85,21 +86,21 @@ std::string reader::read_dimension(const std::string &xml_string)
return dimension;
}
-std::unordered_map> reader::read_relationships(const std::string &content)
+std::vector reader::read_relationships(const std::string &content)
{
pugi::xml_document doc;
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
- std::unordered_map> relationships;
+ std::vector relationships;
for(auto relationship : root_node.children("Relationship"))
{
std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").as_string();
- relationships[id] = std::make_pair(target, type);
+ relationships.push_back(xlnt::relationship(type, id, target));
}
return relationships;
@@ -129,24 +130,13 @@ std::pair, std::unordered_map> &root_relationships,
- const std::unordered_map &override_types)
+std::string reader::determine_document_type(const std::unordered_map &override_types)
{
- auto relationship_match = std::find_if(root_relationships.begin(), root_relationships.end(),
- [](const std::pair> &v)
- { return v.second.second == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; });
std::string type;
- if(relationship_match != root_relationships.end())
+ if(override_types.find("/xl/workbook.xml") != override_types.end())
{
- std::string office_document_relationship = relationship_match->second.first;
-
- if(office_document_relationship[0] != '/')
- {
- office_document_relationship = std::string("/") + office_document_relationship;
- }
-
- type = override_types.at(office_document_relationship);
+ type = override_types.at("/xl/workbook.xml");
}
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
@@ -214,12 +204,7 @@ void read_worksheet_common(worksheet ws, const pugi::xml_node &root_node, const
else if(cell_node.attribute("s") != nullptr)
{
auto style_index = cell_node.attribute("s").as_int();
- auto number_format_id = number_format_ids[style_index];
-
- if(style_index < 0 || style_index >= number_format_ids.size())
- {
- throw std::out_of_range(std::to_string(style_index));
- }
+ auto number_format_id = number_format_ids.at(style_index);
if(number_format_id == 0) // integer
{
diff --git a/source/relationship.cpp b/source/relationship.cpp
index 3f006362..8786c571 100644
--- a/source/relationship.cpp
+++ b/source/relationship.cpp
@@ -2,16 +2,15 @@
namespace xlnt {
-relationship::relationship(const std::string &t, const std::string &r_id, const std::string &target_uri) : id_(r_id), source_uri_(""), target_uri_(target_uri)
+relationship::relationship(type t, const std::string &r_id, const std::string &target_uri) : type_(t), id_(r_id), source_uri_(""), target_uri_(target_uri)
{
- if(t == "hyperlink")
+ if(t == type::hyperlink)
{
- type_ = type::hyperlink;
target_mode_ = target_mode::external;
}
}
-relationship::relationship() : id_(""), source_uri_(""), target_uri_("")
+relationship::relationship() : type_(type::invalid), id_(""), source_uri_(""), target_uri_("")
{
}
diff --git a/source/workbook.cpp b/source/workbook.cpp
index 82db0986..c9aaa44c 100644
--- a/source/workbook.cpp
+++ b/source/workbook.cpp
@@ -52,6 +52,9 @@ workbook_impl::workbook_impl() : active_sheet_index_(0), date_1904_(false)
workbook::workbook() : d_(new detail::workbook_impl())
{
create_sheet("Sheet");
+ create_relationship("rId2", "sharedStrings.xml", relationship::type::shared_strings);
+ create_relationship("rId3", "styles.xml", relationship::type::styles);
+ create_relationship("rId4", "theme/theme1.xml", relationship::type::theme);
}
workbook::~workbook()
@@ -176,6 +179,7 @@ worksheet workbook::create_sheet()
}
d_->worksheets_.push_back(detail::worksheet_impl(this, title));
+ create_relationship("rId" + std::to_string(d_->relationships_.size() + 1), "worksheets/sheet" + std::to_string(d_->worksheets_.size()) + ".xml", relationship::type::worksheet);
return worksheet(&d_->worksheets_.back());
}
@@ -284,21 +288,22 @@ bool workbook::load(const std::string &filename)
zip_file f(filename, file_mode::open);
//auto core_properties = read_core_properties();
//auto app_properties = read_app_properties();
- auto root_relationships = reader::read_relationships(f.get_file_contents("_rels/.rels"));
auto content_types = reader::read_content_types(f.get_file_contents("[Content_Types].xml"));
- auto type = reader::determine_document_type(root_relationships, content_types.second);
+ auto type = reader::determine_document_type(content_types.second);
if(type != "excel")
{
throw std::runtime_error("unsupported document type: " + filename);
}
+ clear();
+
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);
+ create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
}
pugi::xml_document doc;
@@ -311,24 +316,25 @@ bool workbook::load(const std::string &filename)
auto sheets_node = root_node.child("sheets");
- clear();
-
std::vector shared_strings;
if(f.has_file("xl/sharedStrings.xml"))
{
shared_strings = xlnt::reader::read_shared_string(f.get_file_contents("xl/sharedStrings.xml"));
}
- pugi::xml_document styles_doc;
- styles_doc.load(f.get_file_contents("xl/styles.xml").c_str());
- auto stylesheet_node = styles_doc.child("styleSheet");
- auto cell_xfs_node = stylesheet_node.child("cellXfs");
-
std::vector number_format_ids;
-
- for(auto xf_node : cell_xfs_node.children("xf"))
+
+ if(f.has_file("xl/styles.xml"))
{
- number_format_ids.push_back(xf_node.attribute("numFmtId").as_int());
+ pugi::xml_document styles_doc;
+ styles_doc.load(f.get_file_contents("xl/styles.xml").c_str());
+ auto stylesheet_node = styles_doc.child("styleSheet");
+ auto cell_xfs_node = stylesheet_node.child("cellXfs");
+
+ for(auto xf_node : cell_xfs_node.children("xf"))
+ {
+ number_format_ids.push_back(xf_node.attribute("numFmtId").as_int());
+ }
}
for(auto sheet_node : sheets_node.children("sheet"))
@@ -343,7 +349,7 @@ bool workbook::load(const std::string &filename)
return true;
}
-void workbook::create_relationship(const std::string &id, const std::string &target, const std::string &type)
+void workbook::create_relationship(const std::string &id, const std::string &target, relationship::type type)
{
d_->relationships_.push_back(relationship(type, id, target));
}
@@ -468,6 +474,10 @@ worksheet workbook::operator[](std::size_t index)
void workbook::clear()
{
d_->worksheets_.clear();
+ d_->relationships_.clear();
+ d_->active_sheet_index_ = 0;
+ d_->date_1904_ = false;
+ d_->drawings_.clear();
}
bool workbook::save(std::vector &data)
@@ -495,6 +505,16 @@ bool workbook::save(const std::string &filename)
f.set_file_contents("xl/_rels/workbook.xml.rels", writer::write_workbook_rels(*this));
f.set_file_contents("xl/workbook.xml", writer::write_workbook(*this));
+
+ for(auto relationship : d_->relationships_)
+ {
+ if(relationship.get_type() == relationship::type::worksheet)
+ {
+ std::string sheet_index_string = relationship.get_target_uri().substr(16);
+ std::size_t sheet_index = std::stoi(sheet_index_string.substr(0, sheet_index_string.find('.'))) - 1;
+ f.set_file_contents("xl/" + relationship.get_target_uri(), writer::write_worksheet(get_sheet_by_index(sheet_index)));
+ }
+ }
return true;
}
@@ -514,7 +534,7 @@ std::vector xlnt::workbook::get_content_types() const
std::vector content_types;
content_types.push_back({ true, "xml", "", "application/xml" });
content_types.push_back({ true, "rels", "", "application/vnd.openxmlformats-package.relationships+xml" });
- content_types.push_back({ false, "", "xl/workbook.xml", "applications/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" });
+ content_types.push_back({ false, "", "/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" });
return content_types;
}
diff --git a/source/worksheet.cpp b/source/worksheet.cpp
index 8d67af2b..c82bcf72 100644
--- a/source/worksheet.cpp
+++ b/source/worksheet.cpp
@@ -299,10 +299,10 @@ std::vector worksheet::get_relationships()
return d_->relationships_;
}
-relationship worksheet::create_relationship(const std::string &relationship_type, const std::string &target_uri)
+relationship worksheet::create_relationship(relationship::type type, const std::string &target_uri)
{
std::string r_id = "rId" + std::to_string(d_->relationships_.size() + 1);
- d_->relationships_.push_back(relationship(relationship_type, r_id, target_uri));
+ d_->relationships_.push_back(relationship(type, r_id, target_uri));
return d_->relationships_.back();
}
@@ -496,4 +496,19 @@ void worksheet::reserve(std::size_t n)
d_->cell_map_.reserve(n);
}
+void worksheet::add_comment(const xlnt::comment &c)
+{
+ d_->comments_.push_back(c);
+}
+
+void worksheet::remove_comment(const xlnt::comment &c)
+{
+ d_->comments_.pop_back();
+}
+
+std::size_t worksheet::get_comment_count() const
+{
+ return d_->comments_.size();
+}
+
} // namespace xlnt
diff --git a/source/writer.cpp b/source/writer.cpp
index fe5a9a46..697d1f14 100644
--- a/source/writer.cpp
+++ b/source/writer.cpp
@@ -162,25 +162,30 @@ std::string writer::write_workbook(const workbook &wb)
auto sheets_node = root_node.append_child("sheets");
auto defined_names_node = root_node.append_child("definedNames");
- int i = 0;
- for(auto ws : wb)
+ for(auto relationship : wb.get_relationships())
{
- auto sheet_node = sheets_node.append_child("sheet");
- sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
- sheet_node.append_attribute("r:id").set_value((std::string("rId") + std::to_string(i + 1)).c_str());
- sheet_node.append_attribute("sheetId").set_value(std::to_string(i + 1).c_str());
-
- if(ws.has_auto_filter())
+ if(relationship.get_type() == relationship::type::worksheet)
{
- auto defined_name_node = defined_names_node.append_child("definedName");
- defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase");
- defined_name_node.append_attribute("hidden").set_value(1);
- defined_name_node.append_attribute("localSheetId").set_value(0);
- std::string name = "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string();
- defined_name_node.text().set(name.c_str());
- }
+ std::string sheet_index_string = relationship.get_target_uri().substr(16);
+ std::size_t sheet_index = std::stoi(sheet_index_string.substr(0, sheet_index_string.find('.'))) - 1;
+
+ auto ws = wb.get_sheet_by_index(sheet_index);
+
+ auto sheet_node = sheets_node.append_child("sheet");
+ sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
+ sheet_node.append_attribute("r:id").set_value(relationship.get_id().c_str());
+ sheet_node.append_attribute("sheetId").set_value(std::to_string(sheet_index + 1).c_str());
- i++;
+ if(ws.has_auto_filter())
+ {
+ auto defined_name_node = defined_names_node.append_child("definedName");
+ defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase");
+ defined_name_node.append_attribute("hidden").set_value(1);
+ defined_name_node.append_attribute("localSheetId").set_value(0);
+ std::string name = "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string();
+ defined_name_node.text().set(name.c_str());
+ }
+ }
}
auto calc_pr_node = root_node.append_child("calcPr");
@@ -507,9 +512,9 @@ std::string xlnt::writer::write_root_rels()
{
std::vector relationships;
- relationships.push_back(relationship("a"));
- relationships.push_back(relationship("b"));
- relationships.push_back(relationship("c"));
+ relationships.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml"));
+ relationships.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml"));
+ relationships.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml"));
return write_relationships(relationships);
}
@@ -525,7 +530,11 @@ std::string writer::write_relationships(const std::vector &relatio
auto app_props_node = root_node.append_child("Relationship");
app_props_node.append_attribute("Id").set_value(relationship.get_id().c_str());
app_props_node.append_attribute("Target").set_value(relationship.get_target_uri().c_str());
- app_props_node.append_attribute("Type").set_value(relationship.get_target_uri().c_str());
+ app_props_node.append_attribute("Type").set_value(relationship.get_type_string().c_str());
+ if(relationship.get_target_mode() == target_mode::external)
+ {
+ app_props_node.append_attribute("TargetMode").set_value("External");
+ }
}
std::stringstream ss;
diff --git a/tests/helpers/helper.hpp b/tests/helpers/helper.hpp
index a0496887..74c55076 100644
--- a/tests/helpers/helper.hpp
+++ b/tests/helpers/helper.hpp
@@ -8,25 +8,116 @@
class Helper
{
public:
- static bool EqualsFileContent(const std::string &reference_file, const std::string &fixture)
+ enum class difference_type
{
- std::string fixture_content;
-
- if(false)//PathHelper::FileExists(fixture))
+ names_differ,
+ missing_attribute,
+ attribute_values_differ,
+ missing_text,
+ text_values_differ,
+ missing_child,
+ child_order_differs,
+ equivalent,
+ };
+
+ struct comparison_result
+ {
+ difference_type difference;
+ std::string value_left;
+ std::string value_right;
+ operator bool() const { return difference == difference_type::equivalent; }
+ };
+
+ static comparison_result compare_xml(const pugi::xml_node &left, const pugi::xml_node &right)
+ {
+ std::string left_temp = left.name();
+ std::string right_temp = right.name();
+
+ if(left_temp != right_temp)
{
- std::fstream fixture_file;
- fixture_file.open(fixture);
- std::stringstream ss;
- ss << fixture_file.rdbuf();
- fixture_content = ss.str();
+ return {difference_type::names_differ, left_temp, right_temp};
}
- else
+
+ for(auto left_attribute : left.attributes())
{
- fixture_content = fixture;
+ left_temp = left_attribute.name();
+ auto right_attribute = right.attribute(left_attribute.name());
+
+ if(right_attribute == nullptr)
+ {
+ return {difference_type::missing_attribute, left_temp, "((empty))"};
+ }
+
+ left_temp = left_attribute.value();
+ right_temp = right_attribute.value();
+
+ if(left_temp != right_temp)
+ {
+ return {difference_type::attribute_values_differ, left_temp, right_temp};
+ }
}
-
+
+ if(left.text() != nullptr)
+ {
+ left_temp = left.text().as_string();
+
+ if(right.text() == nullptr)
+ {
+ return {difference_type::missing_text, left_temp, "((empty))"};
+ }
+
+ right_temp = right.text().as_string();
+
+ if(left_temp != right_temp)
+ {
+ return {difference_type::text_values_differ, left_temp, right_temp};
+ }
+ }
+ else if(right.text() != nullptr)
+ {
+ right_temp = right.text().as_string();
+ return {difference_type::text_values_differ, "((empty))", right_temp};
+ }
+
+ auto right_child_iter = right.children().begin();
+ for(auto left_child : left.children())
+ {
+ left_temp = left_child.name();
+
+ if(right_child_iter == right.children().end())
+ {
+ return {difference_type::child_order_differs, left_temp, "((end))"};
+ }
+
+ auto right_child = *right_child_iter;
+ right_child_iter++;
+
+ if(left_child.type() == pugi::xml_node_type::node_cdata || left_child.type() == pugi::xml_node_type::node_pcdata)
+ {
+ continue; // ignore text children, these have already been compared
+ }
+
+ auto child_comparison_result = compare_xml(left_child, right_child);
+
+ if(!child_comparison_result)
+ {
+ return child_comparison_result;
+ }
+ }
+
+ if(right_child_iter != right.children().end())
+ {
+ right_temp = right_child_iter->name();
+ return {difference_type::child_order_differs, "((end))", right_temp};
+ }
+
+ return {difference_type::equivalent, "", ""};
+ }
+
+ static bool EqualsFileContent(const std::string &reference_file, const std::string &observed_content)
+ {
std::string expected_content;
-
+
if(PathHelper::FileExists(reference_file))
{
std::fstream file;
@@ -39,23 +130,13 @@ public:
{
throw std::runtime_error("file not found");
}
-
- {
- pugi::xml_document doc;
- doc.load(fixture_content.c_str());
- std::stringstream ss;
- doc.save(ss);
- fixture_content = ss.str();
- }
-
- {
- pugi::xml_document doc;
- doc.load(expected_content.c_str());
- std::stringstream ss;
- doc.save(ss);
- expected_content = ss.str();
- }
-
- return expected_content == fixture_content;
+
+ pugi::xml_document doc_observed;
+ doc_observed.load(observed_content.c_str());
+
+ pugi::xml_document doc_expected;
+ doc_expected.load(expected_content.c_str());
+
+ return compare_xml(doc_expected, doc_observed);
}
};
diff --git a/tests/runner-autogen.cpp b/tests/runner-autogen.cpp
index 83e3ae57..8ef6a19c 100644
--- a/tests/runner-autogen.cpp
+++ b/tests/runner-autogen.cpp
@@ -21,7 +21,7 @@ int main( int argc, char *argv[] ) {
return status;
}
bool suite_test_cell_init = false;
-#include "c:\Users\taf656\Development\xlnt\tests\test_cell.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_cell.hpp"
static test_cell suite_test_cell;
@@ -238,7 +238,25 @@ public:
void runTest() { suite_test_cell.test_is_not_date_color_format(); }
} testDescription_suite_test_cell_test_is_not_date_color_format;
-#include "c:\Users\taf656\Development\xlnt\tests\test_chart.hpp"
+static class TestDescription_suite_test_cell_test_comment_count : public CxxTest::RealTestDescription {
+public:
+ TestDescription_suite_test_cell_test_comment_count() : CxxTest::RealTestDescription( Tests_test_cell, suiteDescription_test_cell, 394, "test_comment_count" ) {}
+ void runTest() { suite_test_cell.test_comment_count(); }
+} testDescription_suite_test_cell_test_comment_count;
+
+static class TestDescription_suite_test_cell_test_comment_assignment : public CxxTest::RealTestDescription {
+public:
+ TestDescription_suite_test_cell_test_comment_assignment() : CxxTest::RealTestDescription( Tests_test_cell, suiteDescription_test_cell, 410, "test_comment_assignment" ) {}
+ void runTest() { suite_test_cell.test_comment_assignment(); }
+} testDescription_suite_test_cell_test_comment_assignment;
+
+static class TestDescription_suite_test_cell_test_cell_offset : public CxxTest::RealTestDescription {
+public:
+ TestDescription_suite_test_cell_test_cell_offset() : CxxTest::RealTestDescription( Tests_test_cell, suiteDescription_test_cell, 424, "test_cell_offset" ) {}
+ void runTest() { suite_test_cell.test_cell_offset(); }
+} testDescription_suite_test_cell_test_cell_offset;
+
+#include "/Users/thomas/Development/xlnt/tests/test_chart.hpp"
static test_chart suite_test_chart;
@@ -329,7 +347,7 @@ public:
void runTest() { suite_test_chart.test_write_chart_scatter(); }
} testDescription_suite_test_chart_test_write_chart_scatter;
-#include "c:\Users\taf656\Development\xlnt\tests\test_named_range.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_named_range.hpp"
static test_named_range suite_test_named_range;
@@ -420,7 +438,7 @@ public:
void runTest() { suite_test_named_range.test_can_be_saved(); }
} testDescription_suite_test_named_range_test_can_be_saved;
-#include "c:\Users\taf656\Development\xlnt\tests\test_number_format.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_number_format.hpp"
static test_number_format suite_test_number_format;
@@ -523,7 +541,7 @@ public:
void runTest() { suite_test_number_format.test_mac_date(); }
} testDescription_suite_test_number_format_test_mac_date;
-#include "c:\Users\taf656\Development\xlnt\tests\test_props.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_props.hpp"
static test_props suite_test_props;
@@ -566,7 +584,7 @@ public:
void runTest() { suite_test_props.test_write_properties_app(); }
} testDescription_suite_test_props_test_write_properties_app;
-#include "c:\Users\taf656\Development\xlnt\tests\test_read.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_read.hpp"
static test_read suite_test_read;
@@ -699,7 +717,7 @@ public:
void runTest() { suite_test_read.test_read_date_value(); }
} testDescription_suite_test_read_test_read_date_value;
-#include "c:\Users\taf656\Development\xlnt\tests\test_strings.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_strings.hpp"
static test_strings suite_test_strings;
@@ -730,7 +748,7 @@ public:
void runTest() { suite_test_strings.test_formatted_string_table(); }
} testDescription_suite_test_strings_test_formatted_string_table;
-#include "c:\Users\taf656\Development\xlnt\tests\test_style.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_style.hpp"
static test_style suite_test_style;
@@ -827,7 +845,7 @@ public:
void runTest() { suite_test_style.test_read_cell_style(); }
} testDescription_suite_test_style_test_read_cell_style;
-#include "c:\Users\taf656\Development\xlnt\tests\test_theme.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_theme.hpp"
static test_theme suite_test_theme;
@@ -840,7 +858,7 @@ public:
void runTest() { suite_test_theme.test_write_theme(); }
} testDescription_suite_test_theme_test_write_theme;
-#include "c:\Users\taf656\Development\xlnt\tests\test_workbook.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_workbook.hpp"
static test_workbook suite_test_workbook;
@@ -961,7 +979,7 @@ public:
void runTest() { suite_test_workbook.test_write_regular_float(); }
} testDescription_suite_test_workbook_test_write_regular_float;
-#include "c:\Users\taf656\Development\xlnt\tests\test_worksheet.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_worksheet.hpp"
static test_worksheet suite_test_worksheet;
@@ -1130,7 +1148,7 @@ public:
void runTest() { suite_test_worksheet.test_printer_settings(); }
} testDescription_suite_test_worksheet_test_printer_settings;
-#include "c:\Users\taf656\Development\xlnt\tests\test_write.hpp"
+#include "/Users/thomas/Development/xlnt/tests/test_write.hpp"
static test_write suite_test_write;
diff --git a/tests/test_cell.hpp b/tests/test_cell.hpp
index 4fcc248c..e148cc36 100644
--- a/tests/test_cell.hpp
+++ b/tests/test_cell.hpp
@@ -70,13 +70,13 @@ public:
void test_bad_column_index()
{
- for(auto bad_string : {"JJJJ", "", "$", "1"})
+ for(auto bad_string : {"JJJJ", "", "$", "1"})
{
TS_ASSERT_THROWS(xlnt::cell_reference::column_index_from_string(bad_string), xlnt::column_string_index_exception);
}
}
- void test_column_letter_boundries()
+ void test_column_letter_boundaries()
{
TS_ASSERT_THROWS(xlnt::cell_reference::column_string_from_index(0),
xlnt::column_string_index_exception);
@@ -336,6 +336,16 @@ public:
TS_ASSERT_EQUALS(xlnt::cell::type::numeric, cell.get_data_type());
TS_ASSERT_EQUALS(cell, xlnt::time(3, 40));
}
+
+ void test_timedelta()
+ {
+ xlnt::worksheet ws = wb.create_sheet();
+ xlnt::cell cell(ws, "A1");
+
+ cell = xlnt::timedelta().days(1).hours(3);
+ TS_ASSERT_EQUALS(cell, 1.125);
+ TS_ASSERT_EQUALS(cell.get_data_type(), xlnt::cell::type::numeric);
+ }
void test_date_format_on_non_date()
{
@@ -390,6 +400,42 @@ public:
TS_ASSERT(!cell.is_date());
}
+
+ void test_comment_count()
+ {
+ xlnt::worksheet ws = wb.create_sheet();
+ xlnt::cell cell(ws, "A1");
+
+ TS_ASSERT(ws.get_comment_count() == 0);
+ cell.set_comment(xlnt::comment("text", "author"));
+ TS_ASSERT(ws.get_comment_count() == 1);
+ cell.set_comment(xlnt::comment("text", "author"));
+ TS_ASSERT(ws.get_comment_count() == 1);
+ cell.clear_comment();
+ TS_ASSERT(ws.get_comment_count() == 0);
+ cell.clear_comment();
+ TS_ASSERT(ws.get_comment_count() == 0);
+ }
+
+ void test_comment_assignment()
+ {
+ xlnt::worksheet ws = wb.create_sheet();
+ xlnt::cell cell(ws, "A1");
+
+ xlnt::comment c("text", "author");
+ cell.set_comment(c);
+ TS_ASSERT_THROWS_ANYTHING(ws.get_cell("A2").set_comment(c));
+ ws.get_cell("A2").set_comment(xlnt::comment("text2", "author2"));
+ TS_ASSERT_THROWS_ANYTHING(ws.get_cell("A1").set_comment(ws.get_cell("A2").get_comment()));
+ ws.get_cell("A1").clear_comment();
+ TS_ASSERT_THROWS_NOTHING(ws.get_cell("A2").set_comment(c));
+ }
+
+ void test_cell_offset()
+ {
+ xlnt::worksheet ws = wb.create_sheet();
+ TS_ASSERT(ws.get_cell("B15").offset(1, 2).get_reference() == "C17");
+ }
private:
xlnt::workbook wb;
diff --git a/tests/test_worksheet.hpp b/tests/test_worksheet.hpp
index d27acbed..35b9fa65 100644
--- a/tests/test_worksheet.hpp
+++ b/tests/test_worksheet.hpp
@@ -164,7 +164,7 @@ public:
void test_bad_relationship_type()
{
- xlnt::relationship rel("bad_type");
+ xlnt::relationship rel("bad");
}
void test_append_list()