improve manifest, implement true thumbnail handling, being refactoring serialization tests

This commit is contained in:
Thomas Fussell 2016-08-06 10:40:17 -04:00
parent cf80c46b66
commit 6c829a235a
124 changed files with 1059 additions and 3042 deletions

View File

@ -115,106 +115,82 @@ public:
/// </summary> /// </summary>
bool has_package_relationship(relationship::type type) const; bool has_package_relationship(relationship::type type) const;
/// <summary>
/// Returns true if the manifest contains a package relationship with the given id.
/// </summary>
bool has_package_relationship(const std::string &rel_id) const;
/// <summary> /// <summary>
/// Returns true if the manifest contains a relationship with the given type with part as the source. /// Returns true if the manifest contains a relationship with the given type with part as the source.
/// </summary> /// </summary>
bool has_part_relationship(const path &part, relationship::type type) const; bool has_part_relationship(const path &part, relationship::type type) const;
/// <summary>
/// Returns true if the manifest contains a relationship with the given type with part as the source.
/// </summary>
bool has_part_relationship(const path &part, const std::string &rel_id) const;
relationship get_package_relationship(relationship::type type) const; relationship get_package_relationship(relationship::type type) const;
relationship get_package_relationship(const std::string &rel_id) const;
std::vector<relationship> get_package_relationships() const;
std::vector<relationship> get_package_relationships(relationship::type type) const; std::vector<relationship> get_package_relationships(relationship::type type) const;
relationship get_part_relationship(const path &part, relationship::type type) const; relationship get_part_relationship(const path &part, relationship::type type) const;
relationship get_part_relationship(const path &part, const std::string &rel_id) const;
std::vector<relationship> get_part_relationships(const path &part) const;
std::vector<relationship> get_part_relationships(const path &part, relationship::type type) const; std::vector<relationship> get_part_relationships(const path &part, relationship::type type) const;
/// <summary> /// <summary>
/// Given the path to a part, returns the content type of the part as /// Given the path to a part, returns the content type of the part as
/// a content_type enum value or content_type::unknown if it isn't known. /// a content_type enum value or content_type::unknown if it isn't known.
/// </summary> /// </summary>
content_type get_part_content_type(const path &part) const; content_type get_content_type(const path &part) const;
/// <summary> /// <summary>
/// Given the path to a part, returns the content type of the part as a string. /// Given the path to a part, returns the content type of the part as a string.
/// </summary> /// </summary>
std::string get_part_content_type_string(const path &part) const; std::string get_content_type_string(const path &part) const;
/// <summary> void register_override_type(const path &part, const std::string &content_type);
/// Registers a part of the given type at the standard path and creates a
/// relationship with the new part as a target of "source". The type of the
/// relationship is inferred based on the provided types.
/// </summary>
void register_part(const path &part, const path &parent, const std::string &content_type, relationship::type relation);
/// <summary>
///
/// </summary>
void register_part(const path &parent, const relationship &rel, const std::string &content_type);
/// <summary>
/// Registers a package part of the given type at the standard path and creates a
/// relationship with the package root as the source. The type of the
/// relationship is inferred based on the provided type.
/// </summary>
void register_package_part(const path &part, const std::string &content_type, relationship::type relation);
/// <summary> /// <summary>
/// Unregisters the path of the part of the given type and removes all relationships /// Unregisters the path of the part of the given type and removes all relationships
/// with the part as a source or target. /// with the part as a source or target.
/// </summary> /// </summary>
void unregister_part(const path &part); void unregister_override_type(const path &part);
/// <summary> /// <summary>
/// Returns true if the part at the given path has been registered in this manifest. /// Returns true if the part at the given path has been registered in this manifest.
/// </summary> /// </summary>
bool has_part(const path &part) const; bool has_override_type(const path &part) const;
/// <summary> std::vector<path> get_overriden_parts() const;
/// Returns the path of every registered part in this manifest.
/// </summary>
std::vector<path> get_parts() const;
/// <summary>
/// Returns every registered relationship with the package root as the source.
/// </summary>
std::vector<relationship> get_package_relationships() const;
/// <summary>
/// Returns the package relationship with the given ID.
/// </summary>
relationship get_package_relationship(const std::string &rel_id);
/// <summary>
/// Returns every registered relationship with the part of the given type
/// as the source.
/// </summary>
std::vector<relationship> get_part_relationships(const path &part) const;
/// <summary>
/// Returns the registered relationship with the part of the given type
/// as the source and the ID matching the provided ID.
/// </summary>
relationship get_part_relationship(const path &part, const std::string &rel_id) const;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
std::string register_external_package_relationship(relationship::type type, const std::string &target_uri); std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
std::string register_external_package_relationship(relationship::type type, const std::string &target_uri, const std::string &rel_id); std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
std::string register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri); std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
std::string register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri, const std::string &rel_id); std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id);
/// <summary> /// <summary>
/// Returns true if a default content type for the extension has been registered. /// Returns true if a default content type for the extension has been registered.
@ -226,6 +202,8 @@ public:
/// </summary> /// </summary>
std::vector<std::string> get_default_type_extensions() const; std::vector<std::string> get_default_type_extensions() const;
std::vector<path> get_parts_with_relationships() const;
/// <summary> /// <summary>
/// Returns the registered default content type for parts of the given extension. /// Returns the registered default content type for parts of the given extension.
/// </summary> /// </summary>
@ -248,7 +226,7 @@ private:
std::vector<relationship> relationships; std::vector<relationship> relationships;
}; };
std::string next_package_relationship_id() const;\ std::string next_package_relationship_id() const;
std::string next_relationship_id(const path &part) const; std::string next_relationship_id(const path &part) const;

View File

@ -85,7 +85,6 @@ enum class XLNT_CLASS relationship_type
styles, styles,
table_definition, table_definition,
volatile_dependencies, volatile_dependencies,
workbook,
worksheet, worksheet,
hyperlink, hyperlink,

View File

@ -373,10 +373,10 @@ public:
// serialization // serialization
void save(std::vector<std::uint8_t> &data); void save(std::vector<std::uint8_t> &data) const;
void save(const std::string &filename); void save(const std::string &filename) const;
void save(const xlnt::path &filename); void save(const xlnt::path &filename) const;
void save(std::ostream &stream); void save(std::ostream &stream) const;
void load(const std::vector<std::uint8_t> &data); void load(const std::vector<std::uint8_t> &data);
void load(const std::string &filename); void load(const std::string &filename);
@ -423,7 +423,8 @@ public:
// thumbnail // thumbnail
void set_thumbnail(const std::vector<std::uint8_t> &thumbnail); void set_thumbnail(const std::vector<std::uint8_t> &thumbnail,
const std::string &extension, const std::string &content_type);
const std::vector<std::uint8_t> &get_thumbnail() const; const std::vector<std::uint8_t> &get_thumbnail() const;
// operators // operators

View File

