lots of fixes

This commit is contained in:
Thomas Fussell 2014-07-19 14:43:48 -04:00
parent a87c144340
commit a7bb9f0e77
19 changed files with 431 additions and 137 deletions

View File

@ -44,8 +44,17 @@ namespace detail {
struct cell_impl; struct cell_impl;
} // namespace detail } // 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_;
};
/// <summary> /// <summary>
/// Describes cell associated properties. /// Describes cell associated properties.
/// </summary> /// </summary>
@ -116,7 +125,8 @@ public:
//std::pair<int, int> get_anchor() const; //std::pair<int, int> get_anchor() const;
comment get_comment() const; comment get_comment() const;
void set_comment(comment comment); void set_comment(const comment &comment);
void clear_comment();
std::string get_formula() const; std::string get_formula() const;
void set_formula(const std::string &formula); void set_formula(const std::string &formula);
@ -126,6 +136,10 @@ public:
void set_null(); void set_null();
cell offset(row_t row, column_t column);
worksheet get_parent();
cell &operator=(const cell &rhs); cell &operator=(const cell &rhs);
cell &operator=(bool value); cell &operator=(bool value);
cell &operator=(int value); cell &operator=(int value);

View File

@ -47,9 +47,78 @@ enum class target_mode
/// </summary> /// </summary>
class relationship 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();
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 = "");
/// <summary> /// <summary>
/// gets a string that identifies the relationship. /// gets a string that identifies the relationship.
@ -71,20 +140,10 @@ public:
/// </summary> /// </summary>
std::string get_target_uri() const { return target_uri_; } std::string get_target_uri() const { return target_uri_; }
private: type get_type() const { return type_; }
relationship(const std::string &id, const std::string &relationship_type_, const std::string &source_uri, target_mode target_mode, const std::string &target_uri); std::string get_type_string() const { return type_to_string(type_); }
//relationship &operator=(const relationship &rhs) = delete;
enum class type private:
{
hyperlink,
drawing,
worksheet,
sharedStrings,
styles,
theme
};
type type_; type type_;
std::string id_; std::string id_;
std::string source_uri_; std::string source_uri_;

View File

@ -30,6 +30,7 @@
namespace xlnt { namespace xlnt {
class relationship;
class workbook; class workbook;
class worksheet; class worksheet;
class document_properties; class document_properties;
@ -38,10 +39,9 @@ class zip_file;
class reader class reader
{ {
public: public:
static std::unordered_map<std::string, std::pair<std::string, std::string>> read_relationships(const std::string &content); static std::vector<relationship> read_relationships(const std::string &content);
static std::pair<std::unordered_map<std::string, std::string>, std::unordered_map<std::string, std::string>> read_content_types(const std::string &content); static std::pair<std::unordered_map<std::string, std::string>, std::unordered_map<std::string, std::string>> read_content_types(const std::string &content);
static std::string determine_document_type(const std::unordered_map<std::string, std::pair<std::string, std::string>> &root_relationships, static std::string determine_document_type(const std::unordered_map<std::string, std::string> &override_types);
const std::unordered_map<std::string, std::string> &override_types);
static worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table); static worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table);
static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids); static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids);
static std::vector<std::string> read_shared_string(const std::string &xml_string); static std::vector<std::string> read_shared_string(const std::string &xml_string);

View File

@ -29,6 +29,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "../common/relationship.hpp"
namespace xlnt { namespace xlnt {
class drawing; class drawing;
@ -159,7 +161,7 @@ public:
std::vector<content_type> get_content_types() const; std::vector<content_type> 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; relationship get_relationship(const std::string &id) const;
std::vector<relationship> get_relationships() const; std::vector<relationship> get_relationships() const;

View File

@ -30,11 +30,13 @@
#include <vector> #include <vector>
#include "../common/types.hpp" #include "../common/types.hpp"
#include "../common/relationship.hpp"
namespace xlnt { namespace xlnt {
class cell; class cell;
class cell_reference; class cell_reference;
class comment;
class range; class range;
class range_reference; class range_reference;
class relationship; class relationship;
@ -203,7 +205,7 @@ public:
range_reference calculate_dimension() const; range_reference calculate_dimension() const;
// relationships // 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<relationship> get_relationships(); std::vector<relationship> get_relationships();
// charts // charts
@ -244,6 +246,11 @@ public:
void auto_filter(const range_reference &reference); void auto_filter(const range_reference &reference);
void unset_auto_filter(); void unset_auto_filter();
bool has_auto_filter() const; 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); void reserve(std::size_t n);

View File

@ -23,6 +23,8 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <string>
const std::string xlnt_version = "0.1.0"; const std::string xlnt_version = "0.1.0";
const std::string author = "Thomas Fussell"; const std::string author = "Thomas Fussell";

View File

@ -560,7 +560,7 @@ void cell::set_hyperlink(const std::string &hyperlink)
} }
d_->has_hyperlink_ = true; 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) if(d_->type_ == type::null)
{ {
@ -584,6 +584,26 @@ void cell::set_formula(const std::string &formula)
d_->string_value = 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) void cell::set_error(const std::string &error)
{ {
if(error.length() == 0 || error[0] != '#') if(error.length() == 0 || error[0] != '#')
@ -595,4 +615,19 @@ void cell::set_error(const std::string &error)
d_->string_value = 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 } // namespace xlnt

View File

@ -4,15 +4,15 @@
namespace xlnt { namespace xlnt {
namespace detail { 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; *this = rhs;
} }

View File

@ -30,6 +30,7 @@ struct cell_impl
bool merged; bool merged;
bool is_date_; bool is_date_;
bool has_hyperlink_; bool has_hyperlink_;
comment comment_;
}; };
} // namespace detail } // namespace detail

