begin refactoring writing code

This commit is contained in:
Thomas Fussell 2015-10-14 00:03:48 -04:00
parent 7316e2184c
commit 6b3781d03b
27 changed files with 818 additions and 487 deletions

View File

@ -119,4 +119,12 @@ public:
attribute_error();
};
class value_error : public std::runtime_error
{
public:
value_error() : std::runtime_error("")
{
}
};
} // namespace xlnt

View File

@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <vector>
namespace xlnt {
class worksheet;
std::vector<std::pair<std::string, std::string>> split_named_range(const std::string &named_range_string);
class named_range
{
public:
named_range(const std::string &name, const std::vector<std::pair<worksheet, std::string>> &targets);
};
}

View File

@ -46,6 +46,7 @@ class pattern_fill;
class font;
class protection;
class color;
class named_range;
enum class encoding;
@ -174,8 +175,19 @@ public:
bool load(const std::istream &stream);
bool operator==(const workbook &rhs) const;
bool operator!=(const workbook &rhs) const
{
return !(*this == rhs);
}
bool operator==(std::nullptr_t) const;
bool operator!=(std::nullptr_t) const
{
return !(*this == std::nullptr_t{});
}
std::vector<content_type> get_content_types() const;
void create_relationship(const std::string &id, const std::string &target, relationship::type type);
@ -190,6 +202,13 @@ public:
void add_color(color c);
void add_number_format(const std::string &format);
void set_code_name(const std::string &code_name);
void add_named_range(const named_range &n);
bool has_loaded_theme();
std::string get_loaded_theme();
private:
static std::size_t index_from_ws_filename(const std::string &ws_filename);

View File

@ -64,12 +64,12 @@ struct page_setup
public:
page_setup() : default_(true), break_(page_break::none), sheet_state_(sheet_state::visible), paper_size_(paper_size::letter),
orientation_(orientation::portrait), fit_to_page_(false), fit_to_height_(false), fit_to_width_(false) {}
orientation_(orientation::portrait), fit_to_page_(false), fit_to_height_(false), fit_to_width_(false), horizontal_centered_(false), vertical_centered_(false), scale_(1) {}
bool is_default() const { return default_; }
page_break get_break() const { return break_; }
void set_break(page_break b) { default_ = false; break_ = b; }
sheet_state get_sheet_state() const { return sheet_state_; }
void set_sheet_state(sheet_state sheet_state) { default_ = false; sheet_state_ = sheet_state; }
void set_sheet_state(sheet_state sheet_state) { sheet_state_ = sheet_state; }
paper_size get_paper_size() const { return paper_size_; }
void set_paper_size(paper_size paper_size) { default_ = false; paper_size_ = paper_size; }
orientation get_orientation() const { return orientation_; }
@ -80,6 +80,12 @@ public:
void set_fit_to_height(bool fit_to_height) { default_ = false; fit_to_height_ = fit_to_height; }
bool fit_to_width() const { return fit_to_width_; }
void set_fit_to_width(bool fit_to_width) { default_ = false; fit_to_width_ = fit_to_width; }
void set_horizontal_centered(bool horizontal_centered) { default_ = false; horizontal_centered_ = horizontal_centered; }
bool get_horizontal_centered() const { return horizontal_centered_; }
void set_vertical_centered(bool vertical_centered) { default_ = false; vertical_centered_ = vertical_centered; }
bool get_vertical_centered() const { return vertical_centered_; }
void set_scale(double scale) { default_ = false; scale_ = scale; }
double get_scale() const { return scale_; }
private:
bool default_;
@ -90,6 +96,9 @@ private:
bool fit_to_page_;
bool fit_to_height_;
bool fit_to_width_;
bool horizontal_centered_;
bool vertical_centered_;
double scale_;
};
} // namespace xlnt

View File