@ -77,41 +77,63 @@ std::pair<bool, xlnt::time> cast_time(const std::string &s)
{ {
xlnt::time result; xlnt::time result;
try std::vector<std::string> time_components;
{ std::size_t prev = 0;
auto last_colon = s.find_last_of(':'); auto colon_index = s.find(':');
if (last_colon == std::string::npos) return{ false, result };
double seconds = std::stod(s.substr(last_colon + 1));
result.second = static_cast<int>(seconds);
result.microsecond = static_cast<int>((seconds - static_cast<double>(result.second)) * 1e6);
auto first_colon = s.find_first_of(':'); while (colon_index != std::string::npos)
{
time_components.push_back(s.substr(prev, colon_index - prev));
prev = colon_index + 1;
colon_index = s.find(':', colon_index + 1);
}
if (first_colon == last_colon) time_components.push_back(s.substr(prev, colon_index - prev));
{
auto decimal_pos = s.find('.'); if (time_components.size() < 2 || time_components.size() > 3)
if (decimal_pos != std::string::npos)
{
result.minute = std::stoi(s.substr(0, first_colon));
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = result.second;
result.second = 0;
}
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = std::stoi(s.substr(first_colon + 1, last_colon - first_colon - 1));
}
}
catch (std::invalid_argument)
{ {
return{ false, result }; return{ false, result };
} }
std::vector<double> numeric_components;
for (auto component : time_components)
{
if (component.empty() || (component.substr(0, component.find('.')).size() > 2))
{
return{ false, result };
}
for (auto d : component)
{
if (!(d >= '0' && d <= '9') && d != '.')
{
return{ false, result };
}
}
auto without_leading_zero = component.front() == '0' ? component.substr(1) : component;
auto numeric = std::stod(without_leading_zero);
numeric_components.push_back(numeric);
}
result.hour = static_cast<int>(numeric_components[0]);
result.minute = static_cast<int>(numeric_components[1]);
if (result.minute != numeric_components[1])
{
result.minute = result.hour;
result.hour = 0;
result.second = static_cast<int>(numeric_components[1]);
result.microsecond = static_cast<int>((numeric_components[1] - result.second) * 1E6);
}
else if (numeric_components.size() > 2)
{
result.second = static_cast<int>(numeric_components[2]);
result.microsecond = static_cast<int>((numeric_components[2] - result.second) * 1E6);
}
return{ true, result }; return{ true, result };
} }
@ -453,9 +475,9 @@ void cell::set_hyperlink(const std::string &hyperlink)
void cell::set_formula(const std::string &formula) void cell::set_formula(const std::string &formula)
{ {
if (formula.length() == 0) if (formula.empty())
{ {
throw invalid_data_type(); throw invalid_parameter();
} }
if (formula[0] == '=') if (formula[0] == '=')
@ -477,7 +499,7 @@ std::string cell::get_formula() const
{ {
if (d_->formula_.empty()) if (d_->formula_.empty())
{ {
throw invalid_data_type(); throw invalid_attribute();
} }
return d_->formula_; return d_->formula_;

View File

@ -139,8 +139,8 @@ public:
auto cell = ws.get_cell(xlnt::cell_reference(1, 1)); auto cell = ws.get_cell(xlnt::cell_reference(1, 1));
TS_ASSERT(!cell.has_formula()); TS_ASSERT(!cell.has_formula());
TS_ASSERT_THROWS(cell.set_formula(""), std::runtime_error); TS_ASSERT_THROWS(cell.set_formula(""), xlnt::invalid_parameter);
TS_ASSERT_THROWS(cell.get_formula(), std::runtime_error); TS_ASSERT_THROWS(cell.get_formula(), xlnt::invalid_attribute);
cell.set_formula("=42"); cell.set_formula("=42");
TS_ASSERT(cell.has_formula()); TS_ASSERT(cell.has_formula());
TS_ASSERT_EQUALS(cell.get_formula(), "42"); TS_ASSERT_EQUALS(cell.get_formula(), "42");

View File

@ -37,7 +37,7 @@
namespace { namespace {
template<typename T> template<typename T>
std::size_t search_vector(const std::vector<T> &items, const T &to_find) std::size_t index(const std::vector<T> &items, const T &to_find)
{ {
auto match = std::find(items.begin(), items.end(), to_find); auto match = std::find(items.begin(), items.end(), to_find);
@ -49,6 +49,13 @@ std::size_t search_vector(const std::vector<T> &items, const T &to_find)
return std::distance(items.begin(), match); return std::distance(items.begin(), match);
} }
template<typename T>
bool contains(const std::vector<T> &items, const T &to_find)
{
auto match = std::find(items.begin(), items.end(), to_find);
return match != items.end();
}
} // namespace } // namespace
namespace xlnt { namespace xlnt {
@ -58,7 +65,10 @@ struct stylesheet
{ {
~stylesheet() {} ~stylesheet() {}
std::size_t index(const format &f) { return search_vector(formats, f); } std::size_t index(const format &f)
{
return ::index(formats, f);
}
std::size_t index(const std::string &style_name) std::size_t index(const std::string &style_name)
{ {
@ -73,11 +83,6 @@ struct stylesheet
return std::distance(styles.begin(), match); return std::distance(styles.begin(), match);
} }
std::size_t index(const border &b) { return search_vector(borders, b); }
std::size_t index(const fill &f) { return search_vector(fills, f); }
std::size_t index(const font &f) { return search_vector(fonts, f); }
std::size_t index(const number_format &f) { return search_vector(number_formats, f); }
std::size_t add_format(const format &f) std::size_t add_format(const format &f)
{ {
auto match = std::find(formats.begin(), formats.end(), f); auto match = std::find(formats.begin(), formats.end(), f);
@ -97,44 +102,25 @@ struct stylesheet
formats.push_back(f); formats.push_back(f);
format_styles.push_back("Normal"); format_styles.push_back("Normal");
try if (!contains(borders, f.get_border()))
{
search_vector(borders, f.get_border());
}
catch(std::out_of_range)
{ {
borders.push_back(f.get_border()); borders.push_back(f.get_border());
} }
try if (!contains(fills, f.get_fill()))
{
search_vector(fills, f.get_fill());
}
catch(std::out_of_range)
{ {
fills.push_back(f.get_fill()); fills.push_back(f.get_fill());
} }
try if (!contains(fonts, f.get_font()))
{
search_vector(fonts, f.get_font());
}
catch(std::out_of_range)
{ {
fonts.push_back(f.get_font()); fonts.push_back(f.get_font());
} }
if (f.get_number_format().get_id() >= 164) if (f.get_number_format().get_id() >= 164 && !contains(number_formats, f.get_number_format()))
{
try
{
search_vector(number_formats, f.get_number_format());
}
catch(std::out_of_range)
{ {
number_formats.push_back(f.get_number_format()); number_formats.push_back(f.get_number_format());
} }
}
return formats.size() - 1; return formats.size() - 1;
} }

View File

@ -22,8 +22,9 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <iterator>
#include <list> #include <list>
#include <string>
#include <unordered_map>
#include <vector> #include <vector>
#include <detail/stylesheet.hpp> #include <detail/stylesheet.hpp>
@ -92,7 +93,8 @@ struct workbook_impl
links_up_to_date_(other.links_up_to_date_), links_up_to_date_(other.links_up_to_date_),
shared_doc_(other.shared_doc_), shared_doc_(other.shared_doc_),
hyperlinks_changed_(other.hyperlinks_changed_), hyperlinks_changed_(other.hyperlinks_changed_),
app_version_(other.app_version_) app_version_(other.app_version_),
sheet_title_rel_id_map_(other.sheet_title_rel_id_map_)
{ {
} }
@ -131,6 +133,8 @@ struct workbook_impl
hyperlinks_changed_ = other.hyperlinks_changed_; hyperlinks_changed_ = other.hyperlinks_changed_;
app_version_ = other.app_version_; app_version_ = other.app_version_;
sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_;
return *this; return *this;
} }
@ -173,6 +177,8 @@ struct workbook_impl
bool shared_doc_; bool shared_doc_;
bool hyperlinks_changed_; bool hyperlinks_changed_;
std::string app_version_; std::string app_version_;
std::unordered_map<std::string, std::string> sheet_title_rel_id_map_;
}; };
} // namespace detail } // namespace detail

View File

@ -708,18 +708,21 @@ xlsx_consumer::xlsx_consumer(workbook &destination) : destination_(destination)
void xlsx_consumer::read(const path &source) void xlsx_consumer::read(const path &source)
{ {
destination_.clear();
source_.load(source); source_.load(source);
populate_workbook(); populate_workbook();
} }
void xlsx_consumer::read(std::istream &source) void xlsx_consumer::read(std::istream &source)
{ {
destination_.clear();
source_.load(source); source_.load(source);
populate_workbook(); populate_workbook();
} }
void xlsx_consumer::read(const std::vector<std::uint8_t> &source) void xlsx_consumer::read(const std::vector<std::uint8_t> &source)
{ {
destination_.clear();
source_.load(source); source_.load(source);
populate_workbook(); populate_workbook();
} }
@ -729,10 +732,8 @@ void xlsx_consumer::read(const std::vector<std::uint8_t> &source)
void xlsx_consumer::populate_workbook() void xlsx_consumer::populate_workbook()
{ {
auto &manifest = destination_.get_manifest(); auto &manifest = destination_.get_manifest();
manifest.clear();
destination_.d_->worksheets_.clear();
read_manifest(); read_manifest();
path workbook_part;
for (const auto &rel : manifest.get_package_relationships()) for (const auto &rel : manifest.get_package_relationships())
{ {
@ -751,13 +752,14 @@ void xlsx_consumer::populate_workbook()
read_custom_property(document.root()); read_custom_property(document.root());
break; break;
case relationship::type::office_document: case relationship::type::office_document:
check_document_type(manifest.get_part_content_type_string(rel.get_target_uri())); check_document_type(manifest.get_content_type_string(rel.get_target_uri()));
workbook_part = rel.get_target_uri();
read_workbook(document.root()); read_workbook(document.root());
break; break;
} }
} }
for (const auto &rel : manifest.get_part_relationships(constants::part_workbook())) for (const auto &rel : manifest.get_part_relationships(workbook_part))
{ {
pugi::xml_document document; pugi::xml_document document;
document.load_string(source_.read(path(rel.get_target_uri())).c_str()); document.load_string(source_.read(path(rel.get_target_uri())).c_str());
@ -767,18 +769,12 @@ void xlsx_consumer::populate_workbook()
case relationship::type::calculation_chain: case relationship::type::calculation_chain:
read_calculation_chain(document.root()); read_calculation_chain(document.root());
break; break;
case relationship::type::chartsheet:
read_chartsheet(document.root(), rel.get_id());
break;
case relationship::type::connections: case relationship::type::connections:
read_connections(document.root()); read_connections(document.root());
break; break;
case relationship::type::custom_xml_mappings: case relationship::type::custom_xml_mappings:
read_custom_xml_mappings(document.root()); read_custom_xml_mappings(document.root());
break; break;
case relationship::type::dialogsheet:
read_dialogsheet(document.root(), rel.get_id());
break;
case relationship::type::external_workbook_references: case relationship::type::external_workbook_references:
read_external_workbook_references(document.root()); read_external_workbook_references(document.root());
break; break;
@ -795,7 +791,7 @@ void xlsx_consumer::populate_workbook()
read_shared_workbook_revision_headers(document.root()); read_shared_workbook_revision_headers(document.root());
break; break;
case relationship::type::styles: case relationship::type::styles:
read_styles(document.root()); read_stylesheet(document.root());
break; break;
case relationship::type::theme: case relationship::type::theme:
read_theme(document.root()); read_theme(document.root());
@ -803,6 +799,22 @@ void xlsx_consumer::populate_workbook()
case relationship::type::volatile_dependencies: case relationship::type::volatile_dependencies:
read_volatile_dependencies(document.root()); read_volatile_dependencies(document.root());
break; break;
}
}
for (const auto &rel : manifest.get_part_relationships(workbook_part))
{
pugi::xml_document document;
document.load_string(source_.read(path(rel.get_target_uri())).c_str());
switch (rel.get_type())
{
case relationship::type::chartsheet:
read_chartsheet(document.root(), rel.get_id());
break;
case relationship::type::dialogsheet:
read_dialogsheet(document.root(), rel.get_id());
break;
case relationship::type::worksheet: case relationship::type::worksheet:
read_worksheet(document.root(), rel.get_id()); read_worksheet(document.root(), rel.get_id());
break; break;
@ -818,38 +830,15 @@ void xlsx_consumer::populate_workbook()
void read_unknown_parts(); void read_unknown_parts();
void read_unknown_relationships(); void read_unknown_relationships();
auto current_iter = destination_.d_->worksheets_.begin();
for (std::size_t current_index = 0; current_index < destination_.get_sheet_titles().size(); ++current_index)
{
auto current_id = current_iter->id_;
for (const auto j : worksheet_rel_title_id_index_map_)
{
if (std::get<1>(j.second) == current_id)
{
if (std::get<2>(j.second) == current_index) break;
auto switch_iter = destination_.d_->worksheets_.begin();
for (std::size_t k = 0; k < std::get<2>(j.second); ++k)
{
++switch_iter;
}
std::swap(*switch_iter, *current_iter);
}
}
++current_iter;
}
} }
// Package Parts // Package Parts
void xlsx_consumer::read_manifest() void xlsx_consumer::read_manifest()
{ {
auto package_rels = read_relationships(path("_rels/.rels"), source_); path package_rels_path("_rels/.rels");
if (!source_.has_file(package_rels_path)) throw invalid_file("missing package rels");
auto package_rels = read_relationships(package_rels_path, source_);
pugi::xml_document document; pugi::xml_document document;
document.load_string(source_.read(path("[Content_Types].xml")).c_str()); document.load_string(source_.read(path("[Content_Types].xml")).c_str());
@ -857,8 +846,6 @@ void xlsx_consumer::read_manifest()
const auto root_node = document.child("Types"); const auto root_node = document.child("Types");
auto &manifest = destination_.get_manifest(); auto &manifest = destination_.get_manifest();
std::unordered_map<path, std::string, std::hash<xlnt::hashable>> part_types;
auto make_relative = [](const path &p) auto make_relative = [](const path &p)
{ {
path copy; path copy;
@ -881,30 +868,32 @@ void xlsx_consumer::read_manifest()
else if (child.name() == std::string("Override")) else if (child.name() == std::string("Override"))
{ {
path part(child.attribute("PartName").value()); path part(child.attribute("PartName").value());
part_types[make_relative(part)] = child.attribute("ContentType").value(); manifest.register_override_type(part, child.attribute("ContentType").value());
} }
} }
for (const auto &package_rel : package_rels) for (const auto &package_rel : package_rels)
{ {
manifest.register_package_part(package_rel.get_target_uri(), manifest.register_package_relationship(package_rel.get_type(),
part_types.at(package_rel.get_target_uri()), package_rel.get_type()); package_rel.get_target_uri(),
package_rel.get_target_mode(),
package_rel.get_id());
} }
for (const auto &relationship_source : part_types) for (const auto &relationship_source : source_.infolist())
{ {
auto rels_name = relationship_source.first.parent() if (relationship_source.filename == path("_rels/.rels")
.append("_rels") || relationship_source.filename.extension() != ".rels") continue;
.append(relationship_source.first.basename() + ".rels");
auto part_rels = read_relationships(rels_name, source_);
if (part_rels.empty()) continue; auto part_rels = read_relationships(relationship_source.filename, source_);
auto source_uri = relationship_source.filename.parent().parent()
.append(relationship_source.filename.basename());
for (const auto part_rel : part_rels) for (const auto part_rel : part_rels)
{ {
auto part = relationship_source.first.parent().append(part_rel.get_target_uri()); auto part = source_uri;
relationship new_part_rel(part_rel.get_id(), part_rel.get_type(), part, target_mode::internal); manifest.register_part_relationship(source_uri, part_rel.get_type(),
manifest.register_part(relationship_source.first, new_part_rel, part_types.at(part)); part_rel.get_target_uri(), part_rel.get_target_mode(), part_rel.get_id());
} }
} }
} }
@ -965,14 +954,16 @@ void xlsx_consumer::read_workbook(const pugi::xml_node root)
} }
} }
std::size_t index = 0;
for (auto sheet_node : workbook_node.child("sheets").children("sheet")) for (auto sheet_node : workbook_node.child("sheets").children("sheet"))
{ {
std::string rel_id(sheet_node.attribute("r:id").value()); std::string rel_id(sheet_node.attribute("r:id").value());
std::string title(sheet_node.attribute("name").value()); std::string title(sheet_node.attribute("name").value());
auto id = string_to_size_t(sheet_node.attribute("sheetId").value()); auto id = string_to_size_t(sheet_node.attribute("sheetId").value());
worksheet_rel_title_id_index_map_[rel_id] = sheet_title_id_map_[rel_id] = id;
{ title, id, worksheet_rel_title_id_index_map_.size() }; sheet_title_index_map_[rel_id] = index++;
destination_.d_->sheet_title_rel_id_map_[title] = rel_id;
} }
} }
@ -1135,21 +1126,18 @@ void xlsx_consumer::read_shared_workbook_user_data(const pugi::xml_node root)
auto root_node = document.append_child("users"); auto root_node = document.append_child("users");
} }
void xlsx_consumer::read_styles(const pugi::xml_node root) void xlsx_consumer::read_stylesheet(const pugi::xml_node root)
{ {
pugi::xml_document document; auto stylesheet_node = root.child("styleSheet");
auto stylesheet_node = document.child("styleSheet");
auto &stylesheet = destination_.impl().stylesheet_; auto &stylesheet = destination_.impl().stylesheet_;
::read_borders(stylesheet_node.child("borders"), stylesheet.borders); read_borders(stylesheet_node.child("borders"), stylesheet.borders);
::read_fills(stylesheet_node.child("fills"), stylesheet.fills); read_fills(stylesheet_node.child("fills"), stylesheet.fills);
::read_fonts(stylesheet_node.child("fonts"), stylesheet.fonts); read_fonts(stylesheet_node.child("fonts"), stylesheet.fonts);
::read_number_formats(stylesheet_node.child("numFmts"), stylesheet.number_formats); read_number_formats(stylesheet_node.child("numFmts"), stylesheet.number_formats);
::read_colors(stylesheet_node.child("colors"), stylesheet.colors); read_colors(stylesheet_node.child("colors"), stylesheet.colors);
::read_styles(stylesheet_node.child("cellStyles"), stylesheet_node.child("cellStyleXfs"), stylesheet, stylesheet.styles, stylesheet.style_name_map); read_styles(stylesheet_node.child("cellStyles"), stylesheet_node.child("cellStyleXfs"), stylesheet, stylesheet.styles, stylesheet.style_name_map);
::read_formats(stylesheet_node.child("cellXfs"), stylesheet, stylesheet.formats, stylesheet.format_styles); read_formats(stylesheet_node.child("cellXfs"), stylesheet, stylesheet.formats, stylesheet.format_styles);
} }
void xlsx_consumer::read_theme(const pugi::xml_node root) void xlsx_consumer::read_theme(const pugi::xml_node root)
@ -1167,10 +1155,24 @@ void xlsx_consumer::read_volatile_dependencies(const pugi::xml_node root)
void xlsx_consumer::read_worksheet(const pugi::xml_node root, const std::string &rel_id) void xlsx_consumer::read_worksheet(const pugi::xml_node root, const std::string &rel_id)
{ {
auto title = std::get<0>(worksheet_rel_title_id_index_map_[rel_id]); auto title = std::find_if(destination_.d_->sheet_title_rel_id_map_.begin(),
auto id = std::get<1>(worksheet_rel_title_id_index_map_[rel_id]); destination_.d_->sheet_title_rel_id_map_.end(),
[&](const std::pair<std::string, std::string> &p)
{
return p.second == rel_id;
})->first;
destination_.d_->worksheets_.emplace_back(&destination_, id, title); auto id = sheet_title_id_map_[title];
auto index = sheet_title_index_map_[title];
auto insertion_iter = destination_.d_->worksheets_.begin();
while (insertion_iter != destination_.d_->worksheets_.end()
&& sheet_title_index_map_[insertion_iter->title_] < index)
{
++insertion_iter;
}
destination_.d_->worksheets_.emplace(insertion_iter, &destination_, id, title);
auto ws = destination_.get_sheet_by_id(id); auto ws = destination_.get_sheet_by_id(id);

View File

@ -86,7 +86,7 @@ private:
void read_shared_workbook_revision_headers(const pugi::xml_node root); void read_shared_workbook_revision_headers(const pugi::xml_node root);
void read_shared_workbook(const pugi::xml_node root); void read_shared_workbook(const pugi::xml_node root);
void read_shared_workbook_user_data(const pugi::xml_node root); void read_shared_workbook_user_data(const pugi::xml_node root);
void read_styles(const pugi::xml_node root); void read_stylesheet(const pugi::xml_node root);
void read_theme(const pugi::xml_node root); void read_theme(const pugi::xml_node root);
void read_volatile_dependencies(const pugi::xml_node root); void read_volatile_dependencies(const pugi::xml_node root);
@ -110,11 +110,8 @@ private:
/// </summary> /// </summary>
zip_file source_; zip_file source_;
/// <summary> std::unordered_map<std::string, std::size_t> sheet_title_id_map_;
/// Worksheet titles from workbook.xml are saved here so we know what its std::unordered_map<std::string, std::size_t> sheet_title_index_map_;
/// title is when readind the associated worksheet XML.
/// </summary>
std::unordered_map<std::string, std::tuple<std::string, std::size_t, std::size_t>> worksheet_rel_title_id_index_map_;
/// <summary> /// <summary>
/// A reference to the workbook which is being read. /// A reference to the workbook which is being read.

View File

@ -180,14 +180,14 @@ std::string to_string(xlnt::horizontal_alignment horizontal_alignment)
} }
} }
void write_relationships(const std::vector<xlnt::relationship> &relationships, pugi::xml_document &document) void write_relationships(const std::vector<xlnt::relationship> &relationships, pugi::xml_node root)
{ {
auto root_node = document.append_child("Relationships"); auto relationships_node = root.append_child("Relationships");
root_node.append_attribute("xmlns").set_value(xlnt::constants::get_namespace("relationships").c_str()); relationships_node.append_attribute("xmlns").set_value(xlnt::constants::get_namespace("relationships").c_str());
for (const auto &relationship : relationships) for (const auto &relationship : relationships)
{ {
auto relationship_node = root_node.append_child("Relationship"); auto relationship_node = relationships_node.append_child("Relationship");
relationship_node.append_attribute("Id").set_value(relationship.get_id().c_str()); relationship_node.append_attribute("Id").set_value(relationship.get_id().c_str());
relationship_node.append_attribute("Type").set_value(to_string(relationship.get_type()).c_str()); relationship_node.append_attribute("Type").set_value(to_string(relationship.get_type()).c_str());
@ -565,104 +565,113 @@ xlsx_producer::xlsx_producer(const workbook &target) : source_(target)
void xlsx_producer::write(const path &destination) void xlsx_producer::write(const path &destination)
{ {
xlnt::zip_file archive; populate_archive();
populate_archive(destination_); destination_.save(destination);
archive.save(destination);
} }
void xlsx_producer::write(std::ostream &destination) void xlsx_producer::write(std::ostream &destination)
{ {
xlnt::zip_file archive; populate_archive();
populate_archive(destination_); destination_.save(destination);
archive.save(destination);
} }
void xlsx_producer::write(std::vector<std::uint8_t> &destination) void xlsx_producer::write(std::vector<std::uint8_t> &destination)
{ {
xlnt::zip_file archive; populate_archive();
populate_archive(destination_); destination_.save(destination);
archive.save(destination);
} }
// Part Writing Methods // Part Writing Methods
void xlsx_producer::populate_archive(zip_file &destination_) void xlsx_producer::populate_archive()
{ {
write_package_relationships(); write_manifest();
write_content_types(); path workbook_part;
write_workbook();
write_workbook_relationships();
for (auto &rel : source_.impl().manifest_.get_package_relationships()) for (auto &rel : source_.impl().manifest_.get_package_relationships())
{ {
pugi::xml_document document;
switch (rel.get_type()) switch (rel.get_type())
{ {
case relationship::type::core_properties: case relationship::type::core_properties:
write_core_properties(); write_core_properties(document.root());
break; break;
case relationship::type::extended_properties: case relationship::type::extended_properties:
write_app_properties(); write_extended_properties(document.root());
break; break;
case relationship::type::custom_properties: case relationship::type::custom_properties:
write_custom_property(); write_custom_properties(document.root());
break; break;
} case relationship::type::office_document:
write_workbook(document.root());
workbook_part = rel.get_target_uri();
break;
case relationship::type::thumbnail:
destination_.write_string(
std::string(source_.get_thumbnail().begin(), source_.get_thumbnail().end()),
rel.get_target_uri());
continue;
} }
for (auto rel : source_.impl().manifest_.get_part_relationships(constants::part_workbook())) write_document_to_archive(document, rel.get_target_uri(), destination_);
}
for (auto rel : source_.impl().manifest_.get_part_relationships(workbook_part))
{ {
pugi::xml_document document;
switch (rel.get_type()) switch (rel.get_type())
{ {
case relationship::type::calculation_chain: case relationship::type::calculation_chain:
write_calculation_chain(); write_calculation_chain(document.root());
break; break;
case relationship::type::chartsheet: case relationship::type::chartsheet:
write_chartsheet(rel); write_chartsheet(document.root(), rel);
break; break;
case relationship::type::connections: case relationship::type::connections:
write_connections(); write_connections(document.root());
break; break;
case relationship::type::custom_xml_mappings: case relationship::type::custom_xml_mappings:
write_custom_xml_mappings(); write_custom_xml_mappings(document.root());
break; break;
case relationship::type::dialogsheet: case relationship::type::dialogsheet:
write_dialogsheet(rel); write_dialogsheet(document.root(), rel);
break; break;
case relationship::type::external_workbook_references: case relationship::type::external_workbook_references:
write_external_workbook_references(); write_external_workbook_references(document.root());
break; break;
case relationship::type::metadata: case relationship::type::metadata:
write_metadata(); write_metadata(document.root());
break; break;
case relationship::type::pivot_table: case relationship::type::pivot_table:
write_pivot_table(); write_pivot_table(document.root());
break; break;
case relationship::type::shared_string_table: case relationship::type::shared_string_table:
write_shared_string_table(); write_shared_string_table(document.root());
break; break;
case relationship::type::shared_workbook_revision_headers: case relationship::type::shared_workbook_revision_headers:
write_shared_workbook_revision_headers(); write_shared_workbook_revision_headers(document.root());
break; break;
case relationship::type::styles: case relationship::type::styles:
write_styles(); write_styles(document.root());
break; break;
case relationship::type::theme: case relationship::type::theme:
write_theme(); write_theme(document.root());
break; break;
case relationship::type::volatile_dependencies: case relationship::type::volatile_dependencies:
write_volatile_dependencies(); write_volatile_dependencies(document.root());
break; break;
case relationship::type::worksheet: case relationship::type::worksheet:
write_worksheet(rel); write_worksheet(document.root(), rel);
break; break;
} }
std::ostringstream document_stream;
document.save(document_stream);
destination_.write_string(document_stream.str(), rel.get_target_uri());
} }
// Sheet Relationship Target Parts
void write_comments();
void write_drawings();
// Unknown Parts // Unknown Parts
void write_unknown_parts(); void write_unknown_parts();
@ -671,30 +680,11 @@ void xlsx_producer::populate_archive(zip_file &destination_)
// Package Parts // Package Parts
void xlsx_producer::write_package_relationships() void xlsx_producer::write_manifest()
{ {
pugi::xml_document document; pugi::xml_document content_types_document;
auto relationships_node = document.append_child("Relationships"); auto types_node = content_types_document.append_child("Types");
relationships_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/relationships");
for (const auto &relationship : source_.impl().manifest_.get_package_relationships())
{
auto relationship_node = relationships_node.append_child("Relationship");
relationship_node.append_attribute("Id").set_value(relationship.get_id().c_str());
relationship_node.append_attribute("Type").set_value(to_string(relationship.get_type()).c_str());
relationship_node.append_attribute("Target").set_value(relationship.get_target_uri().to_string('/').c_str());
}
write_document_to_archive(document, xlnt::constants::part_root_relationships(), destination_);
}
void xlsx_producer::write_content_types()
{
pugi::xml_document document;
auto types_node = document.append_child("Types");
types_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/content-types"); types_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/content-types");
for (const auto &extension : source_.get_manifest().get_default_type_extensions()) for (const auto &extension : source_.get_manifest().get_default_type_extensions())
@ -705,44 +695,57 @@ void xlsx_producer::write_content_types()
default_node.append_attribute("ContentType").set_value(content_type.c_str()); default_node.append_attribute("ContentType").set_value(content_type.c_str());
} }
for (const auto &part : source_.get_manifest().get_parts()) for (const auto &part : source_.get_manifest().get_overriden_parts())
{ {
auto override_node = types_node.append_child("Override"); auto override_node = types_node.append_child("Override");
override_node.append_attribute("PartName").set_value(part.to_string('/').c_str()); override_node.append_attribute("PartName").set_value(("/" + part.to_string('/')).c_str());
auto content_type = source_.get_manifest().get_part_content_type_string(part); auto content_type = source_.get_manifest().get_content_type_string(part);
override_node.append_attribute("ContentType").set_value(content_type.c_str()); override_node.append_attribute("ContentType").set_value(content_type.c_str());
} }
write_document_to_archive(document, xlnt::constants::part_content_types(), destination_); for (const auto &part : source_.get_manifest().get_parts_with_relationships())
{
auto part_rels = source_.get_manifest().get_part_relationships(part);
pugi::xml_document part_rels_document;
write_relationships(part_rels, part_rels_document.root());
path rels_path = part.parent().append("_rels").append(part.basename() + ".rels");
write_document_to_archive(part_rels_document, rels_path, destination_);
} }
void xlsx_producer::write_app_properties() write_document_to_archive(content_types_document, path("[Content_Types].xml"), destination_);
pugi::xml_document package_rels_document;
write_relationships(source_.get_manifest().get_package_relationships(), package_rels_document);
write_document_to_archive(package_rels_document, path("_rels/.rels"), destination_);
}
void xlsx_producer::write_extended_properties(pugi::xml_node root)
{ {
pugi::xml_document document; auto properties_node = root.append_child("Properties");
auto root_node = document.append_child("Properties");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); properties_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties");
root_node.append_attribute("xmlns:vt").set_value("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); properties_node.append_attribute("xmlns:vt").set_value("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
root_node.append_child("Application").text().set(source_.get_application().c_str()); properties_node.append_child("Application").text().set(source_.get_application().c_str());
root_node.append_child("DocSecurity").text().set(std::to_string(source_.get_doc_security()).c_str()); properties_node.append_child("DocSecurity").text().set(std::to_string(source_.get_doc_security()).c_str());
root_node.append_child("ScaleCrop").text().set(source_.get_scale_crop() ? "true" : "false"); properties_node.append_child("ScaleCrop").text().set(source_.get_scale_crop() ? "true" : "false");
auto company_node = root_node.append_child("Company"); auto company_node = properties_node.append_child("Company");
if (!source_.get_company().empty()) if (!source_.get_company().empty())
{ {
company_node.text().set(source_.get_company().c_str()); company_node.text().set(source_.get_company().c_str());
} }
root_node.append_child("LinksUpToDate").text().set(source_.links_up_to_date() ? "true" : "false"); properties_node.append_child("LinksUpToDate").text().set(source_.links_up_to_date() ? "true" : "false");
root_node.append_child("SharedDoc").text().set(source_.is_shared_doc() ? "true" : "false"); properties_node.append_child("SharedDoc").text().set(source_.is_shared_doc() ? "true" : "false");
root_node.append_child("HyperlinksChanged").text().set(source_.hyperlinks_changed() ? "true" : "false"); properties_node.append_child("HyperlinksChanged").text().set(source_.hyperlinks_changed() ? "true" : "false");
root_node.append_child("AppVersion").text().set(source_.get_app_version().c_str()); properties_node.append_child("AppVersion").text().set(source_.get_app_version().c_str());
// TODO what is this stuff? // TODO what is this stuff?
auto heading_pairs_node = root_node.append_child("HeadingPairs"); auto heading_pairs_node = properties_node.append_child("HeadingPairs");
auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector"); auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector");
heading_pairs_vector_node.append_attribute("size").set_value("2"); heading_pairs_vector_node.append_attribute("size").set_value("2");
heading_pairs_vector_node.append_attribute("baseType").set_value("variant"); heading_pairs_vector_node.append_attribute("baseType").set_value("variant");
@ -751,7 +754,7 @@ void xlsx_producer::write_app_properties()
.append_child("vt:i4") .append_child("vt:i4")
.text().set(std::to_string(source_.get_sheet_titles().size()).c_str()); .text().set(std::to_string(source_.get_sheet_titles().size()).c_str());
auto titles_of_parts_node = root_node.append_child("TitlesOfParts"); auto titles_of_parts_node = properties_node.append_child("TitlesOfParts");
auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector"); auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector");
titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(source_.get_sheet_titles().size()).c_str()); titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(source_.get_sheet_titles().size()).c_str());
titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr"); titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr");
@ -760,49 +763,40 @@ void xlsx_producer::write_app_properties()
{ {
titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str()); titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str());
} }
write_document_to_archive(document, xlnt::constants::part_app(), destination_);
} }
void xlsx_producer::write_core_properties() void xlsx_producer::write_core_properties(pugi::xml_node root)
{ {
pugi::xml_document document; auto core_properties_node = root.append_child("cp:coreProperties");
auto root_node = document.append_child("cp:coreProperties");
root_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); core_properties_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
root_node.append_attribute("xmlns:dc").set_value("http://purl.org/dc/elements/1.1/"); core_properties_node.append_attribute("xmlns:dc").set_value("http://purl.org/dc/elements/1.1/");
root_node.append_attribute("xmlns:dcmitype").set_value("http://purl.org/dc/dcmitype/"); core_properties_node.append_attribute("xmlns:dcmitype").set_value("http://purl.org/dc/dcmitype/");
root_node.append_attribute("xmlns:dcterms").set_value("http://purl.org/dc/terms/"); core_properties_node.append_attribute("xmlns:dcterms").set_value("http://purl.org/dc/terms/");
root_node.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance"); core_properties_node.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance");
root_node.append_child("dc:creator").text().set(source_.get_creator().c_str()); core_properties_node.append_child("dc:creator").text().set(source_.get_creator().c_str());
root_node.append_child("cp:lastModifiedBy").text().set(source_.get_last_modified_by().c_str()); core_properties_node.append_child("cp:lastModifiedBy").text().set(source_.get_last_modified_by().c_str());
root_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(source_.get_created()).c_str()); core_properties_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(source_.get_created()).c_str());
root_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); core_properties_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF");
root_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(source_.get_modified()).c_str()); core_properties_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(source_.get_modified()).c_str());
root_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); core_properties_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF");
root_node.append_child("dc:title").text().set(source_.get_title().c_str()); core_properties_node.append_child("dc:title").text().set(source_.get_title().c_str());
root_node.append_child("dc:description"); core_properties_node.append_child("dc:description");
root_node.append_child("dc:subject"); core_properties_node.append_child("dc:subject");
root_node.append_child("cp:keywords"); core_properties_node.append_child("cp:keywords");
root_node.append_child("cp:category"); core_properties_node.append_child("cp:category");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_custom_file_properties() void xlsx_producer::write_custom_properties(pugi::xml_node root)
{ {
pugi::xml_document document; auto properties_node = root.append_child("Properties");
auto root_node = document.append_child("customFileProperties");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
// Write SpreadsheetML-Specific Package Parts // Write SpreadsheetML-Specific Package Parts
void xlsx_producer::write_workbook() void xlsx_producer::write_workbook(pugi::xml_node root)
{ {
pugi::xml_document document;
std::size_t num_visible = 0; std::size_t num_visible = 0;
for (auto ws : source_) for (auto ws : source_)
@ -818,23 +812,23 @@ void xlsx_producer::write_workbook()
throw xlnt::no_visible_worksheets(); throw xlnt::no_visible_worksheets();
} }
auto root_node = document.append_child("workbook"); auto workbook_node = root.append_child("workbook");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); workbook_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); workbook_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto file_version_node = root_node.append_child("fileVersion"); auto file_version_node = workbook_node.append_child("fileVersion");
file_version_node.append_attribute("appName").set_value("xl"); file_version_node.append_attribute("appName").set_value("xl");
file_version_node.append_attribute("lastEdited").set_value("4"); file_version_node.append_attribute("lastEdited").set_value("4");
file_version_node.append_attribute("lowestEdited").set_value("4"); file_version_node.append_attribute("lowestEdited").set_value("4");
file_version_node.append_attribute("rupBuild").set_value("4505"); file_version_node.append_attribute("rupBuild").set_value("4505");
auto workbook_pr_node = root_node.append_child("workbookPr"); auto workbook_pr_node = workbook_node.append_child("workbookPr");
workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook"); workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook");
workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226"); workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226");
workbook_pr_node.append_attribute("date1904").set_value(source_.get_base_date() == calendar::mac_1904 ? "1" : "0"); workbook_pr_node.append_attribute("date1904").set_value(source_.get_base_date() == calendar::mac_1904 ? "1" : "0");
auto book_views_node = root_node.append_child("bookViews"); auto book_views_node = workbook_node.append_child("bookViews");
auto workbook_view_node = book_views_node.append_child("workbookView"); auto workbook_view_node = book_views_node.append_child("workbookView");
workbook_view_node.append_attribute("activeTab").set_value("0"); workbook_view_node.append_attribute("activeTab").set_value("0");
workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1"); workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1");
@ -846,17 +840,16 @@ void xlsx_producer::write_workbook()
workbook_view_node.append_attribute("tabRatio").set_value("600"); workbook_view_node.append_attribute("tabRatio").set_value("600");
workbook_view_node.append_attribute("visibility").set_value("visible"); workbook_view_node.append_attribute("visibility").set_value("visible");
auto sheets_node = root_node.append_child("sheets"); auto sheets_node = workbook_node.append_child("sheets");
auto defined_names_node = root_node.append_child("definedNames"); auto defined_names_node = workbook_node.append_child("definedNames");
std::size_t index = 1; std::size_t index = 1;
auto wb_rel = source_.d_->manifest_.get_package_relationship(xlnt::relationship::type::office_document);
for (const auto ws : source_) for (const auto ws : source_)
{ {
path target("worksheets/sheet" + std::to_string(index++) + ".xml"); auto sheet_rel_id = source_.d_->sheet_title_rel_id_map_[ws.get_title()];
auto sheet_rel = source_.d_->manifest_.get_part_relationship(wb_rel.get_target_uri(), sheet_rel_id);
for (const auto &rel : source_.impl().manifest_.get_part_relationships(constants::part_workbook()))
{
if (rel.get_target_uri().to_string('/') != target.to_string('/')) continue;
auto sheet_node = sheets_node.append_child("sheet"); auto sheet_node = sheets_node.append_child("sheet");
sheet_node.append_attribute("name").set_value(ws.get_title().c_str()); sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
@ -867,7 +860,7 @@ void xlsx_producer::write_workbook()
sheet_node.append_attribute("state").set_value("hidden"); sheet_node.append_attribute("state").set_value("hidden");
} }
sheet_node.append_attribute("r:id").set_value(rel.get_id().c_str()); sheet_node.append_attribute("r:id").set_value(sheet_rel_id.c_str());
if (ws.has_auto_filter()) if (ws.has_auto_filter())
{ {
@ -879,12 +872,9 @@ void xlsx_producer::write_workbook()
"'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string(); "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string();
defined_name_node.text().set(name.c_str()); defined_name_node.text().set(name.c_str());
} }
break;
}
} }
auto calc_pr_node = root_node.append_child("calcPr"); auto calc_pr_node = workbook_node.append_child("calcPr");
calc_pr_node.append_attribute("calcId").set_value("124519"); calc_pr_node.append_attribute("calcId").set_value("124519");
calc_pr_node.append_attribute("calcMode").set_value("auto"); calc_pr_node.append_attribute("calcMode").set_value("auto");
calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1"); calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1");
@ -903,110 +893,68 @@ void xlsx_producer::write_workbook()
defined_name_node.text().set(target_string.c_str()); defined_name_node.text().set(target_string.c_str());
} }
*/ */
write_document_to_archive(document, xlnt::path("workbook.xml"), destination_);
}
void xlsx_producer::write_workbook_relationships()
{
pugi::xml_document document;
auto relationships_node = document.append_child("Relationships");
relationships_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/relationships");
auto relationship_node = relationships_node.append_child("Relationship");
relationship_node.append_attribute("Id").set_value("rId1");
relationship_node.append_attribute("Type").set_value("http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet");
relationship_node.append_attribute("Target").set_value("sheet1.xml");
write_document_to_archive(document, xlnt::path("_rels/workbook.xml.rels"), destination_);
} }
// Write Workbook Relationship Target Parts // Write Workbook Relationship Target Parts
void xlsx_producer::write_calculation_chain() void xlsx_producer::write_calculation_chain(pugi::xml_node root)
{ {
pugi::xml_document document; auto calc_chain_node = root.append_child("calcChain");
auto root_node = document.append_child("calcChain");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_chartsheet(const relationship &rel) void xlsx_producer::write_chartsheet(pugi::xml_node root, const relationship &rel)
{ {
pugi::xml_document document; auto chartsheet_node = root.append_child("chartsheet");
auto root_node = document.append_child("chartsheet");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_connections() void xlsx_producer::write_connections(pugi::xml_node root)
{ {
pugi::xml_document document; auto connections_node = root.append_child("connections");
auto root_node = document.append_child("connections");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_custom_property() void xlsx_producer::write_custom_xml_mappings(pugi::xml_node root)
{ {
pugi::xml_document document; auto map_info_node = root.append_child("MapInfo");
auto root_node = document.append_child("customProperty");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_custom_xml_mappings() void xlsx_producer::write_dialogsheet(pugi::xml_node root, const relationship &rel)
{ {
pugi::xml_document document; auto dialogsheet_node = root.append_child("dialogsheet");
auto root_node = document.append_child("connections");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_dialogsheet(const relationship &rel) void xlsx_producer::write_external_workbook_references(pugi::xml_node root)
{ {
pugi::xml_document document; auto external_link_node = root.append_child("externalLink");
auto root_node = document.append_child("dialogsheet");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_external_workbook_references() void xlsx_producer::write_metadata(pugi::xml_node root)
{ {
pugi::xml_document document; auto metadata_node = root.append_child("metadata");
auto root_node = document.append_child("externalLink");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_metadata() void xlsx_producer::write_pivot_table(pugi::xml_node root)
{ {
pugi::xml_document document; auto pivot_table_definition_node = root.append_child("pivotTableDefinition");
auto root_node = document.append_child("metadata");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_pivot_table() void xlsx_producer::write_shared_string_table(pugi::xml_node root)
{ {
pugi::xml_document document; auto sst_node = root.append_child("sst");
auto root_node = document.append_child("pivotTableDefinition");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
}
void xlsx_producer::write_shared_string_table() sst_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
{
pugi::xml_document document;
auto root_node = document.append_child("sst"); sst_node.append_attribute("count").set_value(std::to_string(source_.get_shared_strings().size()).c_str());
sst_node.append_attribute("uniqueCount").set_value(std::to_string(source_.get_shared_strings().size()).c_str());
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.append_attribute("count").set_value(std::to_string(source_.get_shared_strings().size()).c_str());
root_node.append_attribute("uniqueCount").set_value(std::to_string(source_.get_shared_strings().size()).c_str());
for (const auto &string : source_.get_shared_strings()) for (const auto &string : source_.get_shared_strings())
{ {
if (string.get_runs().size() == 1 && !string.get_runs().at(0).has_formatting()) if (string.get_runs().size() == 1 && !string.get_runs().at(0).has_formatting())
{ {
root_node.append_child("si").append_child("t").text().set(string.get_plain_string().c_str()); sst_node.append_child("si").append_child("t").text().set(string.get_plain_string().c_str());
} }
else else
{ {
auto string_item_node = root_node.append_child("si"); auto string_item_node = sst_node.append_child("si");
for (const auto &run : string.get_runs()) for (const auto &run : string.get_runs())
{ {
@ -1047,95 +995,81 @@ void xlsx_producer::write_shared_string_table()
} }
} }
} }
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_shared_workbook_revision_headers() void xlsx_producer::write_shared_workbook_revision_headers(pugi::xml_node root)
{ {
pugi::xml_document document; auto headers_node = root.append_child("headers");
auto root_node = document.append_child("headers");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_shared_workbook() void xlsx_producer::write_shared_workbook(pugi::xml_node root)
{ {
pugi::xml_document document; auto revisions_node = root.append_child("revisions");
auto root_node = document.append_child("revisions");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_shared_workbook_user_data() void xlsx_producer::write_shared_workbook_user_data(pugi::xml_node root)
{ {
pugi::xml_document document; auto users_node = root.append_child("users");
auto root_node = document.append_child("users");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_styles() void xlsx_producer::write_styles(pugi::xml_node root)
{ {
pugi::xml_document document; auto stylesheet_node = root.append_child("styleSheet");
stylesheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
auto root_node = document.append_child("styleSheet"); stylesheet_node.append_attribute("xmlns:mc").set_value("http://schemas.openxmlformats.org/markup-compatibility/2006");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); stylesheet_node.append_attribute("mc:Ignorable").set_value("x14ac");
root_node.append_attribute("xmlns:mc").set_value("http://schemas.openxmlformats.org/markup-compatibility/2006"); stylesheet_node.append_attribute("xmlns:x14ac").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
root_node.append_attribute("mc:Ignorable").set_value("x14ac");
root_node.append_attribute("xmlns:x14ac").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
const auto &stylesheet = source_.impl().stylesheet_; const auto &stylesheet = source_.impl().stylesheet_;
if (!stylesheet.number_formats.empty()) if (!stylesheet.number_formats.empty())
{ {
auto number_formats_node = root_node.append_child("numFmts"); auto number_formats_node = stylesheet_node.append_child("numFmts");
write_number_formats(stylesheet.number_formats, number_formats_node); write_number_formats(stylesheet.number_formats, number_formats_node);
} }
if (!stylesheet.fonts.empty()) if (!stylesheet.fonts.empty())
{ {
auto fonts_node = root_node.append_child("fonts"); auto fonts_node = stylesheet_node.append_child("fonts");
write_fonts(stylesheet.fonts, fonts_node); write_fonts(stylesheet.fonts, fonts_node);
} }
if (!stylesheet.fills.empty()) if (!stylesheet.fills.empty())
{ {
auto fills_node = root_node.append_child("fills"); auto fills_node = stylesheet_node.append_child("fills");
write_fills(stylesheet.fills, fills_node); write_fills(stylesheet.fills, fills_node);
} }
if (!stylesheet.borders.empty()) if (!stylesheet.borders.empty())
{ {
auto borders_node = root_node.append_child("borders"); auto borders_node = stylesheet_node.append_child("borders");
write_borders(stylesheet.borders, borders_node); write_borders(stylesheet.borders, borders_node);
} }
auto cell_style_xfs_node = root_node.append_child("cellStyleXfs"); auto cell_style_xfs_node = stylesheet_node.append_child("cellStyleXfs");
auto cell_xfs_node = root_node.append_child("cellXfs"); auto cell_xfs_node = stylesheet_node.append_child("cellXfs");
write_formats(stylesheet, cell_xfs_node); write_formats(stylesheet, cell_xfs_node);
auto cell_styles_node = root_node.append_child("cellStyles"); auto cell_styles_node = stylesheet_node.append_child("cellStyles");
::write_styles(stylesheet, cell_styles_node, cell_style_xfs_node); ::write_styles(stylesheet, cell_styles_node, cell_style_xfs_node);
auto dxfs_node = root_node.append_child("dxfs"); auto dxfs_node = stylesheet_node.append_child("dxfs");
write_dxfs(dxfs_node); write_dxfs(dxfs_node);
auto table_styles_node = root_node.append_child("tableStyles"); auto table_styles_node = stylesheet_node.append_child("tableStyles");
write_table_styles(table_styles_node); write_table_styles(table_styles_node);
if (!stylesheet.colors.empty()) if (!stylesheet.colors.empty())
{ {
auto colors_node = root_node.append_child("colors"); auto colors_node = stylesheet_node.append_child("colors");
write_colors(stylesheet.colors, colors_node); write_colors(stylesheet.colors, colors_node);
} }
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_theme() void xlsx_producer::write_theme(pugi::xml_node root)
{ {
pugi::xml_document document; auto theme_node = root.append_child("a:theme");
auto theme_node = document.append_child("a:theme");
theme_node.append_attribute("xmlns:a").set_value(constants::get_namespace("drawingml").c_str()); theme_node.append_attribute("xmlns:a").set_value(constants::get_namespace("drawingml").c_str());
theme_node.append_attribute("name").set_value("Office Theme"); theme_node.append_attribute("name").set_value("Office Theme");
@ -1467,191 +1401,16 @@ void xlsx_producer::write_theme()
theme_node.append_child("a:objectDefaults"); theme_node.append_child("a:objectDefaults");
theme_node.append_child("a:extraClrSchemeLst"); theme_node.append_child("a:extraClrSchemeLst");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_volatile_dependencies() void xlsx_producer::write_volatile_dependencies(pugi::xml_node root)
{ {
pugi::xml_document document; auto vol_types_node = root.append_child("volTypes");
auto root_node = document.append_child("volTypes");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
/* void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel)
bool xlsx_producer::read_worksheet(const relationship &rel)
{ {
const auto ws = source_.get_active_sheet(); auto worksheet_node = root.append_child("worksheet");
auto root_node = xml.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension(dimension_node.attribute("ref").value());
auto full_range = xlnt::range_reference(dimension);
auto sheet_data_node = root_node.child("sheetData");
if (root_node.child("mergeCells"))
{
auto merge_cells_node = root_node.child("mergeCells");
auto count = std::stoull(merge_cells_node.attribute("count").value());
for (auto merge_cell_node : merge_cells_node.children("mergeCell"))
{
sheet_.merge_cells(range_reference(merge_cell_node.attribute("ref").value()));
count--;
}
if (count != 0)
{
throw std::runtime_error("mismatch between count and actual number of merged cells");
}
}
auto &shared_strings = sheet_.get_workbook().get_shared_strings();
for (auto row_node : sheet_data_node.children("row"))
{
auto row_index = static_cast<row_t>(std::stoull(row_node.attribute("r").value()));
if (row_node.attribute("ht"))
{
sheet_.get_row_properties(row_index).height = std::stold(row_node.attribute("ht").value());
}
std::string span_string = row_node.attribute("spans").value();
auto colon_index = span_string.find(':');
column_t min_column = 0;
column_t max_column = 0;
if (colon_index != std::string::npos)
{
min_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(0, colon_index)));
max_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(colon_index + 1)));
}
else
{
min_column = full_range.get_top_left().get_column_index();
max_column = full_range.get_bottom_right().get_column_index();
}
for (column_t i = min_column; i <= max_column; i++)
{
std::string address = i.column_string() + std::to_string(row_index);
pugi::xml_node cell_node;
bool cell_found = false;
for (auto &check_cell_node : row_node.children("c"))
{
if (check_cell_node.attribute("r") && check_cell_node.attribute("r").value() == address)
{
cell_node = check_cell_node;
cell_found = true;
break;
}
}
if (cell_found)
{
bool has_value = cell_node.child("v") != nullptr;
std::string value_string = has_value ? cell_node.child("v").text().get() : "";
bool has_type = cell_node.attribute("t") != nullptr;
std::string type = has_type ? cell_node.attribute("t").value() : "";
bool has_format = cell_node.attribute("s") != nullptr;
auto format_id = static_cast<std::size_t>(has_format ? std::stoull(cell_node.attribute("s").value()) : 0LL);
bool has_formula = cell_node.child("f") != nullptr;
bool has_shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr
&& cell_node.child("f").attribute("t").value() == std::string("shared");
auto cell = sheet_.get_cell(address);
if (has_formula && !has_shared_formula && !sheet_.get_workbook().get_data_only())
{
std::string formula = cell_node.child("f").text().get();
cell.set_formula(formula);
}
if (has_type && type == "inlineStr") // inline string
{
std::string inline_string = cell_node.child("is").child("t").text().get();
cell.set_value(inline_string);
}
else if (has_type && type == "s" && !has_formula) // shared string
{
auto shared_string_index = static_cast<std::size_t>(std::stoull(value_string));
auto shared_string = shared_strings.at(shared_string_index);
cell.set_value(shared_string);
}
else if (has_type && type == "b") // boolean
{
cell.set_value(value_string != "0");
}
else if (has_type && type == "str")
{
cell.set_value(value_string);
}
else if (has_value && !value_string.empty())
{
if (!value_string.empty() && value_string[0] == '#')
{
cell.set_error(value_string);
}
else
{
cell.set_value(std::stold(value_string));
}
}
if (has_format)
{
cell.set_format(stylesheet.formats.at(format_id));
}
}
}
}
auto cols_node = root_node.child("cols");
for (auto col_node : cols_node.children("col"))
{
auto min = static_cast<column_t::index_t>(std::stoull(col_node.attribute("min").value()));
auto max = static_cast<column_t::index_t>(std::stoull(col_node.attribute("max").value()));
auto width = std::stold(col_node.attribute("width").value());
bool custom = col_node.attribute("customWidth").value() == std::string("1");
auto column_style = static_cast<std::size_t>(col_node.attribute("style") ? std::stoull(col_node.attribute("style").value()) : 0);
for (auto column = min; column <= max; column++)
{
if (!sheet_.has_column_properties(column))
{
sheet_.add_column_properties(column, column_properties());
}
sheet_.get_column_properties(min).width = width;
sheet_.get_column_properties(min).style = column_style;
sheet_.get_column_properties(min).custom = custom;
}
}
if (root_node.child("autoFilter"))
{
auto auto_filter_node = root_node.child("autoFilter");
xlnt::range_reference ref(auto_filter_node.attribute("ref").value());
sheet_.auto_filter(ref);
}
return true;
}
*/
void xlsx_producer::write_worksheet(const relationship &rel)
{
pugi::xml_document document;
auto worksheet_node = document.append_child("worksheet");
worksheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); worksheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
worksheet_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/package/2006/relationships"); worksheet_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/package/2006/relationships");
@ -1661,7 +1420,14 @@ void xlsx_producer::write_worksheet(const relationship &rel)
outline_pr_node.append_attribute("summaryBelow").set_value("1"); outline_pr_node.append_attribute("summaryBelow").set_value("1");
outline_pr_node.append_attribute("summaryRight").set_value("1"); outline_pr_node.append_attribute("summaryRight").set_value("1");
auto ws = source_.get_sheet_by_index(0); auto title = std::find_if(source_.d_->sheet_title_rel_id_map_.begin(),
source_.d_->sheet_title_rel_id_map_.end(),
[&](const std::pair<std::string, std::string> &p)
{
return p.second == rel.get_id();
})->first;
auto ws = source_.get_sheet_by_title(title);
if (!ws.get_page_setup().is_default()) if (!ws.get_page_setup().is_default())
{ {
@ -1821,11 +1587,6 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{ {
if (!cell.garbage_collectible()) if (!cell.garbage_collectible())
{ {
if (cell.has_hyperlink())
{
// hyperlink_references[cell.get_hyperlink().get_id()] = cell.get_reference().to_string();
}
auto cell_node = row_node.append_child("c"); auto cell_node = row_node.append_child("c");
cell_node.append_attribute("r").set_value(cell.get_reference().to_string().c_str()); cell_node.append_attribute("r").set_value(cell.get_reference().to_string().c_str());
@ -1942,15 +1703,29 @@ void xlsx_producer::write_worksheet(const relationship &rel)
} }
if (!source_.impl().manifest_.get_part_relationships(rel.get_target_uri()).empty()) if (!source_.impl().manifest_.get_part_relationships(rel.get_target_uri()).empty())
{
auto sheet_rels = source_.impl().manifest_.get_part_relationships(rel.get_target_uri());
std::vector<relationship> hyperlink_sheet_rels;
for (const auto &sheet_rel : sheet_rels)
{
if (sheet_rel.get_type() == relationship::type::hyperlink)
{
hyperlink_sheet_rels.push_back(sheet_rel);
}
}
if (!hyperlink_sheet_rels.empty())
{ {
auto hyperlinks_node = worksheet_node.append_child("hyperlinks"); auto hyperlinks_node = worksheet_node.append_child("hyperlinks");
for (const auto &relationship : source_.impl().manifest_.get_part_relationships(rel.get_target_uri())) for (const auto &hyperlink_rel : hyperlink_sheet_rels)
{ {
auto hyperlink_node = hyperlinks_node.append_child("hyperlink"); auto hyperlink_node = hyperlinks_node.append_child("hyperlink");
hyperlink_node.append_attribute("display").set_value(relationship.get_target_uri().to_string('/').c_str()); hyperlink_node.append_attribute("display").set_value(hyperlink_rel.get_target_uri().to_string('/').c_str());
hyperlink_node.append_attribute("ref").set_value(hyperlink_references.at(relationship.get_id()).c_str()); hyperlink_node.append_attribute("ref").set_value(hyperlink_references.at(hyperlink_rel.get_id()).c_str());
hyperlink_node.append_attribute("r:id").set_value(relationship.get_id().c_str()); hyperlink_node.append_attribute("r:id").set_value(hyperlink_rel.get_id().c_str());
}
} }
} }
@ -2022,40 +1797,32 @@ void xlsx_producer::write_worksheet(const relationship &rel)
"Text &P of &N"; "Text &P of &N";
odd_footer_node.text().set(footer_text.c_str()); odd_footer_node.text().set(footer_text.c_str());
} }
write_document_to_archive(document, rel.get_target_uri(), destination_);
} }
// Sheet Relationship Target Parts // Sheet Relationship Target Parts
void xlsx_producer::write_comments() void xlsx_producer::write_comments(pugi::xml_node root)
{ {
pugi::xml_document document; auto comments_node = root.append_child("comments");
auto root_node = document.append_child("comments");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_drawings() void xlsx_producer::write_drawings(pugi::xml_node root)
{ {
pugi::xml_document document; auto ws_dr_node = root.append_child("wsDr");
auto root_node = document.append_child("wsDr");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
// Unknown Parts // Other Parts
void xlsx_producer::write_custom_property()
{
}
void xlsx_producer::write_unknown_parts() void xlsx_producer::write_unknown_parts()
{ {
pugi::xml_document document;
auto root_node = document.append_child("Hmm");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
void xlsx_producer::write_unknown_relationships() void xlsx_producer::write_unknown_relationships()
{ {
pugi::xml_document document;
auto root_node = document.append_child("Relationships");
write_document_to_archive(document, xlnt::constants::part_core(), destination_);
} }
} // namespace detail } // namespace detail

View File

@ -27,6 +27,7 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <detail/include_pugixml.hpp>
#include <xlnt/xlnt_config.hpp> #include <xlnt/xlnt_config.hpp>
#include <xlnt/packaging/zip_file.hpp> #include <xlnt/packaging/zip_file.hpp>
@ -57,50 +58,47 @@ private:
/// Write all files needed to create a valid XLSX file which represents all /// Write all files needed to create a valid XLSX file which represents all
/// data contained in workbook. /// data contained in workbook.
/// </summary> /// </summary>
void populate_archive(zip_file &archive); void populate_archive();
// Package Parts // Package Parts
void write_package_relationships(); void write_manifest();
void write_content_types(); void write_core_properties(pugi::xml_node root);
void write_app_properties(); void write_extended_properties(pugi::xml_node root);
void write_core_properties(); void write_custom_properties(pugi::xml_node root);
void write_custom_file_properties();
// SpreadsheetML-Specific Package Parts // SpreadsheetML-Specific Package Parts
void write_workbook(); void write_workbook(pugi::xml_node root);
void write_workbook_relationships();
// Workbook Relationship Target Parts // Workbook Relationship Target Parts
void write_calculation_chain(); void write_calculation_chain(pugi::xml_node root);
void write_connections(); void write_connections(pugi::xml_node root);
void write_custom_property(); void write_custom_xml_mappings(pugi::xml_node root);
void write_custom_xml_mappings(); void write_external_workbook_references(pugi::xml_node root);
void write_external_workbook_references(); void write_metadata(pugi::xml_node root);
void write_metadata(); void write_pivot_table(pugi::xml_node root);
void write_pivot_table(); void write_shared_string_table(pugi::xml_node root);
void write_shared_string_table(); void write_shared_workbook_revision_headers(pugi::xml_node root);
void write_shared_workbook_revision_headers(); void write_shared_workbook(pugi::xml_node root);
void write_shared_workbook(); void write_shared_workbook_user_data(pugi::xml_node root);
void write_shared_workbook_user_data(); void write_styles(pugi::xml_node root);
void write_styles(); void write_theme(pugi::xml_node root);
void write_theme(); void write_volatile_dependencies(pugi::xml_node root);
void write_volatile_dependencies();
void write_chartsheet(const relationship &rel); void write_chartsheet(pugi::xml_node root, const relationship &rel);
void write_dialogsheet(const relationship &rel); void write_dialogsheet(pugi::xml_node root, const relationship &rel);
bool read_worksheet(const relationship &rel); void write_worksheet(pugi::xml_node root, const relationship &rel);
void write_worksheet(const relationship &rel);
// Sheet Relationship Target Parts // Sheet Relationship Target Parts
void write_comments(); void write_comments(pugi::xml_node root);
void write_drawings(); void write_drawings(pugi::xml_node root);
// Unknown Parts // Other Parts
void write_custom_property();
void write_unknown_parts(); void write_unknown_parts();
void write_unknown_relationships(); void write_unknown_relationships();

View File

@ -169,99 +169,77 @@ std::vector<relationship> manifest::get_part_relationships(const path &part, rel
return matches; return matches;
} }
content_type manifest::get_part_content_type(const path &part) const content_type manifest::get_content_type(const path &part) const
{ {
return from_string(get_part_content_type_string(part)); return from_string(get_content_type_string(part));
} }
std::string manifest::get_part_content_type_string(const path &part) const std::string manifest::get_content_type_string(const path &part) const
{ {
if (part_infos_.find(part) == part_infos_.end()) if (part_infos_.find(part) == part_infos_.end())
{ {
auto extension = part.extension();
if (has_default_type(extension))
{
return get_default_type(extension);
}
throw key_not_found(); throw key_not_found();
} }
return part_infos_.at(part).content_type; return part_infos_.at(part).content_type;
} }
void manifest::register_part(const path &part, const path &parent, const std::string &content_type, relationship::type relation) void manifest::register_override_type(const path &part, const std::string &content_type)
{ {
part_infos_[part] = { content_type, {} }; part_infos_[part] = { content_type, {} };
relationship rel(next_package_relationship_id(), relation, part, target_mode::internal);
part_infos_[parent].relationships.push_back(rel);
} }
void manifest::register_part(const path &parent, const relationship &rel, const std::string &content_type) std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode)
{ {
part_infos_[rel.get_target_uri()] = { content_type,{} }; return register_package_relationship(type, target_uri, mode, next_package_relationship_id());
part_infos_[parent].relationships.push_back(rel);
} }
void manifest::register_package_part(const path &part, const std::string &content_type, relationship::type relation) std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id)
{ {
part_infos_[part] = { content_type, {} }; if (has_package_relationship(rel_id))
{
throw invalid_parameter();
}
relationship rel(next_package_relationship_id(), relation, part, target_mode::internal); relationship rel(rel_id, type, target_uri, mode);
package_relationships_.push_back(rel); package_relationships_.push_back(rel);
return rel_id;
} }
void manifest::unregister_part(const path &part) bool manifest::has_package_relationship(const std::string &rel_id) const
{ {
if (part_infos_.find(part) != part_infos_.end()) for (const auto &rel : package_relationships_)
{ {
part_infos_.erase(part); if (rel.get_id() == rel_id)
}
else
{ {
throw key_not_found(); return true;
}
auto package_rels_iter = package_relationships_.begin();
while (package_rels_iter != package_relationships_.end())
{
if (package_rels_iter->get_target_uri() == part)
{
package_rels_iter = package_relationships_.erase(package_rels_iter);
continue;
}
++package_rels_iter;
}
for (auto &current_part : part_infos_)
{
auto rels_iter = current_part.second.relationships.begin();
while (rels_iter != current_part.second.relationships.end())
{
if (rels_iter->get_target_uri() == part)
{
rels_iter = current_part.second.relationships.erase(rels_iter);
continue;
}
++rels_iter;
}
} }
} }
std::vector<path> manifest::get_parts() const return false;
}
std::vector<path> manifest::get_overriden_parts() const
{ {
std::vector<path> parts; std::vector<path> overriden;
for (const auto &part : part_infos_) for (const auto &part : part_infos_)
{ {
parts.push_back(part.first); if (!part.second.content_type.empty())
}
return parts;
}
bool manifest::has_part(const path &part) const
{ {
return part_infos_.find(part) != part_infos_.end(); overriden.push_back(part.first);
}
}
return overriden;
} }
std::vector<relationship> manifest::get_package_relationships() const std::vector<relationship> manifest::get_package_relationships() const
@ -269,7 +247,7 @@ std::vector<relationship> manifest::get_package_relationships() const
return package_relationships_; return package_relationships_;
} }
relationship manifest::get_package_relationship(const std::string &rel_id) relationship manifest::get_package_relationship(const std::string &rel_id) const
{ {
for (const auto rel : package_relationships_) for (const auto rel : package_relationships_)
{ {
@ -284,14 +262,12 @@ relationship manifest::get_package_relationship(const std::string &rel_id)
std::vector<relationship> manifest::get_part_relationships(const path &part) const std::vector<relationship> manifest::get_part_relationships(const path &part) const
{ {
std::vector<relationship> matches; if (part_infos_.find(part) == part_infos_.end())
for (const auto &rel : part_infos_.at(part).relationships)
{ {
matches.push_back(rel); throw key_not_found();
} }
return matches; return part_infos_.at(part).relationships;
} }
relationship manifest::get_part_relationship(const path &part, const std::string &rel_id) const relationship manifest::get_part_relationship(const path &part, const std::string &rel_id) const
@ -312,27 +288,30 @@ relationship manifest::get_part_relationship(const path &part, const std::string
throw key_not_found(); throw key_not_found();
} }
std::string manifest::register_external_package_relationship(relationship::type type, const std::string &target_uri) std::vector<path> manifest::get_parts_with_relationships() const
{ {
return register_external_package_relationship(type, target_uri, next_package_relationship_id()); std::vector<path> with_relationships;
for (const auto &info : part_infos_)
{
if (!info.second.relationships.empty())
{
with_relationships.push_back(info.first);
}
} }
std::string manifest::register_external_package_relationship(relationship::type type, const std::string &target_uri, const std::string &rel_id) return with_relationships;
{
relationship rel(rel_id, type, path(target_uri), target_mode::external);
package_relationships_.push_back(rel);
return rel_id;
} }
std::string manifest::register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri) std::string manifest::register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode)
{ {
return register_external_relationship(source_part, type, target_uri, next_relationship_id(source_part)); return register_part_relationship(source_uri, type, target_uri, mode, next_relationship_id(source_uri));
} }
std::string manifest::register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri, const std::string &rel_id) std::string manifest::register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id)
{ {
relationship rel(rel_id, type, path(target_uri), target_mode::external); relationship rel(rel_id, type, path(target_uri), mode);
part_infos_.at(source_part).relationships.push_back(rel); part_infos_.at(source_uri).relationships.push_back(rel);
return rel_id; return rel_id;
} }

View File

@ -1,82 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <detail/constants.hpp>
#include <helpers/path_helper.hpp>
#include <helpers/xml_helper.hpp>
#include <xlnt/workbook/workbook.hpp>
class test_core : public CxxTest::TestSuite
{
public:
void test_read_properties_core()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty.xlsx"));
TS_ASSERT_EQUALS(wb.get_creator(), "*.*");
TS_ASSERT_EQUALS(wb.get_last_modified_by(), "Charlie Clark");
TS_ASSERT_EQUALS(wb.get_created(), xlnt::datetime(2010, 4, 9, 20, 43, 12));
TS_ASSERT_EQUALS(wb.get_modified(), xlnt::datetime(2014, 1, 2, 14, 53, 6));
}
void test_read_sheets_titles()
{
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty.xlsx"));
TS_ASSERT_EQUALS(wb.get_sheet_titles(), expected_titles);
}
void test_read_properties_core_libre()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty_libre.xlsx"));
TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::windows_1900);
}
void test_read_sheets_titles_libre()
{
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty_libre.xlsx"));
auto title_iter = expected_titles.begin();
for(auto ws : wb)
{
TS_ASSERT_EQUALS(ws.get_title(), *(title_iter++));
}
}
void test_write_properties_core()
{
xlnt::workbook wb;
wb.set_creator("TEST_USER");
wb.set_last_modified_by("SOMEBODY");
wb.set_created(xlnt::datetime(2010, 4, 1, 20, 30, 00));
wb.set_modified(xlnt::datetime(2010, 4, 5, 14, 5, 30));
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/core.xml"),
wb, xlnt::constants::part_core()));
}
void test_write_properties_app()
{
xlnt::workbook wb;
wb.set_application("Microsoft Excel");
wb.set_app_version("12.0000");
wb.set_company("Company");
wb.create_sheet();
wb.create_sheet();
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/core.xml"),
wb, xlnt::constants::part_core()));
}
};

View File

@ -0,0 +1,225 @@
#include <cassert>
#include <fstream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include "helpers/path_helper.hpp"
#include "helpers/temporary_file.hpp"
class test_zip_file : public CxxTest::TestSuite
{
public:
test_zip_file()
{
existing_file = path_helper::get_data_directory("xlsx/4_not-package.xlsx");
expected_string = "not-empty";
}
bool files_equal(const xlnt::path &left, const xlnt::path &right)
{
if(left.to_string() == right.to_string())
{
return true;
}
std::ifstream stream_left(left.to_string(), std::ios::binary);
std::ifstream stream_right(right.to_string(), std::ios::binary);
while(stream_left && stream_right)
{
if(stream_left.get() != stream_right.get())
{
return false;
}
}
return true;
}
void test_load_file()
{
temporary_file temp_file;
xlnt::zip_file f(existing_file);
f.save(temp_file.get_path());
TS_ASSERT(files_equal(existing_file, temp_file.get_path()));
}
void test_load_stream()
{
temporary_file temp;
std::ifstream in_stream(existing_file.to_string(), std::ios::binary);
xlnt::zip_file f(in_stream);
std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary);
f.save(out_stream);
out_stream.close();
TS_ASSERT(files_equal(existing_file, temp.get_path()));
}
void test_load_bytes()
{
temporary_file temp_file;
std::vector<std::uint8_t> source_bytes;
std::ifstream in_stream(existing_file.to_string(), std::ios::binary);
while(in_stream)
{
source_bytes.push_back(static_cast<std::uint8_t>(in_stream.get()));
}
xlnt::zip_file f(source_bytes);
f.save(temp_file.get_path());
xlnt::zip_file f2;
f2.load(temp_file.get_path());
std::vector<std::uint8_t> result_bytes;
f2.save(result_bytes);
TS_ASSERT(source_bytes == result_bytes);
}
void test_reset()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(!f.namelist().empty());
try
{
f.read(xlnt::path("text.txt"));
}
catch(std::exception e)
{
TS_ASSERT(false);
}
f.reset();
TS_ASSERT(f.namelist().empty());
try
{
f.read(xlnt::path("doesnt-exist.txt"));
TS_ASSERT(false);
}
catch(std::exception e)
{
}
f.write_string("b", xlnt::path("a"));
f.reset();
TS_ASSERT(f.namelist().empty());
f.write_string("b", xlnt::path("a"));
TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0);
}
void test_getinfo()
{
xlnt::zip_file f(existing_file);
auto info = f.getinfo(xlnt::path("text.txt"));
TS_ASSERT(info.filename.to_string() == "text.txt");
}
void test_infolist()
{
xlnt::zip_file f(existing_file);
TS_ASSERT_EQUALS(f.infolist().size(), 1);
}
void test_namelist()
{
xlnt::zip_file f(existing_file);
TS_ASSERT_EQUALS(f.namelist().size(), 1);
}
void test_open_by_name()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open(xlnt::path("text.txt")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_string);
}
void test_open_by_info()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open(xlnt::path("text.txt")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_string);
}
void test_read()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(f.read(xlnt::path("text.txt")) == expected_string);
TS_ASSERT(f.read(f.getinfo(xlnt::path("text.txt"))) == expected_string);
}
void test_testzip()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(!f.check_crc());
}
void test_write_file()
{
temporary_file temp_file;
xlnt::zip_file f;
auto text_file = path_helper::get_data_directory("xlsx/2_text.xlsx");
f.write_file(text_file);
f.write_file(text_file, xlnt::path("a.txt"));
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
for(auto &info : f2.infolist())
{
TS_ASSERT(f2.read(info) == expected_string);
}
}
void test_write_string()
{
xlnt::zip_file f;
f.write_string("a\na", xlnt::path("a.txt"));
xlnt::zip_info info;
info.filename = xlnt::path("b.txt");
info.date_time.year = 2014;
f.write_string("b\nb", info);
temporary_file temp_file;
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
TS_ASSERT(f2.read(xlnt::path("a.txt")) == "a\na");
TS_ASSERT(f2.read(f2.getinfo(xlnt::path("b.txt"))) == "b\nb");
}
void test_comment()
{
xlnt::zip_file f;
f.comment = "comment";
temporary_file temp_file;
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
TS_ASSERT(f2.comment == "comment");
xlnt::zip_file f3;
std::vector<std::uint8_t> bytes { 1, 2, 3 };
TS_ASSERT_THROWS(f3.load(bytes), std::runtime_error);
}
private:
xlnt::path existing_file;
std::string expected_string;
};

View File

@ -31,6 +31,7 @@
#include <xlnt/packaging/zip_file.hpp> #include <xlnt/packaging/zip_file.hpp>
#include <xlnt/utils/path.hpp> #include <xlnt/utils/path.hpp>
#include <xlnt/utils/exceptions.hpp>
namespace { namespace {
@ -164,8 +165,19 @@ zip_file::~zip_file()
void zip_file::load(std::istream &stream) void zip_file::load(std::istream &stream)
{ {
if (!stream.good())
{
throw invalid_file("((std::istream))");
}
reset(); reset();
buffer_.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); buffer_.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
if (buffer_.empty())
{
throw invalid_file("((stream)) - empty file");
}
remove_comment(); remove_comment();
start_read(); start_read();
} }
@ -174,11 +186,31 @@ void zip_file::load(const path &filename)
{ {
filename_ = filename; filename_ = filename;
std::ifstream stream(filename.to_string(), std::ios::binary); std::ifstream stream(filename.to_string(), std::ios::binary);
load(stream);
if (!stream.good())
{
throw invalid_file(filename.to_string());
}
reset();
buffer_.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
if (buffer_.empty())
{
throw invalid_file(filename.to_string() + " - empty file");
}
remove_comment();
start_read();
} }
void zip_file::load(const std::vector<unsigned char> &bytes) void zip_file::load(const std::vector<unsigned char> &bytes)
{ {
if (bytes.empty())
{
throw invalid_file("((bytes))");
}
reset(); reset();
buffer_.assign(bytes.begin(), bytes.end()); buffer_.assign(bytes.begin(), bytes.end());
remove_comment(); remove_comment();

View File

@ -1,285 +0,0 @@
#pragma once
#include <iostream>
#include <pugixml.hpp>
#include <cxxtest/TestSuite.h>
#include <detail/stylesheet.hpp>
#include <xlnt/packaging/zip_file.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <helpers/xml_helper.hpp>
#include <helpers/path_helper.hpp>
class test_stylesheet : public CxxTest::TestSuite
{
public:
void test_from_simple()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("reader/styles/simple-styles.xml"));
}
void test_from_complex()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("reader/styles/complex-styles.xml"));
}
void test_add_existing_style()
{
xlnt::workbook wb;
auto &s = wb.create_style("test");
wb.add_style(s);
xlnt::style copy;
copy = s;
TS_ASSERT_EQUALS(s, copy);
}
/*
void _test_unprotected_cell()
{
datadir.chdir();
src = open ("worksheet_unprotected_style.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
styles = stylesheet.cell_styles;
assert len(styles) == 3;
// default is cells are locked
assert styles[1] == StyleArray([4,0,0,0,0,0,0,0,0]);
assert styles[2] == StyleArray([3,0,0,0,1,0,0,0,0]);
}
void _test_read_cell_style()
{
datadir.chdir();
src = open("empty-workbook-styles.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
styles = stylesheet.cell_styles;
assert len(styles) == 2;
assert styles[1] == StyleArray([0,0,0,9,0,0,0,0,1];
}
void _test_read_xf_no_number_format()
{
datadir.chdir();
src = open("no_number_format.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
styles = stylesheet.cell_styles;
assert len(styles) == 3;
assert styles[1] == StyleArray([1,0,1,0,0,0,0,0,0]);
assert styles[2] == StyleArray([0,0,0,14,0,0,0,0,0]);
}
void _test_none_values()
{
datadir.chdir();
src = open("none_value_styles.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
fonts = stylesheet.fonts;
assert fonts[0].scheme is None;
assert fonts[0].vertAlign is None;
assert fonts[1].u is None;
}
void _test_alignment()
{
datadir.chdir();
src = open("alignment_styles.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
styles = stylesheet.cell_styles;
assert len(styles) == 3;
assert styles[2] == StyleArray([0,0,0,0,0,2,0,0,0]);
assert stylesheet.alignments == [
Alignment(),
Alignment(textRotation=180),
Alignment(vertical='top', textRotation=255),
];
}
void _test_rgb_colors()
{
datadir.chdir();
src = open("rgb_colors.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
assert len(stylesheet.colors.index) == 64;
assert stylesheet.colors.index[0] == "00000000";
assert stylesheet.colors.index[-1] == "00333333";
}
void _test_custom_number_formats()
{
datadir.chdir();
src = open("styles_number_formats.xml", "rb");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
assert stylesheet.number_formats == [
'_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ ',
"#,##0.00_ ",
"yyyy/m/d;@",
"0.00000_ "
];
}
void _test_assign_number_formats()
{
node = fromstring(
"<styleSheet>"
"<numFmts count=\"1\">"
" <numFmt numFmtId=\"43\" formatCode=\"_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * \"-\"??_ ;_ @_ \" />"
"</numFmts>"
"<cellXfs count=\"0\">"
"<xf numFmtId=\"43\" fontId=\"2\" fillId=\"0\" borderId=\"0\""
" applyFont=\"0\" applyFill=\"0\" applyBorder=\"0\" applyAlignment=\"0\" applyProtection=\"0\">"
" <alignment vertical=\"center\"/>"
"</xf>"
"</cellXfs>"
"</styleSheet>");
stylesheet = Stylesheet.from_tree(node);
styles = stylesheet.cell_styles;
assert styles[0] == StyleArray([2, 0, 0, 164, 0, 1, 0, 0, 0]);
}
void _test_named_styles()
{
datadir.chdir();
src = open("complex-styles.xml");
xml = src.read();
node = fromstring(xml);
stylesheet = Stylesheet.from_tree(node);
followed = stylesheet.named_styles['Followed Hyperlink'];
assert followed.name == "Followed Hyperlink";
assert followed.font == stylesheet.fonts[2];
assert followed.fill == DEFAULT_EMPTY_FILL;
assert followed.border == Border();
link = stylesheet.named_styles['Hyperlink'];
assert link.name == "Hyperlink";
assert link.font == stylesheet.fonts[1];
assert link.fill == DEFAULT_EMPTY_FILL;
assert link.border == Border();
normal = stylesheet.named_styles['Normal'];
assert normal.name == "Normal";
assert normal.font == stylesheet.fonts[0];
assert normal.fill == DEFAULT_EMPTY_FILL;
assert normal.border == Border();
}
void _test_no_styles()
{
wb1 = wb2 = Workbook();
archive = ZipFile(BytesIO(), "a");
apply_stylesheet(archive, wb1);
assert wb1._cell_styles == wb2._cell_styles;
assert wb2._named_styles == wb2._named_styles;
}
void _test_write_worksheet()
{
wb = Workbook()
node = write_stylesheet(wb);
xml = tostring(node);
const std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"
" <numFmts count=\"0\" />"
" <fonts count=\"1\">"
" <font>"
" <name val=\"Calibri\"></name>"
" <family val=\"2\"></family>"
" <color theme=\"1\"></color>"
" <sz val=\"11\"></sz>"
" <scheme val=\"minor\"></scheme>"
" </font>"
" </fonts>"
" <fills count=\"2\">"
" <fill>"
" <patternFill></patternFill>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"></patternFill>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left></left>"
" <right></right>"
" <top></top>"
" <bottom></bottom>"
" <diagonal></diagonal>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf borderId=\"0\" fillId=\"0\" fontId=\"0\" numFmtId=\"0\"></xf>"
" </cellStyleXfs>"
" <cellXfs count=\"1\">"
" <xf borderId=\"0\" fillId=\"0\" fontId=\"0\" numFmtId=\"0\" pivotButton=\"0\" quotePrefix=\"0\" xfId=\"0\"></xf>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle builtinId=\"0\" hidden=\"0\" name=\"Normal\" xfId=\"0\"></cellStyle>"
" </cellStyles>"
"<tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleLight16\"/>"
"</styleSheet>";
diff = compare_xml(xml, expected);
assert diff is None, diff;
}
void _test_simple_styles() {
wb = Workbook();
wb.guess_types = True;
ws = wb.active;
now = datetime.date.today();
for idx, v in enumerate(['12.34%', now, 'This is a test', '31.31415', None], 1)
{
ws.append([v]);
_ = ws.cell(column=1, row=idx).style_id;
}
// set explicit formats
ws['D9'].number_format = numbers.FORMAT_NUMBER_00;
ws['D9'].protection = Protection(locked=True);
ws['D9'].style_id;
ws['E1'].protection = Protection(hidden=True);
ws['E1'].style_id;
assert len(wb._cell_styles) == 5;
stylesheet = write_stylesheet(wb);
datadir.chdir();
reference_file = open('simple-styles.xml');
expected = reference_file.read();
xml = tostring(stylesheet);
diff = compare_xml(xml, expected);
assert diff is None, diff;
}
*/
};

View File

@ -187,6 +187,10 @@ path path::parent() const
{ {
result.parts_.pop_back(); result.parts_.pop_back();
} }
else
{
return path("");
}
return result; return result;
} }

View File

@ -1,254 +0,0 @@
#include <cassert>
#include <fstream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include "helpers/path_helper.hpp"
#include "helpers/temporary_file.hpp"
class test_zip_file : public CxxTest::TestSuite
{
public:
test_zip_file()
{
existing_file = path_helper::get_data_directory("genuine/empty.xlsx");
expected_content_types_string = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"xml\" ContentType=\"application/xml\"/><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/><Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet2.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet3.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet4.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/><Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/><Override PartName=\"/xl/sharedStrings.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/><Override PartName=\"/xl/calcChain.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml\"/><Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/><Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/></Types>";
expected_atxt_string = "<?xml version=\"1.0\" ?>\n<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"3\" uniqueCount=\"2\"><si><t>This is cell A1 in Sheet 1</t></si><si><t>This is cell G5</t></si></sst>";
expected_printdir_string =
" Length Date Time Name\n"
"--------- ---------- ----- ----\n"
" 1704 01/01/1980 00:00 [Content_Types].xml\n"
" 588 01/01/1980 00:00 _rels/.rels\n"
" 917 01/01/1980 00:00 docProps/app.xml\n"
" 609 01/01/1980 00:00 docProps/core.xml\n"
" 1254 01/01/1980 00:00 xl/_rels/workbook.xml.rels\n"
" 169 01/01/1980 00:00 xl/calcChain.xml\n"
" 233 01/01/1980 00:00 xl/sharedStrings.xml\n"
" 1724 01/01/1980 00:00 xl/styles.xml\n"
" 6995 01/01/1980 00:00 xl/theme/theme1.xml\n"
" 898 01/01/1980 00:00 xl/workbook.xml\n"
" 1068 07/21/2016 20:27 xl/worksheets/sheet1.xml\n"
" 4427 01/01/1980 00:00 xl/worksheets/sheet2.xml\n"
" 1032 01/01/1980 00:00 xl/worksheets/sheet3.xml\n"
" 1231 01/01/1980 00:00 xl/worksheets/sheet4.xml\n"
"--------- -------\n"
" 22849 14 files\n";
}
bool files_equal(const xlnt::path &left, const xlnt::path &right)
{
if(left.to_string() == right.to_string())
{
return true;
}
std::ifstream stream_left(left.to_string(), std::ios::binary);
std::ifstream stream_right(right.to_string(), std::ios::binary);
while(stream_left && stream_right)
{
if(stream_left.get() != stream_right.get())
{
return false;
}
}
return true;
}
void test_load_file()
{
temporary_file temp_file;
xlnt::zip_file f(existing_file);
f.save(temp_file.get_path());
TS_ASSERT(files_equal(existing_file, temp_file.get_path()));
}
void test_load_stream()
{
temporary_file temp;
std::ifstream in_stream(existing_file.to_string(), std::ios::binary);
xlnt::zip_file f(in_stream);
std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary);
f.save(out_stream);
out_stream.close();
TS_ASSERT(files_equal(existing_file, temp.get_path()));
}
void test_load_bytes()
{
temporary_file temp_file;
std::vector<std::uint8_t> source_bytes;
std::ifstream in_stream(existing_file.to_string(), std::ios::binary);
while(in_stream)
{
source_bytes.push_back(static_cast<std::uint8_t>(in_stream.get()));
}
xlnt::zip_file f(source_bytes);
f.save(temp_file.get_path());
xlnt::zip_file f2;
f2.load(temp_file.get_path());
std::vector<std::uint8_t> result_bytes;
f2.save(result_bytes);
TS_ASSERT(source_bytes == result_bytes);
}
void test_reset()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(!f.namelist().empty());
try
{
f.read(xlnt::path("[Content_Types].xml"));
}
catch(std::exception e)
{
TS_ASSERT(false);
}
f.reset();
TS_ASSERT(f.namelist().empty());
try
{
f.read(xlnt::path("[Content_Types].xml"));
TS_ASSERT(false);
}
catch(std::exception e)
{
}
f.write_string("b", xlnt::path("a"));
f.reset();
TS_ASSERT(f.namelist().empty());
f.write_string("b", xlnt::path("a"));
TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0);
}
void test_getinfo()
{
xlnt::zip_file f(existing_file);
auto info = f.getinfo(xlnt::path("[Content_Types].xml"));
TS_ASSERT(info.filename.to_string() == "[Content_Types].xml");
}
void test_infolist()
{
xlnt::zip_file f(existing_file);
TS_ASSERT_EQUALS(f.infolist().size(), 14);
}
void test_namelist()
{
xlnt::zip_file f(existing_file);
TS_ASSERT_EQUALS(f.namelist().size(), 14);
}
void test_open_by_name()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_content_types_string);
}
void test_open_by_info()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_content_types_string);
}
void test_read()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(f.read(xlnt::path("[Content_Types].xml")) == expected_content_types_string);
TS_ASSERT(f.read(f.getinfo(xlnt::path("[Content_Types].xml"))) == expected_content_types_string);
}
void test_testzip()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(!f.check_crc());
}
void test_write_file()
{
temporary_file temp_file;
xlnt::zip_file f;
auto text_file = path_helper::get_data_directory("reader/sharedStrings.xml");
f.write_file(text_file);
f.write_file(text_file, xlnt::path("sharedStrings2.xml"));
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
for(auto &info : f2.infolist())
{
if(info.filename.to_string() == "sharedStrings2.xml")
{
TS_ASSERT(f2.read(info) == expected_atxt_string);
}
else if(info.filename.basename() == "sharedStrings.xml")
{
TS_ASSERT(f2.read(info) == expected_atxt_string);
}
}
}
void test_write_string()
{
xlnt::zip_file f;
f.write_string("a\na", xlnt::path("a.txt"));
xlnt::zip_info info;
info.filename = xlnt::path("b.txt");
info.date_time.year = 2014;
f.write_string("b\nb", info);
temporary_file temp_file;
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
TS_ASSERT(f2.read(xlnt::path("a.txt")) == "a\na");
TS_ASSERT(f2.read(f2.getinfo(xlnt::path("b.txt"))) == "b\nb");
}
void test_comment()
{
xlnt::zip_file f;
f.comment = "comment";
temporary_file temp_file;
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_path());
TS_ASSERT(f2.comment == "comment");
xlnt::zip_file f3;
std::vector<std::uint8_t> bytes { 1, 2, 3 };
TS_ASSERT_THROWS(f3.load(bytes), std::runtime_error);
}
private:
xlnt::path existing_file;
std::string expected_content_types_string;
std::string expected_atxt_string;
std::string expected_printdir_string;
};