View File

@ -60,6 +60,7 @@ struct worksheet_impl
margins page_margins_; margins page_margins_;
std::vector<range_reference> merged_cells_; std::vector<range_reference> merged_cells_;
std::unordered_map<std::string, range_reference> named_ranges_; std::unordered_map<std::string, range_reference> named_ranges_;
std::vector<comment> comments_;
}; };
} // namespace detail } // namespace detail

View File

@ -8,6 +8,7 @@
#include "workbook/workbook.hpp" #include "workbook/workbook.hpp"
#include "worksheet/worksheet.hpp" #include "worksheet/worksheet.hpp"
#include "workbook/document_properties.hpp" #include "workbook/document_properties.hpp"
#include "common/relationship.hpp"
#include "common/zip_file.hpp" #include "common/zip_file.hpp"
namespace xlnt { namespace xlnt {
@ -85,21 +86,21 @@ std::string reader::read_dimension(const std::string &xml_string)
return dimension; return dimension;
} }
std::unordered_map<std::string, std::pair<std::string, std::string>> reader::read_relationships(const std::string &content) std::vector<relationship> reader::read_relationships(const std::string &content)
{ {
pugi::xml_document doc; pugi::xml_document doc;
doc.load(content.c_str()); doc.load(content.c_str());
auto root_node = doc.child("Relationships"); auto root_node = doc.child("Relationships");
std::unordered_map<std::string, std::pair<std::string, std::string>> relationships; std::vector<relationship> relationships;
for(auto relationship : root_node.children("Relationship")) for(auto relationship : root_node.children("Relationship"))
{ {
std::string id = relationship.attribute("Id").as_string(); std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string(); std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").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; return relationships;
@ -129,24 +130,13 @@ std::pair<std::unordered_map<std::string, std::string>, std::unordered_map<std::
return std::make_pair(default_types, override_types); return std::make_pair(default_types, override_types);
} }
std::string reader::determine_document_type(const std::unordered_map<std::string, std::pair<std::string, std::string>> &root_relationships, std::string reader::determine_document_type(const std::unordered_map<std::string, std::string> &override_types)
const std::unordered_map<std::string, std::string> &override_types)
{ {
auto relationship_match = std::find_if(root_relationships.begin(), root_relationships.end(),
[](const std::pair<std::string, std::pair<std::string, std::string>> &v)
{ return v.second.second == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; });
std::string type; 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; type = override_types.at("/xl/workbook.xml");
if(office_document_relationship[0] != '/')
{
office_document_relationship = std::string("/") + office_document_relationship;
}
type = override_types.at(office_document_relationship);
} }
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+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) else if(cell_node.attribute("s") != nullptr)
{ {
auto style_index = cell_node.attribute("s").as_int(); auto style_index = cell_node.attribute("s").as_int();
auto number_format_id = number_format_ids[style_index]; auto number_format_id = number_format_ids.at(style_index);
if(style_index < 0 || style_index >= number_format_ids.size())
{
throw std::out_of_range(std::to_string(style_index));
}
if(number_format_id == 0) // integer if(number_format_id == 0) // integer
{ {

View File

@ -2,16 +2,15 @@
namespace xlnt { 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; target_mode_ = target_mode::external;
} }
} }
relationship::relationship() : id_(""), source_uri_(""), target_uri_("") relationship::relationship() : type_(type::invalid), id_(""), source_uri_(""), target_uri_("")
{ {
} }

View File

@ -52,6 +52,9 @@ workbook_impl::workbook_impl() : active_sheet_index_(0), date_1904_(false)
workbook::workbook() : d_(new detail::workbook_impl()) workbook::workbook() : d_(new detail::workbook_impl())
{ {
create_sheet("Sheet"); 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() workbook::~workbook()
@ -176,6 +179,7 @@ worksheet workbook::create_sheet()
} }
d_->worksheets_.push_back(detail::worksheet_impl(this, title)); 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()); return worksheet(&d_->worksheets_.back());
} }
@ -284,21 +288,22 @@ bool workbook::load(const std::string &filename)
zip_file f(filename, file_mode::open); zip_file f(filename, file_mode::open);
//auto core_properties = read_core_properties(); //auto core_properties = read_core_properties();
//auto app_properties = read_app_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 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") if(type != "excel")
{ {
throw std::runtime_error("unsupported document type: " + filename); throw std::runtime_error("unsupported document type: " + filename);
} }
clear();
auto workbook_relationships = reader::read_relationships(f.get_file_contents("xl/_rels/workbook.xml.rels")); auto workbook_relationships = reader::read_relationships(f.get_file_contents("xl/_rels/workbook.xml.rels"));
for(auto relationship : workbook_relationships) 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; pugi::xml_document doc;
@ -311,24 +316,25 @@ bool workbook::load(const std::string &filename)
auto sheets_node = root_node.child("sheets"); auto sheets_node = root_node.child("sheets");
clear();
std::vector<std::string> shared_strings; std::vector<std::string> shared_strings;
if(f.has_file("xl/sharedStrings.xml")) if(f.has_file("xl/sharedStrings.xml"))
{ {
shared_strings = xlnt::reader::read_shared_string(f.get_file_contents("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<int> number_format_ids; std::vector<int> 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")) for(auto sheet_node : sheets_node.children("sheet"))
@ -343,7 +349,7 @@ bool workbook::load(const std::string &filename)
return true; 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)); d_->relationships_.push_back(relationship(type, id, target));
} }
@ -468,6 +474,10 @@ worksheet workbook::operator[](std::size_t index)
void workbook::clear() void workbook::clear()
{ {
d_->worksheets_.clear(); d_->worksheets_.clear();
d_->relationships_.clear();
d_->active_sheet_index_ = 0;
d_->date_1904_ = false;
d_->drawings_.clear();
} }
bool workbook::save(std::vector<unsigned char> &data) bool workbook::save(std::vector<unsigned char> &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/_rels/workbook.xml.rels", writer::write_workbook_rels(*this));
f.set_file_contents("xl/workbook.xml", writer::write_workbook(*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; return true;
} }
@ -514,7 +534,7 @@ std::vector<content_type> xlnt::workbook::get_content_types() const
std::vector<content_type> content_types; std::vector<content_type> content_types;
content_types.push_back({ true, "xml", "", "application/xml" }); content_types.push_back({ true, "xml", "", "application/xml" });
content_types.push_back({ true, "rels", "", "application/vnd.openxmlformats-package.relationships+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; return content_types;
} }

View File

@ -299,10 +299,10 @@ std::vector<relationship> worksheet::get_relationships()
return d_->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); 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(); return d_->relationships_.back();
} }
@ -496,4 +496,19 @@ void worksheet::reserve(std::size_t n)
d_->cell_map_.reserve(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 } // namespace xlnt