@ -29,6 +29,7 @@
#include <unordered_map>
#include <vector>
#include "page_setup.hpp"
#include "../common/types.hpp"
#include "../common/relationship.hpp"
@ -116,82 +117,6 @@ private:
footer left_footer_, right_footer_, center_footer_;
};
struct page_setup
{
enum class page_break
{
none = 0,
row = 1,
column = 2
};
enum class sheet_state
{
visible,
hidden,
very_hidden
};
enum class paper_size
{
letter = 1,
letter_small = 2,
tabloid = 3,
ledger = 4,
legal = 5,
statement = 6,
executive = 7,
a3 = 8,
a4 = 9,
a4_small = 10,
a5 = 11
};
enum class orientation
{
portrait,
landscape
};
public:
page_setup() : default_(true), break_(page_break::none), sheet_state_(sheet_state::visible), paper_size_(paper_size::letter),
orientation_(orientation::portrait), fit_to_page_(false), fit_to_height_(false), fit_to_width_(false), horizontal_centered_(false), vertical_centered_(false), scale_(1) {}
bool is_default() const { return default_; }
page_break get_break() const { return break_; }
void set_break(page_break b) { default_ = false; break_ = b; }
sheet_state get_sheet_state() const { return sheet_state_; }
void set_sheet_state(sheet_state sheet_state) { sheet_state_ = sheet_state; }
paper_size get_paper_size() const { return paper_size_; }
void set_paper_size(paper_size paper_size) { default_ = false; paper_size_ = paper_size; }
orientation get_orientation() const { return orientation_; }
void set_orientation(orientation orientation) { default_ = false; orientation_ = orientation; }
bool fit_to_page() const { return fit_to_page_; }
void set_fit_to_page(bool fit_to_page) { default_ = false; fit_to_page_ = fit_to_page; }
bool fit_to_height() const { return fit_to_height_; }
void set_fit_to_height(bool fit_to_height) { default_ = false; fit_to_height_ = fit_to_height; }
bool fit_to_width() const { return fit_to_width_; }
void set_fit_to_width(bool fit_to_width) { default_ = false; fit_to_width_ = fit_to_width; }
void set_horizontal_centered(bool horizontal_centered) { default_ = false; horizontal_centered_ = horizontal_centered; }
bool get_horizontal_centered() const { return horizontal_centered_; }
void set_vertical_centered(bool vertical_centered) { default_ = false; vertical_centered_ = vertical_centered; }
bool get_vertical_centered() const { return vertical_centered_; }
void set_scale(double scale) { default_ = false; scale_ = scale; }
double get_scale() const { return scale_; }
private:
bool default_;
page_break break_;
sheet_state sheet_state_;
paper_size paper_size_;
orientation orientation_;
bool fit_to_page_;
bool fit_to_height_;
bool fit_to_width_;
bool horizontal_centered_;
bool vertical_centered_;
double scale_;
};
struct margins
{
public:
@ -348,6 +273,8 @@ public:
std::vector<std::string> get_formula_attributes() const;
void set_sheet_state(page_setup::sheet_state state);
private:
friend class workbook;
friend class cell;

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
namespace xlnt {
class workbook;
std::string write_content_types(const workbook &wb, bool as_template);
};

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <vector>
namespace xlnt {
class relationship;
std::string write_relationships(const std::vector<relationship> &relationships, const std::string &dir);
} // namespace xlnt

View File

@ -23,11 +23,11 @@
// @author: see AUTHORS file
#pragma once
#include <string>
namespace xlnt {
class theme_writer
{
};
std::string write_theme();
//void write_theme(const theme &t);
} // namespace xlnt

View File

@ -23,11 +23,40 @@
// @author: see AUTHORS file
#pragma once
#include <string>
#include <vector>
#include <xlnt/writer/style_writer.hpp>
namespace xlnt {
class workbook_writer
{
class workbook;
class zip_file;
class excel_writer
{
public:
excel_writer(workbook &wb);
void save(const std::string &filename, bool as_template);
void write_data(zip_file &archive, bool as_template);
void write_string_table(zip_file &archive);
void write_images(zip_file &archive);
void write_charts(zip_file &archive);
void write_chartsheets(zip_file &archive);
void write_worksheets(zip_file &archive);
void write_external_links(zip_file &archive);
private:
workbook wb_;
style_writer style_writer_;
};
std::string write_properties_app(const workbook &wb);
std::string write_root_rels(const workbook &wb);
std::string write_workbook(const workbook &wb);
std::string write_workbook_rels(const workbook &wb);
bool save_workbook(workbook &wb, const std::string &filename, bool as_template = false);
std::vector<std::uint8_t> save_virtual_workbook(xlnt::workbook &wb, bool as_template = false);
} // namespace xlnt

View File

@ -1,33 +1,2 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
namespace xlnt {
class worksheet_writer
{
};
} // namespace xlnt

View File

@ -38,13 +38,9 @@ class document_properties;
class writer
{
public:
static std::string write_content_types(const workbook &wb);
static std::string write_content_types(const workbook &wb);
static std::string write_properties_core(const document_properties &prop);
static std::string write_properties_app(const workbook &wb);
static std::string write_workbook(const workbook &wb);
static std::string write_properties_core(const document_properties &prop);
static std::string write_theme();
@ -54,14 +50,7 @@ public:
const std::vector<std::string> &string_table = {},
const std::unordered_map<std::size_t, std::string> &style_table = {});
static std::string write_root_rels();
static std::string write_workbook_rels(const workbook &wb);
static std::string write_worksheet_rels(worksheet ws);
private:
static std::string write_relationships(const std::vector<relationship> &relationships, const std::string &dir = "");
};
} // namespace xlnt

View File

@ -50,3 +50,4 @@ const std::string download_url = "https://github.com/tfussell/xlnt/archive/maste
#include "workbook/document_properties.hpp"
#include "cell/comment.hpp"
#include "common/encoding.hpp"
#include "workbook/named_range.hpp"

View File