View File

@ -0,0 +1,14 @@
#pragma once
#include <fstream>
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/path_helper.hpp>
#include <xlnt/cell/text.hpp>
#include <xlnt/cell/text_run.hpp>
#include <xlnt/packaging/manifest.hpp>
class test_consume_xlsx : public CxxTest::TestSuite
{
};

View File

@ -0,0 +1,32 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/temporary_file.hpp>
#include <helpers/path_helper.hpp>
#include <helpers/xml_helper.hpp>
#include <xlnt/packaging/zip_file.hpp>
#include <xlnt/workbook/workbook.hpp>
class test_produce_xlsx : public CxxTest::TestSuite
{
public:
bool workbook_matches_file(xlnt::workbook &wb, const xlnt::path &file)
{
std::vector<std::uint8_t> buffer;
wb.save(buffer);
wb.save(xlnt::path("C:\\Users\\Thomas\\Desktop\\a.xlsx"));
xlnt::zip_file wb_archive(buffer);
xlnt::zip_file file_archive(file);
return xml_helper::archives_match(wb_archive, file_archive);
}
void test_empty_excel()
{
xlnt::workbook wb = xlnt::workbook::empty_excel();
TS_ASSERT(workbook_matches_file(wb, path_helper::get_data_directory("xlsx/8_default-excel.xlsx")));
}
};