View File

@ -162,25 +162,30 @@ std::string writer::write_workbook(const workbook &wb)
auto sheets_node = root_node.append_child("sheets"); auto sheets_node = root_node.append_child("sheets");
auto defined_names_node = root_node.append_child("definedNames"); auto defined_names_node = root_node.append_child("definedNames");
int i = 0; for(auto relationship : wb.get_relationships())
for(auto ws : wb)
{ {
auto sheet_node = sheets_node.append_child("sheet"); if(relationship.get_type() == relationship::type::worksheet)
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())
{ {
auto defined_name_node = defined_names_node.append_child("definedName"); std::string sheet_index_string = relationship.get_target_uri().substr(16);
defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase"); std::size_t sheet_index = std::stoi(sheet_index_string.substr(0, sheet_index_string.find('.'))) - 1;
defined_name_node.append_attribute("hidden").set_value(1);
defined_name_node.append_attribute("localSheetId").set_value(0); auto ws = wb.get_sheet_by_index(sheet_index);
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 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"); auto calc_pr_node = root_node.append_child("calcPr");
@ -507,9 +512,9 @@ std::string xlnt::writer::write_root_rels()
{ {
std::vector<relationship> relationships; std::vector<relationship> relationships;
relationships.push_back(relationship("a")); relationships.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml"));
relationships.push_back(relationship("b")); relationships.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml"));
relationships.push_back(relationship("c")); relationships.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml"));
return write_relationships(relationships); return write_relationships(relationships);
} }
@ -525,7 +530,11 @@ std::string writer::write_relationships(const std::vector<relationship> &relatio
auto app_props_node = root_node.append_child("Relationship"); 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("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("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; std::stringstream ss;

View File

@ -8,25 +8,116 @@
class Helper class Helper
{ {
public: public:
static bool EqualsFileContent(const std::string &reference_file, const std::string &fixture) enum class difference_type
{ {
std::string fixture_content; names_differ,
missing_attribute,
if(false)//PathHelper::FileExists(fixture)) 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; return {difference_type::names_differ, left_temp, right_temp};
fixture_file.open(fixture);
std::stringstream ss;
ss << fixture_file.rdbuf();
fixture_content = ss.str();
} }
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; std::string expected_content;
if(PathHelper::FileExists(reference_file)) if(PathHelper::FileExists(reference_file))
{ {
std::fstream file; std::fstream file;
@ -39,23 +130,13 @@ public:
{ {
throw std::runtime_error("file not found"); throw std::runtime_error("file not found");
} }
{ pugi::xml_document doc_observed;
pugi::xml_document doc; doc_observed.load(observed_content.c_str());
doc.load(fixture_content.c_str());
std::stringstream ss; pugi::xml_document doc_expected;
doc.save(ss); doc_expected.load(expected_content.c_str());
fixture_content = ss.str();
} return compare_xml(doc_expected, doc_observed);
{
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;
} }
}; };

View File

@ -21,7 +21,7 @@ int main( int argc, char *argv[] ) {
return status; return status;
} }
bool suite_test_cell_init = false; 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; static test_cell suite_test_cell;
@ -238,7 +238,25 @@ public:
void runTest() { suite_test_cell.test_is_not_date_color_format(); } void runTest() { suite_test_cell.test_is_not_date_color_format(); }
} testDescription_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; static test_chart suite_test_chart;
@ -329,7 +347,7 @@ public:
void runTest() { suite_test_chart.test_write_chart_scatter(); } void runTest() { suite_test_chart.test_write_chart_scatter(); }
} testDescription_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; static test_named_range suite_test_named_range;
@ -420,7 +438,7 @@ public:
void runTest() { suite_test_named_range.test_can_be_saved(); } void runTest() { suite_test_named_range.test_can_be_saved(); }
} testDescription_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; static test_number_format suite_test_number_format;
@ -523,7 +541,7 @@ public:
void runTest() { suite_test_number_format.test_mac_date(); } void runTest() { suite_test_number_format.test_mac_date(); }
} testDescription_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; static test_props suite_test_props;
@ -566,7 +584,7 @@ public:
void runTest() { suite_test_props.test_write_properties_app(); } void runTest() { suite_test_props.test_write_properties_app(); }
} testDescription_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; static test_read suite_test_read;
@ -699,7 +717,7 @@ public:
void runTest() { suite_test_read.test_read_date_value(); } void runTest() { suite_test_read.test_read_date_value(); }
} testDescription_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; static test_strings suite_test_strings;
@ -730,7 +748,7 @@ public:
void runTest() { suite_test_strings.test_formatted_string_table(); } void runTest() { suite_test_strings.test_formatted_string_table(); }
} testDescription_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; static test_style suite_test_style;
@ -827,7 +845,7 @@ public:
void runTest() { suite_test_style.test_read_cell_style(); } void runTest() { suite_test_style.test_read_cell_style(); }
} testDescription_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; static test_theme suite_test_theme;
@ -840,7 +858,7 @@ public:
void runTest() { suite_test_theme.test_write_theme(); } void runTest() { suite_test_theme.test_write_theme(); }
} testDescription_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; static test_workbook suite_test_workbook;
@ -961,7 +979,7 @@ public:
void runTest() { suite_test_workbook.test_write_regular_float(); } void runTest() { suite_test_workbook.test_write_regular_float(); }
} testDescription_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; static test_worksheet suite_test_worksheet;
@ -1130,7 +1148,7 @@ public:
void runTest() { suite_test_worksheet.test_printer_settings(); } void runTest() { suite_test_worksheet.test_printer_settings(); }
} testDescription_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; static test_write suite_test_write;

View File

@ -70,13 +70,13 @@ public:
void test_bad_column_index() 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); 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), TS_ASSERT_THROWS(xlnt::cell_reference::column_string_from_index(0),
xlnt::column_string_index_exception); xlnt::column_string_index_exception);
@ -336,6 +336,16 @@ public:
TS_ASSERT_EQUALS(xlnt::cell::type::numeric, cell.get_data_type()); TS_ASSERT_EQUALS(xlnt::cell::type::numeric, cell.get_data_type());
TS_ASSERT_EQUALS(cell, xlnt::time(3, 40)); 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() void test_date_format_on_non_date()
{ {
@ -390,6 +400,42 @@ public:
TS_ASSERT(!cell.is_date()); 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: private:
xlnt::workbook wb; xlnt::workbook wb;

View File

@ -164,7 +164,7 @@ public:
void test_bad_relationship_type() void test_bad_relationship_type()
{ {
xlnt::relationship rel("bad_type"); xlnt::relationship rel("bad");
} }
void test_append_list() void test_append_list()