@ -102,13 +102,27 @@ std::pair<std::string, row_t> cell_reference::split_reference(const std::string
throw cell_coordinates_exception(reference_string);
}
}
else if(character == '$')
{
if(column_part)
{
if(column_string.empty())
{
column_string.append(1, upper);
}
else
{
column_part = false;
}
}
}
else
{
if(column_part)
{
column_part = false;
}
else if(!(std::isdigit(character, std::locale::classic()) || character == '$'))
else if(!std::isdigit(character, std::locale::classic()))
{
throw cell_coordinates_exception(reference_string);
}

View File

@ -0,0 +1,10 @@
#include <xlnt/writer/manifest_writer.hpp>
namespace xlnt {
std::string write_content_types(const workbook &wb, bool as_template)
{
return "";
}
} // namespace xlnt

73
source/named_range.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <xlnt/workbook/named_range.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
namespace {
/// <summary>
/// Return a vector containing string split at each delim.
/// </summary>
/// <remark>
/// This should maybe be in a utility header so it can be used elsewhere.
/// </remarks>
std::vector<std::string> split_string(const std::string &string, char delim)
{
std::vector<std::string> split;
std::string::size_type previous_index = 0;
auto separator_index = string.find(delim);
while(separator_index != std::string::npos)
{
auto part = string.substr(previous_index, separator_index - previous_index);
split.push_back(part);
previous_index = separator_index + 1;
separator_index = string.find(delim, previous_index);
}
split.push_back(string.substr(previous_index));
return split;
}
} // namespace
namespace xlnt {
std::vector<std::pair<std::string, std::string>> split_named_range(const std::string &named_range_string)
{
std::vector<std::pair<std::string, std::string>> final;
for(auto part : split_string(named_range_string, ','))
{
auto split = split_string(part, '!');
if(split[0].front() == '\'' && split[0].back() == '\'')
{
split[0] = split[0].substr(1, split[0].length() - 2);
}
// Try to parse it. Use empty string if it's not a valid range.
try
{
xlnt::range_reference ref(split[1]);
}
catch(xlnt::cell_coordinates_exception)
{
split[1] = "";
}
final.push_back({split[0], split[1]});
}
return final;
}
named_range::named_range(const std::string &name, const std::vector<std::pair<worksheet, std::string>> &targets)
{
}
} // namespace xlnt

View File

@ -0,0 +1,45 @@
#include <sstream>
#include <xlnt/common/relationship.hpp>
#include <xlnt/writer/relationship_writer.hpp>
#include "constants.hpp"
#include "detail/include_pugixml.hpp"
namespace xlnt {
std::string write_relationships(const std::vector<relationship> &relationships, const std::string &dir)
{
pugi::xml_document doc;
auto root_node = doc.append_child("Relationships");
root_node.append_attribute("xmlns").set_value(constants::Namespaces.at("relationships").c_str());
for(auto relationship : relationships)
{
auto target = relationship.get_target_uri();
if (dir != "" && target.substr(0, dir.size()) == dir)
{
target = target.substr(dir.size());
}
auto app_props_node = root_node.append_child("Relationship");
app_props_node.append_attribute("Id").set_value(relationship.get_id().c_str());
app_props_node.append_attribute("Target").set_value(target.c_str());
app_props_node.append_attribute("Type").set_value(relationship.get_type_string().c_str());
if(relationship.get_target_mode() == target_mode::external)
{
app_props_node.append_attribute("TargetMode").set_value("External");
}
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
}

View File

@ -8,17 +8,18 @@
#include <Windows.h>
#endif
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/drawing/drawing.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/reader/reader.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/writer.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/drawing/drawing.hpp>
#include <xlnt/reader/reader.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/style_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include <xlnt/writer/writer.hpp>
#include "detail/cell_impl.hpp"
#include "detail/include_pugixml.hpp"
@ -564,7 +565,7 @@ bool workbook::save(const std::string &filename)
f.writestr("[Content_Types].xml", writer::write_content_types(*this));
f.writestr("docProps/app.xml", writer::write_properties_app(*this));
f.writestr("docProps/app.xml", write_properties_app(*this));
f.writestr("docProps/core.xml", writer::write_properties_core(get_properties()));
std::set<std::string> shared_strings_set;
@ -589,10 +590,10 @@ bool workbook::save(const std::string &filename)
f.writestr("xl/theme/theme1.xml", writer::write_theme());
f.writestr("xl/styles.xml", style_writer(*this).write_table());
f.writestr("_rels/.rels", writer::write_root_rels());
f.writestr("xl/_rels/workbook.xml.rels", writer::write_workbook_rels(*this));
f.writestr("_rels/.rels", write_root_rels(*this));
f.writestr("xl/_rels/workbook.xml.rels", write_workbook_rels(*this));
f.writestr("xl/workbook.xml", writer::write_workbook(*this));
f.writestr("xl/workbook.xml", write_workbook(*this));
for(auto relationship : d_->relationships_)
{
@ -743,4 +744,23 @@ std::size_t workbook::index_from_ws_filename(const std::string &ws_filename)
}
void workbook::set_code_name(const std::string &code_name)
{
}
void workbook::add_named_range(const xlnt::named_range &n)
{
}
bool workbook::has_loaded_theme()
{
return false;
}
std::string workbook::get_loaded_theme()
{
return "";
}
}

285
source/workbook_writer.cpp Normal file
View File

@ -0,0 +1,285 @@
#include <sstream>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/manifest_writer.hpp>
#include <xlnt/writer/relationship_writer.hpp>
#include <xlnt/writer/theme_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "constants.hpp"
#include "detail/include_pugixml.hpp"
namespace {
std::string to_xml(xlnt::document_properties &props)
{
return "";
}
} // namespace
namespace xlnt {
excel_writer::excel_writer(workbook &wb) : wb_(wb), style_writer_(wb_)
{
}
void excel_writer::save(const std::string &filename, bool as_template)
{
zip_file archive;
write_data(archive, as_template);
archive.save(filename);
}
void excel_writer::write_data(zip_file &archive, bool as_template)
{
archive.writestr(constants::ArcRootRels, write_root_rels(wb_));
archive.writestr(constants::ArcWorkbookRels, write_workbook_rels(wb_));
archive.writestr(constants::ArcApp, write_properties_app(wb_));
archive.writestr(constants::ArcCore, to_xml(wb_.get_properties()));
if(wb_.has_loaded_theme())
{
archive.writestr(constants::ArcTheme, wb_.get_loaded_theme());
}
else
{
archive.writestr(constants::ArcTheme, write_theme());
}
archive.writestr(constants::ArcWorkbook, write_workbook(wb_));
/*
if(wb_.has_vba_archive())
{
auto &vba_archive = wb_.get_vba_archive();
for(auto name : vba_archive.namelist())
{
for(auto s : constants::ArcVba)
{
if(match(s, name))
{
archive.writestr(name, vba_archive.read(name));
break;
}
}
}
}
*/
write_charts(archive);
write_images(archive);
write_worksheets(archive);
write_chartsheets(archive);
write_string_table(archive);
write_external_links(archive);
archive.writestr(constants::ArcStyles, style_writer_.write_table());
auto manifest = write_content_types(wb_, as_template);
archive.writestr(constants::ArcContentTypes, manifest);
}
void excel_writer::write_string_table(zip_file &archive)
{
}
void excel_writer::write_images(zip_file &archive)
{
}
void excel_writer::write_charts(zip_file &archive)
{
}
void excel_writer::write_chartsheets(zip_file &archive)
{
}
void excel_writer::write_worksheets(zip_file &archive)
{
}
void excel_writer::write_external_links(zip_file &archive)
{
}
std::string write_properties_app(const workbook &wb)
{
pugi::xml_document doc;
auto root_node = doc.append_child("Properties");
root_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");
root_node.append_child("Application").text().set("Microsoft Excel");
root_node.append_child("DocSecurity").text().set("0");
root_node.append_child("ScaleCrop").text().set("false");
root_node.append_child("Company");
root_node.append_child("LinksUpToDate").text().set("false");
root_node.append_child("SharedDoc").text().set("false");
root_node.append_child("HyperlinksChanged").text().set("false");
root_node.append_child("AppVersion").text().set("12.0000");
auto heading_pairs_node = root_node.append_child("HeadingPairs");
auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector");
heading_pairs_vector_node.append_attribute("baseType").set_value("variant");
heading_pairs_vector_node.append_attribute("size").set_value("2");
heading_pairs_vector_node.append_child("vt:variant").append_child("vt:lpstr").text().set("Worksheets");
heading_pairs_vector_node.append_child("vt:variant").append_child("vt:i4").text().set(std::to_string(wb.get_sheet_names().size()).c_str());
auto titles_of_parts_node = root_node.append_child("TitlesOfParts");
auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector");
titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr");
titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(wb.get_sheet_names().size()).c_str());
for(auto ws : wb)
{
titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str());
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
std::string write_root_rels(const workbook &)
{
std::vector<relationship> relationships;
relationships.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml"));
relationships.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml"));
relationships.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml"));
return write_relationships(relationships, "");
}
std::string write_workbook(const workbook &wb)
{
std::size_t num_visible = 0;
for(auto ws : wb)
{
if(ws.get_page_setup().get_sheet_state() == xlnt::page_setup::sheet_state::visible)
{
num_visible++;
}
}
if(num_visible == 0)
{
throw value_error();
}
pugi::xml_document doc;
auto root_node = doc.append_child("workbook");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto file_version_node = root_node.append_child("fileVersion");
file_version_node.append_attribute("appName").set_value("xl");
file_version_node.append_attribute("lastEdited").set_value("4");
file_version_node.append_attribute("lowestEdited").set_value("4");
file_version_node.append_attribute("rupBuild").set_value("4505");
auto workbook_pr_node = root_node.append_child("workbookPr");
workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook");
workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226");
auto book_views_node = root_node.append_child("bookViews");
auto workbook_view_node = book_views_node.append_child("workbookView");
workbook_view_node.append_attribute("activeTab").set_value("0");
workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1");
workbook_view_node.append_attribute("firstSheet").set_value("0");
workbook_view_node.append_attribute("minimized").set_value("0");
workbook_view_node.append_attribute("showHorizontalScroll").set_value("1");
workbook_view_node.append_attribute("showSheetTabs").set_value("1");
workbook_view_node.append_attribute("showVerticalScroll").set_value("1");
workbook_view_node.append_attribute("tabRatio").set_value("600");
workbook_view_node.append_attribute("visibility").set_value("visible");
auto sheets_node = root_node.append_child("sheets");
auto defined_names_node = root_node.append_child("definedNames");
for(auto relationship : wb.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet)
{
std::string sheet_index_string = relationship.get_target_uri();
sheet_index_string = sheet_index_string.substr(0, sheet_index_string.find('.'));
sheet_index_string = sheet_index_string.substr(sheet_index_string.find_last_of('/'));
auto iter = sheet_index_string.end();
iter--;
while (isdigit(*iter)) iter--;
auto first_digit = iter - sheet_index_string.begin();
sheet_index_string = sheet_index_string.substr(first_digit + 1);
std::size_t sheet_index = std::stoi(sheet_index_string) - 1;
auto ws = wb.get_sheet_by_index(sheet_index);
auto sheet_node = sheets_node.append_child("sheet");
sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
sheet_node.append_attribute("r:id").set_value(relationship.get_id().c_str());
sheet_node.append_attribute("sheetId").set_value(std::to_string(sheet_index + 1).c_str());
if(ws.has_auto_filter())
{
auto defined_name_node = defined_names_node.append_child("definedName");
defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase");
defined_name_node.append_attribute("hidden").set_value(1);
defined_name_node.append_attribute("localSheetId").set_value(0);
std::string name = "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string();
defined_name_node.text().set(name.c_str());
}
}
}
auto calc_pr_node = root_node.append_child("calcPr");
calc_pr_node.append_attribute("calcId").set_value("124519");
calc_pr_node.append_attribute("calcMode").set_value("auto");
calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1");
std::stringstream ss;
doc.save(ss);
return ss.str();
}
std::string write_workbook_rels(const workbook &wb)
{
return write_relationships(wb.get_relationships(), "xl/");
}
std::string write_theme()
{
return "";
}
bool save_workbook(workbook &wb, const std::string &filename, bool as_template)
{
excel_writer writer(wb);
writer.save(filename, as_template);
return true;
}
std::vector<std::uint8_t> save_virtual_workbook(xlnt::workbook &wb, bool as_template)
{
excel_writer writer(wb);
std::vector<std::uint8_t> buffer;
zip_file archive(buffer);
writer.write_data(archive, as_template);
return buffer;
}
} // namespace xlnt

View File

@ -700,4 +700,9 @@ cell_reference worksheet::get_point_pos(const std::pair<int, int> &point) const
return get_point_pos(point.first, point.second);
}
void worksheet::set_sheet_state(page_setup::sheet_state state)
{
get_page_setup().set_sheet_state(state);
}
} // namespace xlnt