View File

@ -1,438 +0,0 @@
#pragma once
#include <fstream>
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/path_helper.hpp>
#include <xlnt/cell/text.hpp>
#include <xlnt/cell/text_run.hpp>
#include <xlnt/packaging/manifest.hpp>
class test_read : public CxxTest::TestSuite
{
public:
xlnt::workbook standard_workbook()
{
xlnt::workbook wb;
auto path = path_helper::get_data_directory("genuine/empty.xlsx");
wb.load(path);
return wb;
}
void test_read_standard_workbook()
{
TS_ASSERT_THROWS_NOTHING(standard_workbook());
}
void test_read_standard_workbook_from_fileobj()
{
auto path = path_helper::get_data_directory("genuine/empty.xlsx");
std::ifstream fo(path.to_string(), std::ios::binary);
xlnt::workbook wb;
wb.load(fo);
TS_ASSERT(wb.get_sheet_titles().size() == 1);
}
void test_read_worksheet()
{
auto wb = standard_workbook();
auto sheet2 = wb.get_sheet_by_title("Sheet2 - Numbers");
TS_ASSERT_DIFFERS(sheet2, nullptr);
TS_ASSERT_EQUALS("This is cell G5", sheet2.get_cell("G5").get_value<std::string>());
TS_ASSERT_EQUALS(18, sheet2.get_cell("D18").get_value<int>());
TS_ASSERT_EQUALS(true, sheet2.get_cell("G9").get_value<bool>());
TS_ASSERT_EQUALS(false, sheet2.get_cell("G10").get_value<bool>());
}
void test_read_nostring_workbook()
{
auto path = path_helper::get_data_directory("genuine/empty-no-string.xlsx");
xlnt::workbook wb;
TS_ASSERT_THROWS_NOTHING(wb.load(path));
}
void test_read_empty_file()
{
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_file.xlsx")), xlnt::invalid_file);
}
void test_read_empty_archive()
{
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_archive.xlsx")), xlnt::invalid_file);
}
void test_read_workbook_with_no_properties()
{
xlnt::workbook wb;
TS_ASSERT_THROWS_NOTHING(wb.load(path_helper::get_data_directory("genuine/empty_with_no_properties.xlsx")));
}
xlnt::workbook workbook_with_styles()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty-with-styles.xlsx"));
return wb;
}
void test_read_workbook_with_styles_general()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto format = ws.get_cell("A1").get_number_format();
auto expected = xlnt::number_format::general();
TS_ASSERT_EQUALS(format, expected);
}
void test_read_workbook_with_styles_date()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto format = ws.get_cell("A2").get_number_format();
auto expected = xlnt::number_format::date_xlsx14();
TS_ASSERT_EQUALS(format, expected);
}
void test_read_workbook_with_styles_number()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A3").get_number_format();
auto expected = xlnt::number_format::number_00();
TS_ASSERT_EQUALS(code, expected);
}
void test_read_workbook_with_styles_time()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A4").get_number_format();
auto expected = xlnt::number_format::date_time3();
TS_ASSERT_EQUALS(code, expected);
}
void test_read_workbook_with_styles_percentage()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A5").get_number_format();
auto expected = xlnt::number_format::percentage_00();
TS_ASSERT_EQUALS(code, expected);
}
void test_read_charset_excel()
{
xlnt::workbook wb;
auto path = path_helper::get_data_directory("reader/charset-excel.xlsx");
wb.load(path);
auto ws = wb["Sheet1"];
auto val = ws.get_cell("A1").get_value<std::string>();
TS_ASSERT_EQUALS(val, "Direnç");
}
void test_read_shared_strings_max_range()
{
xlnt::workbook wb;
const auto path = path_helper::get_data_directory("reader/shared_strings-max_range.xlsx");
wb.load(path);
auto ws = wb["Sheet1"];
auto val = ws.get_cell("A1").get_value<std::string>();
TS_ASSERT_EQUALS(val, "Donald");
}
void test_read_shared_strings_multiple_r_nodes()
{
auto path = path_helper::get_data_directory("reader/shared_strings-multiple_r_nodes.xlsx");
xlnt::workbook wb;
wb.load(path);
auto ws = wb["Sheet1"];
auto val = ws.get_cell("A1").get_value<std::string>();
TS_ASSERT_EQUALS(val, "abcdef");
}
xlnt::workbook date_mac_1904()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("reader/date_1904.xlsx"));
return wb;
}
xlnt::workbook date_std_1900()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("reader/date_1900.xlsx"));
return wb;
}
void test_read_win_base_date()
{
auto wb = date_std_1900();
TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::windows_1900);
}
void test_read_mac_base_date()
{
auto wb = date_mac_1904();
TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::mac_1904);
}
void test_read_date_style_win()
{
auto wb = date_std_1900();
auto ws = wb["Sheet1"];
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14());
}
void test_read_date_style_mac()
{
auto wb = date_mac_1904();
auto ws = wb["Sheet1"];
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14());
}
void test_read_compare_mac_win_dates()
{
auto wb_mac = date_mac_1904();
auto ws_mac = wb_mac["Sheet1"];
auto wb_win = date_std_1900();
auto ws_win = wb_win["Sheet1"];
xlnt::datetime dt(2011, 10, 31);
TS_ASSERT_EQUALS(ws_mac.get_cell("A1").get_value<xlnt::datetime>(), dt);
TS_ASSERT_EQUALS(ws_win.get_cell("A1").get_value<xlnt::datetime>(), dt);
TS_ASSERT_EQUALS(ws_mac.get_cell("A1").get_value<xlnt::datetime>(), ws_win.get_cell("A1").get_value<xlnt::datetime>());
}
void test_read_no_theme()
{
auto path = path_helper::get_data_directory("genuine/libreoffice_nrt.xlsx");
xlnt::workbook wb;
TS_ASSERT_THROWS_NOTHING(wb.load(path));
}
void _test_read_complex_formulae()
{
/*
auto path = PathHelper::GetDataDirectory("reader/formulae.xlsx");
auto wb = xlnt::reader::load_workbook(path);
auto ws = wb.get_active_sheet();
// Test normal forumlae
TS_ASSERT(!ws.get_cell("A1").has_formula());
TS_ASSERT(!ws.get_cell("A2").has_formula());
TS_ASSERT(ws.get_cell("A3").has_formula());
TS_ASSERT(ws.get_formula_attributes().find("A3") == ws.get_formula_attributes().end());
TS_ASSERT(ws.get_cell("A3").get_formula() == "12345");
TS_ASSERT(ws.get_cell("A4").has_formula());
TS_ASSERT(ws.get_formula_attributes().find("A3") == ws.get_formula_attributes().end());
ws.get_cell("A4").set_formula("A2+A3");
TS_ASSERT(ws.get_cell("A5").has_formula());
TS_ASSERT(ws.get_formula_attributes().find("A5") == ws.get_formula_attributes().end());
ws.get_cell("A5").set_formula("SUM(A2:A4)");
// Test unicode
std::string expected = "=IF(ISBLANK(B16), \"D\xFCsseldorf\", B16)";
TS_ASSERT(ws.get_cell("A16").get_formula() == expected);
// Test shared forumlae
TS_ASSERT(ws.get_cell("B7").get_data_type() == "f");
TS_ASSERT(ws.formula_attributes["B7"]["t"] == "shared");
TS_ASSERT(ws.formula_attributes["B7"]["si"] == "0");
TS_ASSERT(ws.formula_attributes["B7"]["ref"] == "B7:E7");
TS_ASSERT(ws.get_cell("B7").value == "=B4*2");
TS_ASSERT(ws.get_cell("C7").get_data_type() == "f");
TS_ASSERT(ws.formula_attributes["C7"]["t"] == "shared");
TS_ASSERT(ws.formula_attributes["C7"]["si"] == "0");
TS_ASSERT("ref" not in ws.formula_attributes["C7"]);
TS_ASSERT(ws.get_cell("C7").value == "=");
TS_ASSERT(ws.get_cell("D7").get_data_type() == "f");
TS_ASSERT(ws.formula_attributes["D7"]["t"] == "shared");
TS_ASSERT(ws.formula_attributes["D7"]["si"] == "0");
TS_ASSERT("ref" not in ws.formula_attributes["D7"]);
TS_ASSERT(ws.get_cell("D7").value == "=");
TS_ASSERT(ws.get_cell("E7").get_data_type() == "f");
TS_ASSERT(ws.formula_attributes["E7"]["t"] == "shared");
TS_ASSERT(ws.formula_attributes["E7"]["si"] == "0");
TS_ASSERT("ref" not in ws.formula_attributes["E7"]);
TS_ASSERT(ws.get_cell("E7").value == "=");
// Test array forumlae
TS_ASSERT(ws.get_cell("C10").get_data_type() == "f");
TS_ASSERT("ref" not in ws.formula_attributes["C10"]["ref"]);
TS_ASSERT(ws.formula_attributes["C10"]["t"] == "array");
TS_ASSERT("si" not in ws.formula_attributes["C10"]);
TS_ASSERT(ws.formula_attributes["C10"]["ref"] == "C10:C14");
TS_ASSERT(ws.get_cell("C10").value == "=SUM(A10:A14*B10:B14)");
TS_ASSERT(ws.get_cell("C11").get_data_type() != "f");
*/
}
void test_data_only()
{
auto path = path_helper::get_data_directory("reader/formulae.xlsx");
xlnt::workbook wb;
wb.set_data_only(true);
wb.load(path);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_formula_attributes().empty());
TS_ASSERT(ws.get_workbook().get_data_only());
TS_ASSERT(ws.get_cell("A2").get_data_type() == xlnt::cell::type::numeric);
TS_ASSERT(ws.get_cell("A2").get_value<int>() == 12345);
TS_ASSERT(!ws.get_cell("A2").has_formula());
TS_ASSERT(ws.get_cell("A3").get_data_type() == xlnt::cell::type::numeric);
TS_ASSERT(ws.get_cell("A3").get_value<int>() == 12345);
TS_ASSERT(!ws.get_cell("A3").has_formula());
TS_ASSERT(ws.get_cell("A4").get_data_type() == xlnt::cell::type::numeric);
TS_ASSERT(ws.get_cell("A4").get_value<int>() == 24690);
TS_ASSERT(!ws.get_cell("A4").has_formula());
TS_ASSERT(ws.get_cell("A5").get_data_type() == xlnt::cell::type::numeric);
TS_ASSERT(ws.get_cell("A5").get_value<int>() == 49380);
TS_ASSERT(!ws.get_cell("A5").has_formula());
}
void test_read_content_types()
{
std::vector<std::pair<std::string, std::string>> expected =
{
{"/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"},
{"/xl/worksheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"},
{"/xl/chartsheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"},
{"/xl/worksheets/sheet2.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"},
{"/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"},
{"/xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"},
{"/xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"},
{"/xl/drawings/drawing1.xml", "application/vnd.openxmlformats-officedocument.drawing+xml"},
{"/xl/charts/chart1.xml", "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"},
{"/xl/drawings/drawing2.xml", "application/vnd.openxmlformats-officedocument.drawing+xml"},
{"/xl/charts/chart2.xml", "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"},
{"/xl/calcChain.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"},
{"/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"},
{"/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"}
};
auto path = path_helper::get_data_directory("reader/contains_chartsheets.xlsx");
xlnt::workbook wb;
wb.load(path);
auto &result = wb.get_manifest().get_parts();
if(result.size() != expected.size())
{
TS_ASSERT_EQUALS(result.size(), expected.size());
return;
}
for(std::size_t i = 0; i < expected.size(); i++)
{
TS_ASSERT(wb.get_manifest().has_part(xlnt::path(expected[i].first)));
TS_ASSERT_EQUALS(wb.get_manifest().get_part_content_type_string(xlnt::path(expected[i].first)), expected[i].second);
}
}
void test_guess_types()
{
bool guess;
xlnt::cell::type dtype;
std::vector<std::pair<bool, xlnt::cell::type>> test_cases = {{true, xlnt::cell::type::numeric}, {false, xlnt::cell::type::string}};
for(const auto &expected : test_cases)
{
std::tie(guess, dtype) = expected;
auto path = path_helper::get_data_directory("genuine/guess_types.xlsx");
xlnt::workbook wb;
wb.set_guess_types(guess);
wb.load(path);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_cell("D2").get_data_type() == dtype);
}
}
void test_read_autofilter()
{
auto path = path_helper::get_data_directory("reader/bug275.xlsx");
xlnt::workbook wb;
wb.load(path);
auto ws = wb.get_active_sheet();
TS_ASSERT_EQUALS(ws.get_auto_filter().to_string(), "A1:B6");
}
void test_bad_formats_xlsb()
{
auto path = path_helper::get_data_directory("genuine/a.xlsb");
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file);
}
void test_bad_formats_xls()
{
auto path = path_helper::get_data_directory("genuine/a.xls");
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file);
}
void test_bad_formats_no()
{
auto path = path_helper::get_data_directory("genuine/a.no-format");
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file);
}
void test_read_shared_strings_with_runs()
{
xlnt::workbook wb;
wb.load("reader/genuine/a.xlsx");
const auto &strings = wb.get_shared_strings();
TS_ASSERT_EQUALS(strings.size(), 1);
TS_ASSERT_EQUALS(strings.front().get_runs().size(), 1);
TS_ASSERT_EQUALS(strings.front().get_runs().front().get_size(), 13);
TS_ASSERT_EQUALS(strings.front().get_runs().front().get_color(), "color");
TS_ASSERT_EQUALS(strings.front().get_runs().front().get_font(), "font");
TS_ASSERT_EQUALS(strings.front().get_runs().front().get_family(), 12);
TS_ASSERT_EQUALS(strings.front().get_runs().front().get_scheme(), "scheme");
}
void test_read_inlinestr()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("genuine/empty.xlsx"));
TS_ASSERT_EQUALS(wb.get_sheet_by_index(0).get_cell("A1").get_value<std::string>(), "This is cell A1 in Sheet 1");
}
void test_determine_document_type()
{
xlnt::workbook wb;
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("1_empty.txt")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("2_not-empty.txt")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("3_empty.zip")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("4_not-package.zip")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("5_visio.vsdx")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("6_document.docx")), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("7_presentation.pptx")), xlnt::invalid_file);
}
};

View File

@ -0,0 +1,82 @@
#pragma once
#include <fstream>
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/path_helper.hpp>
#include <helpers/xml_helper.hpp>
#include <xlnt/packaging/zip_file.hpp>
#include <xlnt/workbook/workbook.hpp>
class test_round_trip : public CxxTest::TestSuite
{
public:
bool round_trip_matches_wr(const xlnt::workbook &original)
{
std::vector<std::uint8_t> serialized;
original.save(serialized);
xlnt::workbook deserialized;
deserialized.load(serialized);
return xml_helper::workbooks_match(original, deserialized);
}
bool round_trip_matches_rw(const xlnt::path &file)
{
xlnt::zip_file original_archive(file);
xlnt::workbook deserialized;
deserialized.load(file);
std::vector<std::uint8_t> serialized;
deserialized.save(serialized);
xlnt::zip_file resulting_archive(serialized);
return xml_helper::archives_match(original_archive, resulting_archive);
}
void test_round_trip_minimal_wr()
{
TS_SKIP("");
xlnt::workbook wb = xlnt::workbook::minimal();
TS_ASSERT(round_trip_matches_wr(wb));
}
void test_round_trip_empty_excel_wr()
{
TS_SKIP("");
xlnt::workbook wb = xlnt::workbook::empty_excel();
TS_ASSERT(round_trip_matches_wr(wb));
}
void test_round_trip_empty_libre_office_wr()
{
TS_SKIP("");
xlnt::workbook wb = xlnt::workbook::empty_libre_office();
TS_ASSERT(round_trip_matches_wr(wb));
}
void test_round_trip_empty_pages_wr()
{
TS_SKIP("");
xlnt::workbook wb = xlnt::workbook::empty_numbers();
TS_ASSERT(round_trip_matches_wr(wb));
}
void test_round_trip_empty_excel_rw()
{
TS_SKIP("");
auto path = path_helper::get_data_directory("xlsx/8_default-excel.xlsx");
TS_ASSERT(round_trip_matches_rw(path));
}
void test_round_trip_empty_libre_rw()
{
TS_SKIP("");
auto path = path_helper::get_data_directory("xlsx/9_default-libre-office.xlsx");
TS_ASSERT(round_trip_matches_rw(path));
}
};