View File

@ -5,25 +5,25 @@
#include <string>
#include <unordered_map>
#include <pugixml.hpp>
#include <xlnt/writer/writer.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/writer/relationship_writer.hpp>
#include <xlnt/writer/writer.hpp>
#include "constants.hpp"
#include "detail/include_pugixml.hpp"
namespace {
bool is_integral(long double d)
{
return d == static_cast<long long int>(d);
}
{
return d == static_cast<long long int>(d);
}
} // namespace
@ -90,129 +90,9 @@ std::string writer::write_properties_core(const document_properties &prop)
return ss.str();
}
std::string writer::write_properties_app(const workbook &wb)
{
pugi::xml_document doc;
auto root_node = doc.append_child("Properties");
root_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");
root_node.append_child("Application").text().set("Microsoft Excel");
root_node.append_child("DocSecurity").text().set("0");
root_node.append_child("ScaleCrop").text().set("false");
root_node.append_child("Company");
root_node.append_child("LinksUpToDate").text().set("false");
root_node.append_child("SharedDoc").text().set("false");
root_node.append_child("HyperlinksChanged").text().set("false");
root_node.append_child("AppVersion").text().set("12.0000");
auto heading_pairs_node = root_node.append_child("HeadingPairs");
auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector");
heading_pairs_vector_node.append_attribute("baseType").set_value("variant");
heading_pairs_vector_node.append_attribute("size").set_value("2");
heading_pairs_vector_node.append_child("vt:variant").append_child("vt:lpstr").text().set("Worksheets");
heading_pairs_vector_node.append_child("vt:variant").append_child("vt:i4").text().set(std::to_string(wb.get_sheet_names().size()).c_str());
auto titles_of_parts_node = root_node.append_child("TitlesOfParts");
auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector");
titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr");
titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(wb.get_sheet_names().size()).c_str());
for(auto ws : wb)
{
titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str());
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
std::string writer::write_workbook_rels(const workbook &wb)
{
return write_relationships(wb.get_relationships(), "xl/");
}
std::string writer::write_worksheet_rels(worksheet ws)
{
return write_relationships(ws.get_relationships());
}
std::string writer::write_workbook(const workbook &wb)
{
pugi::xml_document doc;
auto root_node = doc.append_child("workbook");
root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto file_version_node = root_node.append_child("fileVersion");
file_version_node.append_attribute("appName").set_value("xl");
file_version_node.append_attribute("lastEdited").set_value("4");
file_version_node.append_attribute("lowestEdited").set_value("4");
file_version_node.append_attribute("rupBuild").set_value("4505");
auto workbook_pr_node = root_node.append_child("workbookPr");
workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook");
workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226");
auto book_views_node = root_node.append_child("bookViews");
auto workbook_view_node = book_views_node.append_child("workbookView");
workbook_view_node.append_attribute("activeTab").set_value("0");
workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1");
workbook_view_node.append_attribute("firstSheet").set_value("0");
workbook_view_node.append_attribute("minimized").set_value("0");
workbook_view_node.append_attribute("showHorizontalScroll").set_value("1");
workbook_view_node.append_attribute("showSheetTabs").set_value("1");
workbook_view_node.append_attribute("showVerticalScroll").set_value("1");
workbook_view_node.append_attribute("tabRatio").set_value("600");
workbook_view_node.append_attribute("visibility").set_value("visible");
auto sheets_node = root_node.append_child("sheets");
auto defined_names_node = root_node.append_child("definedNames");
for(auto relationship : wb.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet)
{
std::string sheet_index_string = relationship.get_target_uri();
sheet_index_string = sheet_index_string.substr(0, sheet_index_string.find('.'));
sheet_index_string = sheet_index_string.substr(sheet_index_string.find_last_of('/'));
auto iter = sheet_index_string.end();
iter--;
while (isdigit(*iter)) iter--;
auto first_digit = iter - sheet_index_string.begin();
sheet_index_string = sheet_index_string.substr(first_digit + 1);
std::size_t sheet_index = std::stoi(sheet_index_string) - 1;
auto ws = wb.get_sheet_by_index(sheet_index);
auto sheet_node = sheets_node.append_child("sheet");
sheet_node.append_attribute("name").set_value(ws.get_title().c_str());
sheet_node.append_attribute("r:id").set_value(relationship.get_id().c_str());
sheet_node.append_attribute("sheetId").set_value(std::to_string(sheet_index + 1).c_str());
if(ws.has_auto_filter())
{
auto defined_name_node = defined_names_node.append_child("definedName");
defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase");
defined_name_node.append_attribute("hidden").set_value(1);
defined_name_node.append_attribute("localSheetId").set_value(0);
std::string name = "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string();
defined_name_node.text().set(name.c_str());
}
}
}
auto calc_pr_node = root_node.append_child("calcPr");
calc_pr_node.append_attribute("calcId").set_value("124519");
calc_pr_node.append_attribute("calcMode").set_value("auto");
calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1");
std::stringstream ss;
doc.save(ss);
return ss.str();
return write_relationships(ws.get_relationships(), "");
}
std::string writer::write_worksheet(worksheet ws, const std::vector<std::string> &string_table, const std::unordered_map<std::size_t, std::string> &style_id_by_hash)
@ -571,50 +451,6 @@ std::string writer::write_content_types(const workbook &wb)
return ss.str();
}
std::string xlnt::writer::write_root_rels()
{
std::vector<relationship> relationships;
relationships.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml"));
relationships.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml"));
relationships.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml"));
return write_relationships(relationships);
}
std::string writer::write_relationships(const std::vector<relationship> &relationships, const std::string &dir)
{
pugi::xml_document doc;
auto root_node = doc.append_child("Relationships");
root_node.append_attribute("xmlns").set_value(constants::Namespaces.at("relationships").c_str());
for(auto relationship : relationships)
{
auto target = relationship.get_target_uri();
if (dir != "" && target.substr(0, dir.size()) == dir)
{
target = target.substr(dir.size());
}
auto app_props_node = root_node.append_child("Relationship");
app_props_node.append_attribute("Id").set_value(relationship.get_id().c_str());
app_props_node.append_attribute("Target").set_value(target.c_str());
app_props_node.append_attribute("Type").set_value(relationship.get_type_string().c_str());
if(relationship.get_target_mode() == target_mode::external)
{
app_props_node.append_attribute("TargetMode").set_value("External");
}
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
std::string writer::write_theme()
{
pugi::xml_document doc;

View File

@ -28,6 +28,11 @@ public:
operator bool() const { return difference == difference_type::equivalent; }
};
static comparison_result compare_xml(const std::string &left_contents, const std::string &right_contents)
{
return {difference_type::names_differ,"",""};
}
static comparison_result compare_xml(const pugi::xml_node &left, const pugi::xml_node &right)
{
std::string left_temp = left.name();

View File

@ -2,6 +2,8 @@
#include <array>
#include <fstream>
#include <string>
#include <sstream>
#ifdef __APPLE__
#include <mach-o/dyld.h>
@ -20,6 +22,15 @@
class PathHelper
{
public:
static std::string read_file(const std::string &filename)
{
std::ifstream f(filename);
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
static std::string WindowsToUniversalPath(const std::string &windows_path)
{
std::string fixed;

View File

@ -3,14 +3,38 @@
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include <xlnt/workbook/named_range.hpp>
class test_named_range : public CxxTest::TestSuite
{
public:
void test_split()
{
/*TS_ASSERT_EQUALS([("My Sheet", "$D$8"), ], split_named_range(""My Sheet"!$D$8"))*/
using string_pair = std::pair<std::string, std::string>;
using string_pair_vector = std::vector<string_pair>;
using expected_pair = std::pair<std::string, string_pair_vector>;
std::vector<expected_pair> expected_pairs =
{
{ "'My Sheet'!$D$8", {{ "My Sheet", "$D$8" }} },
{ "Sheet1!$A$1", {{ "Sheet1", "$A$1" }} },
{ "[1]Sheet1!$A$1", {{ "[1]Sheet1", "$A$1" }} },
{ "[1]!B2range", {{ "[1]", "" }} },
{ "Sheet1!$C$5:$C$7,Sheet1!$C$9:$C$11,Sheet1!$E$5:$E$7,Sheet1!$E$9:$E$11,Sheet1!$D$8",
{
{ "Sheet1", "$C$5:$C$7" },
{ "Sheet1", "$C$9:$C$11" },
{ "Sheet1", "$E$5:$E$7" },
{ "Sheet1", "$E$9:$E$11" },
{ "Sheet1", "$D$8" }
}
}
};
for(auto pair : expected_pairs)
{
TS_ASSERT_EQUALS(xlnt::split_named_range(pair.first), pair.second);
}
}
void test_split_no_quotes()

View File

@ -1,165 +0,0 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
class test_number_format : public CxxTest::TestSuite
{
public:
test_number_format()
{
}
void setup_class(int cls)
{
//cls.workbook = Workbook()
// cls.worksheet = Worksheet(cls.workbook, "Test")
// cls.sd = SharedDate()
}
void test_convert_date_to_julian()
{
//TS_ASSERT_EQUALS(40167, sd.to_julian(2009, 12, 20))
}
void test_convert_date_from_julian()
{
}
void test_date_equal(int julian, int datetime)
{
//TS_ASSERT_EQUALS(sd.from_julian(julian), datetime);
//date_pairs = (
// (40167, datetime(2009, 12, 20)),
// (21980, datetime(1960, 3, 5)),
// );
//for count, dt in date_pairs
//{
// yield test_date_equal, count, dt;
//}
}
void test_convert_datetime_to_julian()
{
//TS_ASSERT_EQUALS(40167, sd.datetime_to_julian(datetime(2009, 12, 20)))
// TS_ASSERT_EQUALS(40196.5939815, sd.datetime_to_julian(datetime(2010, 1, 18, 14, 15, 20, 1600)))
}
void test_insert_float()
{
//worksheet.cell("A1").value = 3.14
// TS_ASSERT_EQUALS(Cell.TYPE_NUMERIC, worksheet.cell("A1")._data_type)
}
void test_insert_percentage()
{
//worksheet.cell("A1").value = "3.14%"
// TS_ASSERT_EQUALS(Cell.TYPE_NUMERIC, worksheet.cell("A1")._data_type)
// assert_almost_equal(0.0314, worksheet.cell("A1").value)
}
void test_insert_datetime()
{
//worksheet.cell("A1").value = date.today()
// TS_ASSERT_EQUALS(Cell.TYPE_NUMERIC, worksheet.cell("A1")._data_type)
}
void test_insert_date()
{
//worksheet.cell("A1").value = datetime.now()
// TS_ASSERT_EQUALS(Cell.TYPE_NUMERIC, worksheet.cell("A1")._data_type)
}
void test_internal_date()
{
//dt = datetime(2010, 7, 13, 6, 37, 41)
// worksheet.cell("A3").value = dt
// TS_ASSERT_EQUALS(40372.27616898148, worksheet.cell("A3")._value)
}
void test_datetime_interpretation()
{
//dt = datetime(2010, 7, 13, 6, 37, 41)
// worksheet.cell("A3").value = dt
// TS_ASSERT_EQUALS(dt, worksheet.cell("A3").value)
}
void test_date_interpretation()
{
//dt = date(2010, 7, 13)
// worksheet.cell("A3").value = dt
// TS_ASSERT_EQUALS(datetime(2010, 7, 13, 0, 0), worksheet.cell("A3").value)
}
void test_number_format_style()
{
//worksheet.cell("A1").value = "12.6%"
// TS_ASSERT_EQUALS(NumberFormat.FORMAT_PERCENTAGE, \
// worksheet.cell("A1").style.number_format.format_code)
}
void test_date_format_on_non_date()
{
//cell = worksheet.cell("A1");
}
void check_date_pair(int count, const std::string &date_string)
{
//cell.value = strptime(date_string, "%Y-%m-%d");
//TS_ASSERT_EQUALS(count, cell._value);
//date_pairs = (
// (15, "1900-01-15"),
// (59, "1900-02-28"),
// (61, "1900-03-01"),
// (367, "1901-01-01"),
// (2958465, "9999-12-31"), );
//for count, date_string in date_pairs
//{
// yield check_date_pair, count, date_string;
//}
}
void test_1900_leap_year()
{
//assert_raises(ValueError, sd.from_julian, 60)
// assert_raises(ValueError, sd.to_julian, 1900, 2, 29)
}
void test_bad_date()
{
//void check_bad_date(year, month, day)
//{
// assert_raises(ValueError, sd.to_julian, year, month, day)
//}
//bad_dates = ((1776, 7, 4), (1899, 12, 31), )
// for year, month, day in bad_dates
// {
// yield check_bad_date, year, month, day
// }
}
void test_bad_julian_date()
{
//assert_raises(ValueError, sd.from_julian, -1)
}
void test_mac_date()
{
// sd.excel_base_date = CALENDAR_MAC_1904
// datetuple = (2011, 10, 31)
// dt = date(datetuple[0], datetuple[1], datetuple[2])
// julian = sd.to_julian(datetuple[0], datetuple[1], datetuple[2])
// reverse = sd.from_julian(julian).date()
// TS_ASSERT_EQUALS(dt, reverse)
// sd.excel_base_date = CALENDAR_WINDOWS_1900
}
};

View File

@ -3,7 +3,8 @@
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "helpers/path_helper.hpp"
#include "helpers/helper.hpp"
@ -69,7 +70,7 @@ public:
xlnt::workbook wb;
wb.create_sheet();
wb.create_sheet();
auto content = xlnt::writer::write_properties_app(wb);
auto content = xlnt::write_properties_app(wb);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/app.xml", content));
}
};

View File

@ -41,7 +41,7 @@ public:
void test_write_workbook_rels()
{
xlnt::workbook wb;
auto content = xlnt::writer::write_workbook_rels(wb);
auto content = xlnt::write_workbook_rels(wb);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml.rels", content));
}
@ -49,7 +49,7 @@ public:
void test_write_workbook()
{
xlnt::workbook wb;
auto content = xlnt::writer::write_workbook(wb);
auto content = xlnt::write_workbook(wb);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml", content));
}
@ -163,7 +163,7 @@ public:
auto content = xlnt::writer::write_worksheet(ws, {"hello"}, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_auto_filter.xml", content));
content = xlnt::writer::write_workbook(wb);
content = xlnt::write_workbook(wb);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook_auto_filter.xml", content));
}

View File

@ -0,0 +1,175 @@
#pragma once
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "helpers/path_helper.hpp"
namespace xlnt {
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes)
{
return xlnt::workbook();
}
std::string write_workbook_rels(xlnt::workbook &wb)
{
return "";
}
std::string write_root_rels(xlnt::workbook &wb)
{
return "";
}
std::string write_defined_names(xlnt::workbook &wb)
{
return "";
}
}
class test_write_workbook : public CxxTest::TestSuite
{
public:
void test_write_auto_filter()
{
xlnt::workbook wb;
auto ws = wb.create_sheet();
ws.get_cell("F42").set_value("hello");
ws.get_auto_filter() = "A1:F1";
auto content = xlnt::write_workbook(wb);
auto diff = Helper::compare_xml(PathHelper::read_file("workbook_auto_filter.xml"), content);
TS_ASSERT(!diff);
}
void test_write_hidden_worksheet()
{
xlnt::workbook wb;
auto ws = wb.create_sheet();
ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden);
wb.create_sheet();
auto xml = xlnt::write_workbook(wb);
std::string expected =
"<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>";
auto diff = Helper::compare_xml(xml, expected);
TS_ASSERT(!diff);
}
void test_write_hidden_single_worksheet()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden);
TS_ASSERT_THROWS(xlnt::write_workbook(wb), xlnt::value_error);
}
void test_write_empty_workbook()
{
xlnt::workbook wb;
std::string dest_filename = "empty_book.xlsx";
xlnt::save_workbook(wb, dest_filename);
TS_ASSERT(PathHelper::FileExists(dest_filename));
}
void test_write_virtual_workbook()
{
xlnt::workbook old_wb;
auto saved_wb = xlnt::save_virtual_workbook(old_wb);
auto new_wb = xlnt::load_workbook(saved_wb);
TS_ASSERT(new_wb != nullptr);
}
void test_write_workbook_rels()
{
xlnt::workbook wb;
auto content = xlnt::write_workbook_rels(wb);
auto filename = "workbook.xml.rels";
auto diff = Helper::compare_xml(PathHelper::read_file(filename), content);
TS_ASSERT(!diff);
}
void test_write_workbook_()
{
xlnt::workbook wb;
auto content = xlnt::write_workbook(wb);
auto filename = "workbook.xml";
auto diff = Helper::compare_xml(PathHelper::read_file(filename), content);
TS_ASSERT(!diff);
}
void test_write_named_range()
{
xlnt::workbook wb;
auto ws = wb.create_sheet();
xlnt::named_range xlrange("test_range", {{ws, "A1:B5"}});
wb.add_named_range(xlrange);
auto xml = xlnt::write_defined_names(wb);
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>";
auto diff = Helper::compare_xml(xml, expected);
TS_ASSERT(!diff);
}
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");
auto content = xlnt::write_workbook(wb);
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>";
auto diff = Helper::compare_xml(content, expected);
TS_ASSERT(!diff);
}
void test_write_root_rels()
{
xlnt::workbook wb;
auto xml = xlnt::write_root_rels(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>";
auto diff = Helper::compare_xml(xml, expected);
TS_ASSERT(!diff);
}
private:
xlnt::workbook wb_;
};