View File

@ -1,36 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/workbook/workbook.hpp>
#include <helpers/path_helper.hpp>
class test_style_reader : public CxxTest::TestSuite
{
public:
void test_complex_formatting()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("reader/formatting.xlsx"));
// border_style
auto ws = wb.get_active_sheet();
auto e30 = ws.get_cell("E30");
TS_ASSERT_EQUALS(e30.get_border().get_side(xlnt::border::side::top).get_color().get_indexed().get_index(), 10);
TS_ASSERT_EQUALS(e30.get_border().get_side(xlnt::border::side::top).get_style(), xlnt::border_style::thin);
// underline_style
auto f30 = ws.get_cell("F30");
TS_ASSERT_EQUALS(e30.get_font().get_underline(), xlnt::font::underline_style::none);
TS_ASSERT_EQUALS(f30.get_font().get_underline(), xlnt::font::underline_style::single);
// gradient fill
auto e21 = ws.get_cell("E21");
TS_ASSERT_EQUALS(e21.get_fill().get_type(), xlnt::fill::type::gradient);
TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_type(), xlnt::gradient_fill::type::linear);
TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().size(), 2);
TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().at(0).get_rgb().get_hex_string(), "ffff0000");
TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().at(1).get_rgb().get_hex_string(), "ff0000ff");
}
};

View File

@ -1,365 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <detail/constants.hpp>
class test_style_writer : public CxxTest::TestSuite
{
public:
bool style_xml_matches(const std::string &expected_string, xlnt::workbook &wb)
{
std::vector<std::uint8_t> bytes;
wb.save(bytes);
xlnt::zip_file archive;
archive.load(bytes);
pugi::xml_document observed;
observed.load(archive.read(xlnt::constants::part_styles()).c_str());
return xml_helper::string_matches_document(expected_string, observed);
}
void test_write_custom_number_format()
{
xlnt::workbook wb;
wb.get_active_sheet().get_cell("A1").set_number_format(xlnt::number_format("YYYY"));
auto expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <numFmts count=\"1\">"
" <numFmt formatCode=\"YYYY\" numFmtId=\"164\"></numFmt>"
" </numFmts>"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"2\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"2\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
"</styleSheet>";
TS_ASSERT(style_xml_matches(expected, wb));
}
void test_simple_styles()
{
xlnt::workbook wb;
wb.set_guess_types(true);
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("12.34%");
auto now = xlnt::date::today();
ws.get_cell("A2").set_value(now);
ws.get_cell("A3").set_value("This is a test");
ws.get_cell("A4").set_value("31.31415");
ws.get_cell("A5");
ws.get_cell("D9").set_number_format(xlnt::number_format::number_00());
xlnt::protection locked(true, false);
ws.get_cell("D9").set_protection(locked);
xlnt::protection hidden(true, true);
ws.get_cell("E1").set_protection(hidden);
xlnt::border b;
xlnt::border::border_property prop;
prop.set_style(xlnt::border_style::dashdot);
prop.set_color(xlnt::rgb_color("ffff0000"));
b.set_side(xlnt::border::side::top, prop);
ws.get_cell("D10").set_border(b);
auto expected = path_helper::get_data_directory("writer/expected/simple-styles.xml");
TS_ASSERT(style_xml_matches(expected.read_contents(), wb));
}
void test_empty_workbook()
{
xlnt::workbook wb;
std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"2\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
"</styleSheet>";
TS_ASSERT(style_xml_matches(expected, wb));
}
void test_complex_font()
{
xlnt::font f;
f.set_bold(true);
f.set_color(xlnt::color::red());
f.set_family(3);
f.set_italic(true);
f.set_name("Consolas");
f.set_scheme("major");
f.set_size(21);
f.set_strikethrough(true);
f.set_underline(xlnt::font::underline_style::double_accounting);
xlnt::workbook wb;
wb.get_active_sheet().get_cell("A1").set_font(f);
std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"2\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" <font>"
" <b val=\"1\"/>"
" <i val=\"1\"/>"
" <u val=\"double-accounting\"/>"
" <strike val=\"1\"/>"
" <sz val=\"21\"/>"
" <color rgb=\"ffff0000\"/>"
" <name val=\"Consolas\"/>"
" <family val=\"3\"/>"
" <scheme val=\"major\"/>"
" </font>"
" </fonts>"
" <fills count=\"2\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"2\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
"</styleSheet>";
TS_ASSERT(style_xml_matches(expected, wb));
}
void test_alignment()
{
xlnt::workbook wb;
xlnt::alignment a;
a.set_horizontal(xlnt::horizontal_alignment::center_continuous);
a.set_vertical(xlnt::vertical_alignment::justify);
a.set_wrap_text(true);
a.set_shrink_to_fit(true);
wb.get_active_sheet().get_cell("A1").set_alignment(a);
std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"2\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"2\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\">"
" <alignment vertical=\"justify\" horizontal=\"center-continuous\" wrapText=\"1\" shrinkToFit=\"1\"/>"
" </xf>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
"</styleSheet>";
TS_ASSERT(style_xml_matches(expected, wb));
}
void test_fill()
{
xlnt::workbook wb;
xlnt::fill pattern_fill = xlnt::fill::pattern(xlnt::pattern_fill::type::solid);
pattern_fill.get_pattern_fill().set_foreground_color(xlnt::color::red());
pattern_fill.get_pattern_fill().set_background_color(xlnt::color::blue());
wb.get_active_sheet().get_cell("A1").set_fill(pattern_fill);
xlnt::fill gradient_fill_linear = xlnt::fill::gradient(xlnt::gradient_fill::type::linear);
gradient_fill_linear.get_gradient_fill().set_degree(90);
wb.get_active_sheet().get_cell("A1").set_fill(gradient_fill_linear);
xlnt::fill gradient_fill_path = xlnt::fill::gradient(xlnt::gradient_fill::type::path);
gradient_fill_path.get_gradient_fill().set_gradient_left(1);
gradient_fill_path.get_gradient_fill().set_gradient_right(2);
gradient_fill_path.get_gradient_fill().set_gradient_top(3);
gradient_fill_path.get_gradient_fill().set_gradient_bottom(4);
gradient_fill_path.get_gradient_fill().add_stop(0, xlnt::color::red());
gradient_fill_path.get_gradient_fill().add_stop(1, xlnt::color::blue());
wb.get_active_sheet().get_cell("A1").set_fill(gradient_fill_path);
std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"5\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"gray125\"/>"
" </fill>"
" <fill>"
" <patternFill patternType=\"solid\">"
" <fgColor rgb=\"ffff0000\"/>"
" <bgColor rgb=\"ff0000ff\"/>"
" </patternFill>"
" </fill>"
" <fill>"
" <gradientFill gradientType=\"linear\" degree=\"90\"/>"
" </fill>"
" <fill>"
" <gradientFill gradientType=\"path\" left=\"1\" right=\"2\" top=\"3\" bottom=\"4\">"
" <stop position=\"0\">"
" <color rgb=\"ff00ff00\"/>"
" </stop>"
" <stop position=\"1\">"
" <color rgb=\"ffffff00\"/>"
" </stop>"
" </gradientFill>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"2\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"2\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"3\" borderId=\"0\" xfId=\"0\"/>"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"4\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
"</styleSheet>";
TS_ASSERT(style_xml_matches(expected, wb));
}
};

View File

@ -9,14 +9,4 @@
class test_theme : public CxxTest::TestSuite class test_theme : public CxxTest::TestSuite
{ {
public:
void test_write_theme()
{
xlnt::workbook wb;
wb.set_theme(xlnt::theme());
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/theme1.xml"),
wb, xlnt::constants::part_theme()));
}
}; };

View File

@ -173,40 +173,6 @@ public:
TS_ASSERT_THROWS(wb.remove_named_range("test_nr2"), std::runtime_error); TS_ASSERT_THROWS(wb.remove_named_range("test_nr2"), std::runtime_error);
} }
void test_write_regular_date()
{
const xlnt::datetime today(2010, 1, 18, 14, 15, 20, 1600);
xlnt::workbook book;
auto sheet = book.get_active_sheet();
sheet.get_cell("A1").set_value(today);
temporary_file temp_file;
book.save(temp_file.get_path());
xlnt::workbook test_book;
test_book.load(temp_file.get_path());
auto test_sheet = test_book.get_active_sheet();
TS_ASSERT_EQUALS(test_sheet.get_cell("A1").get_value<xlnt::datetime>(), today);
}
void test_write_regular_float()
{
long double float_value = 1.0L / 3.0L;
xlnt::workbook book;
auto sheet = book.get_active_sheet();
sheet.get_cell("A1").set_value(float_value);
temporary_file temp_file;
book.save(temp_file.get_path());
xlnt::workbook test_book;
test_book.load(temp_file.get_path());
auto test_sheet = test_book.get_active_sheet();
TS_ASSERT_EQUALS(test_sheet.get_cell("A1").get_value<long double>(), float_value);
}
void test_post_increment_iterator() void test_post_increment_iterator()
{ {
xlnt::workbook wb; xlnt::workbook wb;
@ -262,7 +228,7 @@ public:
xlnt::manifest m; xlnt::manifest m;
TS_ASSERT(!m.has_default_type("xml")); TS_ASSERT(!m.has_default_type("xml"));
TS_ASSERT_THROWS(m.get_default_type("xml"), xlnt::key_not_found); TS_ASSERT_THROWS(m.get_default_type("xml"), xlnt::key_not_found);
TS_ASSERT(!m.has_part(xlnt::path("xl/workbook.xml"))); TS_ASSERT(!m.has_package_relationship(xlnt::relationship::type::office_document));
TS_ASSERT_THROWS(m.get_part_relationships(xlnt::path("xl/workbook.xml")), xlnt::key_not_found); TS_ASSERT_THROWS(m.get_part_relationships(xlnt::path("xl/workbook.xml")), xlnt::key_not_found);
} }

View File

@ -1,327 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/temporary_file.hpp>
#include <helpers/path_helper.hpp>
#include <helpers/xml_helper.hpp>
#include <xlnt/workbook/workbook.hpp>
class test_write : public CxxTest::TestSuite
{
public:
void test_write_empty_workbook()
{
xlnt::workbook wbk;
wbk.get_active_sheet().get_cell("A2").set_value("xlnt");
wbk.get_active_sheet().get_cell("B5").set_value(88);
wbk.get_active_sheet().get_cell("B5").set_number_format(xlnt::number_format::percentage_00());
wbk.save(temp_file.get_path());
if(temp_file.get_path().exists())
{
path_helper::delete_file(temp_file.get_path());
}
TS_ASSERT(!temp_file.get_path().exists());
wb_.save(temp_file.get_path());
TS_ASSERT(temp_file.get_path().exists());
}
void test_write_virtual_workbook()
{
xlnt::workbook old_wb;
std::vector<unsigned char> saved_wb;
old_wb.save(saved_wb);
xlnt::workbook new_wb;
new_wb.load(saved_wb);
TS_ASSERT_EQUALS(old_wb, new_wb);
}
void test_write_workbook_rels()
{
xlnt::workbook wb;
wb.add_shared_string(xlnt::text());
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/workbook.xml.rels"),
wb, xlnt::path("xl/_rels/workbook.xml.rels")));
}
void test_write_workbook()
{
xlnt::workbook wb;
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/workbook.xml"),
wb, xlnt::path("xl/workbook.xml")));
}
void test_write_string_table()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("hello");
ws.get_cell("A2").set_value("world");
ws.get_cell("A3").set_value("nice");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sharedStrings.xml"),
wb, xlnt::path("xl/sharedStrings.xml")));
}
void test_write_worksheet()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_hidden_worksheet()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_page_setup().set_sheet_state(xlnt::sheet_state::hidden);
ws.get_cell("F42").set_value("hello");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_bool()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value(false);
ws.get_cell("F43").set_value(true);
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_bool.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_formula()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F1").set_value(10);
ws.get_cell("F2").set_value(32);
ws.get_cell("F3").set_formula("F1+F2");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_bool.xml"),
wb, xlnt::path("writer/expected/sheet1_formula.xml")));
}
void test_write_height()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F1").set_value(10);
ws.get_row_properties(ws.get_cell("F1").get_row()).height = 30;
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_height.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_hyperlink()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("test");
ws.get_cell("A1").set_hyperlink("http://test.com");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_hyperlink_rels()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("test");
ws.get_cell("A1").set_hyperlink("http://test.com/");
ws.get_cell("A2").set_value("test");
ws.get_cell("A2").set_hyperlink("http://test2.com/");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml.rels"),
wb, xlnt::path("xl/worksheets/_rels/sheet1.xml.rels")));
}
void _test_write_hyperlink_image_rels()
{
TS_SKIP("not implemented");
}
void test_hyperlink_value()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_hyperlink("http://test.com");
TS_ASSERT_EQUALS("http://test.com", ws.get_cell("A1").get_value<std::string>());
ws.get_cell("A1").set_value("test");
TS_ASSERT_EQUALS("test", ws.get_cell("A1").get_value<std::string>());
}
void test_write_auto_filter()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
ws.auto_filter("A1:F1");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_auto_filter.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"),
wb, xlnt::path("xl/workbook.xml")));
}
void test_write_auto_filter_filter_column()
{
}
void test_write_auto_filter_sort_condition()
{
}
void test_freeze_panes_horiz()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
ws.freeze_panes("A4");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_horiz.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_freeze_panes_vert()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
ws.freeze_panes("D1");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_vert.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_freeze_panes_both()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
ws.freeze_panes("D4");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_both.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_long_number()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value(9781231231230LL);
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/long_number.xml"),
wb, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_short_number()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value(1234567890);
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/short_number.xml"),
wb_, xlnt::path("xl/worksheets/sheet1.xml")));
}
void test_write_page_setup()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
auto &page_setup = ws.get_page_setup();
TS_ASSERT(page_setup.is_default());
page_setup.set_break(xlnt::page_break::column);
page_setup.set_fit_to_height(true);
page_setup.set_fit_to_page(true);
page_setup.set_fit_to_width(true);
page_setup.set_horizontal_centered(true);
page_setup.set_orientation(xlnt::orientation::landscape);
page_setup.set_paper_size(xlnt::paper_size::a5);
page_setup.set_scale(4.0);
page_setup.set_sheet_state(xlnt::sheet_state::visible);
page_setup.set_vertical_centered(true);
TS_ASSERT(!page_setup.is_default());
std::vector<std::uint8_t> bytes;
wb.save(bytes);
xlnt::zip_file archive;
archive.load(bytes);
auto worksheet_xml_string = archive.read(xlnt::path("xl/worksheets/sheet1.xml"));
pugi::xml_document worksheet_xml;
worksheet_xml.load(worksheet_xml_string.c_str());
std::string expected =
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
" <sheetPr>"
" <outlinePr summaryBelow=\"1\" summaryRight=\"1\" />"
" <pageSetUpPr fitToPage=\"1\" />"
" </sheetPr>"
" <dimension ref=\"A1:A1\" />"
" <sheetViews>"
" <sheetView workbookViewId=\"0\">"
" <selection activeCell=\"A1\" sqref=\"A1\" />"
" </sheetView>"
" </sheetViews>"
" <sheetFormatPr baseColWidth=\"10\" defaultRowHeight=\"15\" />"
" <sheetData />"
" <printOptions horizontalCentered=\"1\" verticalCentered=\"1\" />"
" <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\" />"
" <pageSetup orientation=\"landscape\" paperSize=\"11\" fitToHeight=\"1\" fitToWidth=\"1\" />"
"</worksheet>";
TS_ASSERT(xml_helper::string_matches_document(expected, worksheet_xml));
}
private:
temporary_file temp_file;
xlnt::workbook wb_;
};

View File

@ -1,219 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <helpers/path_helper.hpp>
#include <xlnt/utils/exceptions.hpp>
#include <xlnt/workbook/workbook.hpp>
class test_write_workbook : public CxxTest::TestSuite
{
public:
void test_write_auto_filter()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("F42").set_value("hello");
ws.auto_filter("A1:F1");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"),
wb, xlnt::path("xl/workbook.xml")));
}
void test_write_hidden_worksheet()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.set_sheet_state(xlnt::sheet_state::hidden);
wb.create_sheet();
std::string expected_string =
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
" <workbookPr/>"
" <bookViews>"
" <workbookView activeTab=\"0\"/>"
" </bookViews>"
" <sheets>"
" <sheet name=\"Sheet\" sheetId=\"1\" state=\"hidden\" r:id=\"rId1\"/>"
" <sheet name=\"Sheet1\" sheetId=\"2\" r:id=\"rId2\"/>"
" </sheets>"
" <definedNames/>"
" <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>"
"</workbook>";
TS_ASSERT(xml_helper::string_matches_workbook_part(expected_string,
wb, xlnt::path("xl/workbook.xml")));
}
void test_write_hidden_single_worksheet()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.set_sheet_state(xlnt::sheet_state::hidden);
std::vector<std::uint8_t> trash;
TS_ASSERT_THROWS(wb.save(trash), xlnt::no_visible_worksheets);
}
void test_write_empty_workbook()
{
xlnt::workbook wb;
temporary_file file;
TS_ASSERT(!file.get_path().exists())
wb.save(file.get_path());
TS_ASSERT(file.get_path().exists());
}
void test_write_virtual_workbook()
{
xlnt::workbook old_wb, new_wb;
std::vector<std::uint8_t> wb_bytes;
old_wb.save(wb_bytes);
new_wb.load(wb_bytes);
// TODO more tests!
}
void test_write_workbook_rels()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("string");
TS_ASSERT(xml_helper::file_matches_workbook_part(
path_helper::get_data_directory("writer/expected/workbook.xml.rels"),
wb, xlnt::path("xl/_rels/workbook.xml.rels")));
}
void test_write_workbook_part()
{
xlnt::workbook wb;
auto filename = path_helper::get_data_directory("writer/expected/workbook.xml");
TS_ASSERT(xml_helper::file_matches_workbook_part(filename, wb, xlnt::path("xl/_rels/workbook.xml.rels")));
}
void _test_write_named_range()
{
/*
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
wb.create_named_range("test_range", ws, "$A$1:$B$5");
xlnt::workbook_serializer serializer(wb);
pugi::xml_document xml;
auto root = xml.root().append_child("root");
serializer.write_named_ranges(root);
std::string expected =
"<root>"
"<s:definedName xmlns:s=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" name=\"test_range\">'Sheet'!$A$1:$B$5</s:definedName>"
"</root>";
TS_ASSERT(xml_helper::string_matches_document(expected, xml));
*/
}
void test_read_workbook_code_name()
{
// with open(tmpl, "rb") as expected:
// TS_ASSERT(read_workbook_code_name(expected.read()) == code_name
}
void test_write_workbook_code_name()
{
xlnt::workbook wb;
wb.set_code_name("MyWB");
std::string expected =
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
" <workbookPr codeName=\"MyWB\"/>"
" <bookViews>"
" <workbookView activeTab=\"0\"/>"
" </bookViews>"
" <sheets>"
" <sheet name=\"Sheet\" sheetId=\"1\" r:id=\"rId1\"/>"
" </sheets>"
" <definedNames/>"
" <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>"
"</workbook>";
TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb, xlnt::path("xl/workbook.xml")));
}
void test_write_root_rels()
{
xlnt::workbook wb;
std::string expected =
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
" <Relationship Id=\"rId1\" Target=\"xl/workbook.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"/>"
" <Relationship Id=\"rId2\" Target=\"docProps/core.xml\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\"/>"
" <Relationship Id=\"rId3\" Target=\"docProps/app.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"/>"
"</Relationships>";
TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb, xlnt::path("_rels/.rels")));
}
void test_write_shared_strings_with_runs()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
auto cell = ws.get_cell("A1");
xlnt::text_run run;
run.set_color("color");
run.set_family(12);
run.set_font("font");
run.set_scheme("scheme");
run.set_size(13);
run.set_string("string");
xlnt::text text;
text.add_run(run);
cell.set_value(text);
std::string expected =
"<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"1\" uniqueCount=\"1\">"
" <si>"
" <r>"
" <rPr>"
" <sz val=\"13\"/>"
" <color rgb=\"color\"/>"
" <rFont val=\"font\"/>"
" <family val=\"12\"/>"
" <scheme val=\"scheme\"/>"
" </rPr>"
" <t>string</t>"
" </r>"
" </si>"
"</sst>";
TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb,
xlnt::constants::part_shared_strings()));
}
void test_write_worksheet_order()
{
auto path = path_helper::get_data_directory("genuine/tab_order.xlsx");
// Load an original workbook produced by Excel
xlnt::workbook wb_src;
wb_src.load(path);
// Save it to a new file, unmodified
temporary_file file;
wb_src.save(file.get_path());
TS_ASSERT(file.get_path().exists());
// Load it again
xlnt::workbook wb_dst;
wb_dst.load(file.get_path());
TS_ASSERT_EQUALS(wb_src.get_sheet_titles(), wb_dst.get_sheet_titles());
}
private:
xlnt::workbook wb_;
};

View File

@ -63,13 +63,22 @@ workbook workbook::minimal()
auto impl = new detail::workbook_impl(); auto impl = new detail::workbook_impl();
workbook wb(impl); workbook wb(impl);
wb.d_->worksheets_.push_back(detail::worksheet_impl(&wb, 1, "Sheet")); wb.d_->manifest_.register_default_type("rels",
"application/vnd.openxmlformats-package.relationships+xml");
wb.d_->manifest_.register_package_part(path("workbook.xml"), wb.d_->manifest_.register_override_type(path("workbook.xml"),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
relationship::type::workbook); wb.d_->manifest_.register_package_relationship(relationship::type::office_document,
wb.d_->manifest_.register_part(path("sheet1.xml"), path("workbook.xml"), path("workbook.xml"), target_mode::internal);
"worksheet", relationship::type::worksheet);
std::string title("Sheet");
wb.d_->worksheets_.push_back(detail::worksheet_impl(&wb, 1, title));
wb.d_->manifest_.register_override_type(path("sheet1.xml"),
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
auto ws_rel = wb.d_->manifest_.register_part_relationship(path("workbook.xml"),
relationship::type::worksheet, path("sheet1.xml"), target_mode::internal);
wb.d_->sheet_title_rel_id_map_[title] = ws_rel;
return wb; return wb;
} }
@ -79,15 +88,52 @@ workbook workbook::empty_excel()
auto impl = new detail::workbook_impl(); auto impl = new detail::workbook_impl();
xlnt::workbook wb(impl); xlnt::workbook wb(impl);
wb.d_->manifest_.register_package_part(path("docProps/core.xml"), wb.d_->manifest_.register_override_type(path("xl/workbook.xml"),
"application/vnd.openxmlformats-package.coreproperties+xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
relationship::type::core_properties); wb.d_->manifest_.register_package_relationship(relationship::type::office_document,
wb.d_->manifest_.register_package_part(path("docProps/app.xml"), path("xl/workbook.xml"), target_mode::internal);
"application/vnd.openxmlformats-officedocument.extended-properties+xml",
relationship::type::extended_properties); wb.d_->manifest_.register_default_type("rels",
wb.d_->manifest_.register_package_part(path("xl/workbook.xml"), "application/vnd.openxmlformats-package.relationships+xml");
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", wb.d_->manifest_.register_default_type("xml", "application/xml");
relationship::type::workbook);
const std::vector<std::uint8_t> thumbnail = {
0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,
0x00,0x60,0x00,0x60,0x00,0x00,0xff,0xe1,0x00,0x16,0x45,0x78,0x69,0x66,
0x00,0x00,0x49,0x49,0x2a,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0xff,0xdb,0x00,0x43,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0xff,0xc0,0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,
0x11,0x01,0x03,0x11,0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,
0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x01,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,
0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xbf,0x80,0x01,0xff,
0xd9
};
wb.set_thumbnail(thumbnail, "jpeg", "image/jpeg");
wb.d_->manifest_.register_override_type(path("docProps/core.xml"),
"application/vnd.openxmlformats-package.coreproperties+xml");
wb.d_->manifest_.register_package_relationship(relationship::type::core_properties,
path("docProps/core.xml"), target_mode::internal);
wb.d_->manifest_.register_override_type(path("docProps/app.xml"),
"application/vnd.openxmlformats-officedocument.extended-properties+xml");
wb.d_->manifest_.register_package_relationship(relationship::type::extended_properties,
path("docProps/app.xml"), target_mode::internal);
wb.set_application("Microsoft Excel"); wb.set_application("Microsoft Excel");
wb.create_sheet(); wb.create_sheet();
@ -236,10 +282,12 @@ worksheet workbook::create_sheet()
d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title)); d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document);
d_->manifest_.register_part(sheet_path, workbook_rel.get_target_uri(), d_->manifest_.register_override_type(sheet_path,
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
relationship::type::worksheet); auto ws_rel = d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(),
relationship::type::worksheet, sheet_path, target_mode::internal);
d_->sheet_title_rel_id_map_[title] = ws_rel;
return worksheet(&d_->worksheets_.back()); return worksheet(&d_->worksheets_.back());
} }
@ -343,24 +391,24 @@ void workbook::load(const path &filename)
consumer.read(filename); consumer.read(filename);
} }
void workbook::save(std::vector<unsigned char> &data) void workbook::save(std::vector<unsigned char> &data) const
{ {
detail::xlsx_producer producer(*this); detail::xlsx_producer producer(*this);
producer.write(data); producer.write(data);
} }
void workbook::save(const std::string &filename) void workbook::save(const std::string &filename) const
{ {
return save(path(filename)); return save(path(filename));
} }
void workbook::save(const path &filename) void workbook::save(const path &filename) const
{ {
detail::xlsx_producer producer(*this); detail::xlsx_producer producer(*this);
producer.write(filename); producer.write(filename);
} }
void workbook::save(std::ostream &stream) void workbook::save(std::ostream &stream) const
{ {
detail::xlsx_producer producer(*this); detail::xlsx_producer producer(*this);
producer.write(stream); producer.write(stream);
@ -563,11 +611,14 @@ const theme &workbook::get_theme() const
void workbook::set_theme(const theme &value) void workbook::set_theme(const theme &value)
{ {
if (!d_->manifest_.has_part(constants::part_theme())) auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document);
if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::theme))
{ {
auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); d_->manifest_.register_override_type(constants::part_theme(),
d_->manifest_.register_part(constants::part_theme(), workbook_rel.get_target_uri(), "application/vnd.openxmlformats-officedocument.theme+xml");
"theme", relationship::type::theme); d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(),
relationship::type::theme, constants::part_theme(), target_mode::internal);
} }
d_->has_theme_ = true; d_->has_theme_ = true;
@ -591,12 +642,14 @@ std::vector<named_range> workbook::get_named_ranges() const
std::size_t workbook::add_format(const format &to_add) std::size_t workbook::add_format(const format &to_add)
{ {
if (!d_->manifest_.has_part(constants::part_styles())) auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document);
if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles))
{ {
auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); d_->manifest_.register_override_type(constants::part_styles(),
d_->manifest_.register_part(constants::part_styles(), workbook_rel.get_target_uri(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(),
relationship::type::styles); relationship::type::styles, constants::part_styles(), target_mode::internal);
} }
return d_->stylesheet_.add_format(to_add); return d_->stylesheet_.add_format(to_add);
@ -604,12 +657,14 @@ std::size_t workbook::add_format(const format &to_add)
std::size_t workbook::add_style(const style &to_add) std::size_t workbook::add_style(const style &to_add)
{ {
if (!d_->manifest_.has_part(constants::part_styles())) auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document);
if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles))
{ {
auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); d_->manifest_.register_override_type(constants::part_styles(),
d_->manifest_.register_part(constants::part_styles(), workbook_rel.get_target_uri(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(),
relationship::type::styles); relationship::type::styles, constants::part_styles(), target_mode::internal);
} }
return d_->stylesheet_.add_style(to_add); return d_->stylesheet_.add_style(to_add);
@ -686,12 +741,14 @@ const std::vector<text> &workbook::get_shared_strings() const
void workbook::add_shared_string(const text &shared, bool allow_duplicates) void workbook::add_shared_string(const text &shared, bool allow_duplicates)
{ {
if (!d_->manifest_.has_part(constants::part_shared_strings())) auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document);
if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::shared_string_table))
{ {
auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); d_->manifest_.register_override_type(constants::part_shared_strings(),
d_->manifest_.register_part(constants::part_shared_strings(), workbook_rel.get_target_uri(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(),
relationship::type::shared_string_table); relationship::type::shared_string_table, constants::part_shared_strings(), target_mode::internal);
} }
if (!allow_duplicates) if (!allow_duplicates)
@ -716,8 +773,13 @@ bool workbook::contains(const std::string &sheet_title) const
return false; return false;
} }
void workbook::set_thumbnail(const std::vector<std::uint8_t> &thumbnail) void workbook::set_thumbnail(const std::vector<std::uint8_t> &thumbnail,
const std::string &extension, const std::string &content_type)
{ {
d_->manifest_.register_default_type(extension, content_type);
d_->manifest_.register_package_relationship(relationship::type::thumbnail,
path("docProps/thumbnail.jpeg"), target_mode::internal);
d_->thumbnail_.assign(thumbnail.begin(), thumbnail.end()); d_->thumbnail_.assign(thumbnail.begin(), thumbnail.end());
} }

View File

@ -521,46 +521,31 @@ public:
{ {
xlnt::workbook wb; xlnt::workbook wb;
{
wb.clear();
auto ws = wb.create_sheet(); auto ws = wb.create_sheet();
ws.add_print_title(3); ws.add_print_title(3);
TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:3"); TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:3");
}
{ auto ws2 = wb.create_sheet();
wb.clear(); ws2.add_print_title(4, "cols");
auto ws = wb.create_sheet(); TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:D");
ws.add_print_title(4, "cols");
TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!A:D");
}
} }
void test_print_titles_new() void test_print_titles_new()
{ {
xlnt::workbook wb; xlnt::workbook wb;
{
wb.clear();
auto ws = wb.create_sheet(); auto ws = wb.create_sheet();
ws.set_print_title_rows("1:4"); ws.set_print_title_rows("1:4");
TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:4"); TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:4");
}
{ auto ws2 = wb.create_sheet();
wb.clear(); ws2.set_print_title_cols("A:F");
auto ws = wb.create_sheet(); TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:F");
ws.set_print_title_cols("A:F");
TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!A:F");
}
{ auto ws3 = wb.create_sheet();
wb.clear(); ws3.set_print_title_rows("1:2");
auto ws = wb.create_sheet(); ws3.set_print_title_cols("C:D");
ws.set_print_title_rows("1:2"); TS_ASSERT_EQUALS(ws3.get_print_titles(), "Sheet3!1:2,Sheet3!C:D");
ws.set_print_title_cols("C:D");
TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:2,Sheet!C:D");
}
} }
void test_print_area() void test_print_area()

View File

@ -1 +0,0 @@
not-empty

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,2 +0,0 @@
<?xml version="1.0" ?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="3" uniqueCount="2"><si><t>This is cell A1 in Sheet 1</t></si><si><t>This is cell G5</t></si></sst>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" ?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<numFmts count="1">
<numFmt numFmtId="164" formatCode="yyyy-mm-dd"/>
</numFmts>
<fonts count="1">
<font>
<name val="Calibri"/>
<family val="2"/>
<color theme="1"/>
<sz val="11"/>
<scheme val="minor"/>
</font>
</fonts>
<fills count="2">
<fill>
<patternFill/>
</fill>
<fill>
<patternFill patternType="gray125"/>
</fill>
</fills>
<borders count="2">
<border>
<left/>
<right/>
<top/>
<bottom/>
<diagonal/>
</border>
<border>
<left/>
<right/>
<top style="dashdot">
<color rgb="ffff0000"/>
</top>
<bottom/>
<diagonal/>
</border>
</borders>
<cellStyleXfs count="1">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
</cellStyleXfs>
<cellXfs count="6">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" quotePrefix="0" pivotButton="0" xfId="0"/>
<xf borderId="0" fillId="0" fontId="0" numFmtId="9" quotePrefix="0" pivotButton="0" xfId="0"/>
<xf borderId="0" fillId="0" fontId="0" numFmtId="164" quotePrefix="0" pivotButton="0" xfId="0"/>
<xf borderId="0" fillId="0" fontId="0" numFmtId="2" quotePrefix="0" pivotButton="0" xfId="0"/>
<xf applyProtection="1" borderId="0" fillId="0" fontId="0" quotePrefix="0" pivotButton="0" numFmtId="0" xfId="0">
<protection hidden="1" locked="1"/>
</xf>
<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyBorder="1" xfId="0" />
</cellXfs>
<cellStyles count="1">
<cellStyle name="Normal" hidden="0" xfId="0" builtinId="0"/>
</cellStyles>
<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
</styleSheet>

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More