start correctly implementing opc

This commit is contained in:
Thomas Fussell 2016-08-03 00:12:18 -04:00
parent f21d4c922c
commit ccc3995709
39 changed files with 2022 additions and 1048 deletions

View File

@ -75,28 +75,6 @@ public:
/// </summary>
static const std::unordered_map<std::string, int> &error_codes();
// TODO: Should it be possible to construct and use a cell without a parent worksheet?
//(cont'd) If so, it would need to be responsible for allocating and deleting its PIMPL.
/// <summary>
/// Construct a null cell without a parent.
/// Most methods will throw an exception if this cell is not further initialized.
/// </summary>
cell();
/// <summary>
/// Construct a cell in worksheet, sheet, at the given reference location (e.g. A1).
/// </summary>
cell(worksheet sheet, const cell_reference &reference);
/// <summary>
/// This constructor, provided for convenience, is equivalent to calling:
/// cell c(sheet, reference);
/// c.set_value(initial_value);
/// </summary>
template <typename T>
cell(worksheet sheet, const cell_reference &reference, const T &initial_value);
// value
/// <summary>

View File

@ -35,14 +35,7 @@ namespace xlnt {
class XLNT_CLASS app_properties
{
public:
std::string application = "libxlnt";
int doc_security = 0;
bool scale_crop = false;
std::string company = "";
bool links_up_to_date = false;
bool shared_doc = false;
bool hyperlinks_changed = false;
std::string app_version = "0.9";
};
} // namespace xlnt

View File

@ -38,17 +38,7 @@ class XLNT_CLASS document_properties
public:
document_properties();
std::string creator;
std::string last_modified_by;
datetime created;
datetime modified;
std::string title = "Untitled";
std::string subject;
std::string description;
std::string keywords;
std::string category;
std::string company;
calendar excel_base_date;
};
} // namespace xlnt

View File

@ -29,6 +29,7 @@
#include <xlnt/xlnt_config.hpp>
#include <xlnt/packaging/default_type.hpp>
#include <xlnt/packaging/override_type.hpp>
#include <xlnt/utils/path.hpp>
namespace xlnt {
@ -39,21 +40,24 @@ namespace xlnt {
class XLNT_CLASS manifest
{
public:
using default_types_container = std::unordered_map<std::string, default_type>;
using override_types_container = std::unordered_map<path, override_type, std::hash<hashable>>;
void clear();
bool has_default_type(const std::string &extension) const;
std::string get_default_type(const std::string &extension) const;
const std::unordered_map<std::string, default_type> &get_default_types() const;
const default_types_container &get_default_types() const;
void add_default_type(const std::string &extension, const std::string &content_type);
bool has_override_type(const std::string &part_name) const;
std::string get_override_type(const std::string &part_name) const;
const std::unordered_map<std::string, override_type> &get_override_types() const;
void add_override_type(const std::string &part_name, const std::string &content_type);
bool has_override_type(const path &part) const;
std::string get_override_type(const path &part) const;
const override_types_container &get_override_types() const;
void add_override_type(const path &part, const std::string &content_type);
private:
std::unordered_map<std::string, default_type> default_types_;
std::unordered_map<std::string, override_type> override_types_;
default_types_container default_types_;
override_types_container override_types_;
};
} // namespace xlnt

View File

@ -26,6 +26,7 @@
#include <string>
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/path.hpp>
namespace xlnt {
@ -37,15 +38,15 @@ class XLNT_CLASS override_type
{
public:
override_type();
override_type(const std::string &extension, const std::string &content_type);
override_type(const path &part, const std::string &content_type);
override_type(const override_type &other);
override_type &operator=(const override_type &other);
std::string get_part_name() const;
path get_part() const;
std::string get_content_type() const;
private:
std::string part_name_;
path part_;
std::string content_type_;
};

View File

@ -30,6 +30,7 @@
#include <vector>
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/path.hpp>
// Note: this comes from https://github.com/tfussell/miniz-cpp
@ -61,7 +62,7 @@ struct XLNT_CLASS zip_info
zip_info();
date_time_t date_time;
std::string filename;
path filename;
std::string comment;
std::string extra;
uint16_t create_system;
@ -85,18 +86,18 @@ class XLNT_CLASS zip_file
{
public:
zip_file();
zip_file(const std::string &filename);
zip_file(const std::vector<unsigned char> &bytes);
zip_file(const path &filename);
zip_file(const std::vector<uint8_t> &bytes);
zip_file(std::istream &stream);
~zip_file();
// to/from file
void load(const std::string &filename);
void save(const std::string &filename);
void load(const path &filename);
void save(const path &filename);
// to/from byte vector
void load(const std::vector<unsigned char> &bytes);
void save(std::vector<unsigned char> &bytes);
void load(const std::vector<std::uint8_t> &bytes);
void save(std::vector<std::uint8_t> &bytes);
// to/from iostream
void load(std::istream &stream);
@ -104,42 +105,29 @@ public:
void reset();
bool has_file(const std::string &name);
bool has_file(const path &name);
bool has_file(const zip_info &name);
zip_info getinfo(const std::string &name);
zip_info getinfo(const path &name);
std::vector<zip_info> infolist();
std::vector<std::string> namelist();
std::vector<path> namelist();
std::ostream &open(const std::string &name);
std::ostream &open(const path &name);
std::ostream &open(const zip_info &name);
void extract(const std::string &name);
void extract(const std::string &name, const std::string &path);
void extract(const zip_info &name);
void extract(const zip_info &name, const std::string &path);
void extractall();
void extractall(const std::string &path);
void extractall(const std::string &path, const std::vector<std::string> &members);
void extractall(const std::string &path, const std::vector<zip_info> &members);
void printdir();
void printdir(std::ostream &stream);
std::string read(const std::string &name);
std::string read(const path &name);
std::string read(const zip_info &name);
std::pair<bool, std::string> testzip();
bool check_crc();
void write(const std::string &filename);
void write(const std::string &filename, const std::string &arcname);
void write_file(const path &source_file);
void write_file(const path &source_file, const path &archive_path);
void writestr(const std::string &arcname, const std::string &bytes);
void writestr(const zip_info &arcname, const std::string &bytes);
void write_string(const std::string &string, const path &archive_path);
void write_string(const std::string &string, const zip_info &archive_path);
std::string get_filename() const;
path get_filename() const;
std::string comment;
@ -155,7 +143,7 @@ private:
std::unique_ptr<mz_zip_archive_tag> archive_;
std::vector<char> buffer_;
std::stringstream open_stream_;
std::string filename_;
path filename_;
};
} // namespace xlnt

247
include/xlnt/utils/path.hpp Normal file
View File

@ -0,0 +1,247 @@
// Copyright (c) 2014-2016 Thomas Fussell
//
// 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
#include <string>
#include <vector>
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/hashable.hpp>
namespace xlnt {
/// <summary>
/// Encapsulates a path that points to location in a filesystem.
/// </summary>
class XLNT_CLASS path : public hashable
{
public:
/// <summary>
/// The parts of this path are held in a container of this type.
/// </summary>
using container = std::vector<std::string>;
/// <summary>
/// Expose the container's iterator as the iterator for this class.
/// </summary>
using iterator = container::iterator;
/// <summary>
/// Expose the container's const_iterator as the const_iterator for this class.
/// </summary>
using const_iterator = container::const_iterator;
/// <summary>
/// Expose the container's reverse_iterator as the reverse_iterator for this class.
/// </summary>
using reverse_iterator = container::reverse_iterator;
/// <summary>
/// Expose the container's const_reverse_iterator as the const_reverse_iterator for this class.
/// </summary>
using const_reverse_iterator = container::const_reverse_iterator;
/// <summary>
/// The system-specific path separator character (e.g. '/' or '\').
/// </summary>
static char separator();
/// <summary>
/// Construct an empty path.
/// </summary>
path();
/// <summary>
/// Counstruct a path from a string representing the path.
/// </summary>
explicit path(const std::string &path_string);
/// <summary>
/// Construct a path from a string with an explicit directory seprator.
/// </summary>
path(const std::string &path_string, char sep);
// general attributes
/// <summary>
/// Return true iff this path doesn't begin with / (or a drive letter on Windows).
/// </summary>
bool is_relative() const;
/// <summary>
/// Return true iff path::is_relative() is false.
/// </summary>
bool is_absolute() const;
/// <summary>
/// Return true iff this path is the root of the host's filesystem.
/// </summary>
bool is_root() const;
/// <summary>
/// Return a new path that points to the directory containing the current path
/// Return the path unchanged if this path is the absolute or relative root.
/// </summary>
path parent() const;
/// <summary>
/// Return the last component of this path.
/// </summary>
std::string basename();
// conversion
/// <summary>
/// Create a string representing this path separated by the provided
/// separator or the system-default separator if not provided.
/// </summary>
std::string to_string(char sep = separator()) const;
/// <summary>
/// If this path is relative, append each component of this path
/// to base_path and return the resulting absolute path. Otherwise,
/// the the current path will be returned and base_path will be ignored.
/// </summary>
path make_absolute(const path &base_path) const;
// filesystem attributes
/// <summary>
/// Return true iff the file or directory pointed to by this path
/// exists on the filesystem.
/// </summary>
bool exists() const;
/// <summary>
/// Return true if the file or directory pointed to by this path
/// is a directory.
/// </summary>
bool is_directory() const;
/// <summary>
/// Return true if the file or directory pointed to by this path
/// is a regular file.
/// </summary>
bool is_file() const;
// filesystem
/// <summary>
/// Open the file pointed to by this path and return a string containing
/// the files contents.
/// </summary>
std::string read_contents() const;
// mutators
/// <summary>
/// Append the provided part to this path and return a reference to this same path
/// so calls can be chained.
/// </summary>
path &append(const std::string &to_append);
/// <summary>
/// Append the provided part to this path and return the result.
/// </summary>
path append(const std::string &to_append) const;
/// <summary>
/// Append the provided part to this path and return a reference to this same path
/// so calls can be chained.
/// </summary>
path &append(const path &to_append);
/// <summary>
/// Append the provided part to this path and return the result.
/// </summary>
path append(const path &to_append) const;
// iterators
/// <summary>
/// An iterator to the first element in this path.
/// </summary>
iterator begin();
/// <summary>
/// An iterator to one past the last element in this path.
/// </summary>
iterator end();
/// <summary>
/// An iterator to the first element in this path.
/// </summary>
const_iterator begin() const;
/// <summary>
/// A const iterator to one past the last element in this path.
/// </summary>
const_iterator end() const;
/// <summary>
/// An iterator to the first element in this path.
/// </summary>
const_iterator cbegin() const;
/// <summary>
/// A const iterator to one past the last element in this path.
/// </summary>
const_iterator cend() const;
/// <summary>
/// A reverse iterator to the last element in this path.
/// </summary>
reverse_iterator rbegin();
/// <summary>
/// A reverse iterator to one before the first element in this path.
/// </summary>
reverse_iterator rend();
/// <summary>
/// A const reverse iterator to the last element in this path.
/// </summary>
const_reverse_iterator rbegin() const;
/// <summary>
/// A const reverse iterator to one before the first element in this path.
/// </summary>
const_reverse_iterator rend() const;
/// <summary>
/// A const reverse iterator to the last element in this path.
/// </summary>
const_reverse_iterator crbegin() const;
/// <summary>
/// A const reverse iterator to one before the first element in this path.
/// </summary>
const_reverse_iterator crend() const;
protected:
std::string to_hash_string() const override;
private:
container parts_;
};
} // namespace xlnt

View File

@ -36,13 +36,11 @@
namespace xlnt {
class alignment;
class app_properties;
class border;
class cell;
class cell_style;
class color;
class const_worksheet_iterator;
class document_properties;
class drawing;
class fill;
class font;
@ -50,6 +48,7 @@ class format;
class manifest;
class named_range;
class number_format;
class path;
class pattern_fill;
class protection;
class range;
@ -63,6 +62,9 @@ class worksheet;
class worksheet_iterator;
class zip_file;
struct datetime;
enum class calendar;
enum class relationship_type;
namespace detail {
@ -85,6 +87,14 @@ public:
/// </summary>
friend void swap(workbook &left, workbook &right);
static workbook minimal();
static workbook empty_excel();
static workbook empty_libre_office();
static workbook empty_numbers();
// constructors
/// <summary>
@ -297,11 +307,47 @@ public:
/// </summary>
std::vector<std::string> get_sheet_titles() const;
document_properties &get_properties();
const document_properties &get_properties() const;
std::string get_application() const;
void set_application(const std::string &application);
app_properties &get_app_properties();
const app_properties &get_app_properties() const;
calendar get_base_date() const;
void set_base_date(calendar base_date);
std::string get_creator() const;
void set_creator(const std::string &creator);
std::string get_last_modified_by() const;
void set_last_modified_by(const std::string &last_modified_by);
datetime get_created() const;
void set_created(const datetime &when);
datetime get_modified() const;
void set_modified(const datetime &when);
int get_doc_security() const;
void set_doc_security(int doc_security);
bool get_scale_crop() const;
void set_scale_crop(bool scale_crop);
std::string get_company() const;
void set_company(const std::string &company);
bool links_up_to_date() const;
void set_links_up_to_date(bool links_up_to_date);
bool is_shared_doc() const;
void set_shared_doc(bool shared_doc);
bool hyperlinks_changed() const;
void set_hyperlinks_changed(bool hyperlinks_changed);
std::string get_app_version() const;
void set_app_version(const std::string &version);
std::string get_title() const;
void set_title(const std::string &title);
// named ranges
@ -314,19 +360,23 @@ public:
// serialization
bool save(std::vector<unsigned char> &data);
bool save(std::vector<std::uint8_t> &data);
bool save(const std::string &filename);
bool load(const std::vector<unsigned char> &data);
bool save(const xlnt::path &filename);
bool save(std::ostream &stream);
bool load(const std::vector<std::uint8_t> &data);
bool load(const std::string &filename);
bool load(const xlnt::path &filename);
bool load(std::istream &stream);
bool load(zip_file &archive);
void set_code_name(const std::string &code_name);
// theme
bool has_loaded_theme() const;
const theme &get_loaded_theme() const;
bool has_theme() const;
const theme &get_theme() const;
void set_theme(const theme &value);
// formats
@ -407,6 +457,8 @@ private:
friend class excel_serializer;
friend class worksheet;
workbook(detail::workbook_impl *impl);
/// <summary>
/// Get the name of the next unused relationship.
/// </summary>

View File

@ -65,6 +65,7 @@
#include <xlnt/utils/date.hpp>
#include <xlnt/utils/datetime.hpp>
#include <xlnt/utils/exceptions.hpp>
#include <xlnt/utils/path.hpp>
#include <xlnt/utils/time.hpp>
#include <xlnt/utils/timedelta.hpp>

View File

@ -155,26 +155,10 @@ std::string cell::check_string(const std::string &to_check)
return s;
}
cell::cell() : d_(nullptr)
{
}
cell::cell(detail::cell_impl *d) : d_(d)
{
}
cell::cell(worksheet worksheet, const cell_reference &reference) : d_(nullptr)
{
cell self = worksheet.get_cell(reference);
d_ = self.d_;
}
template <typename T>
cell::cell(worksheet worksheet, const cell_reference &reference, const T &initial_value) : cell(worksheet, reference)
{
set_value(initial_value);
}
bool cell::garbage_collectible() const
{
return !(get_data_type() != type::null || is_merged() || has_comment() || has_formula() || has_format());
@ -953,7 +937,7 @@ void cell::set_format(const format &new_format)
calendar cell::get_base_date() const
{
return get_workbook().get_properties().excel_base_date;
return get_workbook().get_base_date();
}
std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)

View File

@ -604,12 +604,6 @@ public:
TS_ASSERT_EQUALS(cell.get_value<std::string>(), std::string(32'767, 'a'));
}
void test_operators()
{
xlnt::cell cell;
TS_ASSERT_EQUALS(cell, nullptr);
}
void test_comment()
{
xlnt::workbook wb;
@ -646,7 +640,7 @@ public:
void test_anchor()
{
xlnt::workbook wb;
xlnt::cell cell(wb.get_active_sheet(), "A1");
auto cell = wb.get_active_sheet().get_cell("A1");
auto anchor = cell.get_anchor();
TS_ASSERT_EQUALS(anchor.first, 0);
TS_ASSERT_EQUALS(anchor.second, 0);
@ -655,7 +649,7 @@ public:
void test_hyperlink()
{
xlnt::workbook wb;
xlnt::cell cell(wb.get_active_sheet(), "A1");
auto cell = wb.get_active_sheet().get_cell("A1");
TS_ASSERT(!cell.has_hyperlink());
TS_ASSERT_THROWS(cell.get_hyperlink(), std::runtime_error);
TS_ASSERT_THROWS(cell.set_hyperlink("notaurl"), std::runtime_error);

View File

@ -23,8 +23,7 @@
#include <limits>
#include <detail/constants.hpp>
#include "xlnt_config.hpp"
#include <xlnt/xlnt_config.hpp>
namespace xlnt {
@ -49,20 +48,20 @@ const column_t constants::max_column()
}
// constants
const std::string constants::package_properties() { return "docProps"; }
const std::string constants::package_xl() { return "xl"; }
const std::string constants::package_root_rels() { return "_rels"; }
const std::string constants::package_theme() { return package_xl() + "/" + "theme"; }
const std::string constants::package_worksheets() { return package_xl() + "/" + "worksheets"; }
const path constants::package_properties() { return path("docProps"); }
const path constants::package_xl() { return path("xl"); }
const path constants::package_root_rels() { return path(std::string("_rels")); }
const path constants::package_theme() { return package_xl().append("theme"); }
const path constants::package_worksheets() { return package_xl().append("worksheets"); }
const std::string constants::part_content_types() { return "[Content_Types].xml"; }
const std::string constants::part_root_relationships() { return package_root_rels() + "/.rels"; }
const std::string constants::part_core() { return package_properties() + "/core.xml"; }
const std::string constants::part_app() { return package_properties() + "/app.xml"; }
const std::string constants::part_workbook() { return package_xl() + "/workbook.xml"; }
const std::string constants::part_styles() { return package_xl() + "/styles.xml"; }
const std::string constants::part_theme() { return package_theme() + "/theme1.xml"; }
const std::string constants::part_shared_strings() { return package_xl() + "/sharedStrings.xml"; }
const path constants::part_content_types() { return path("[Content_Types].xml"); }
const path constants::part_root_relationships() { return package_root_rels().append(".rels"); }
const path constants::part_core() { return package_properties().append("core.xml"); }
const path constants::part_app() { return package_properties().append("app.xml"); }
const path constants::part_workbook() { return package_xl().append("workbook.xml"); }
const path constants::part_styles() { return package_xl().append("styles.xml"); }
const path constants::part_theme() { return package_theme().append("theme1.xml"); }
const path constants::part_shared_strings() { return package_xl().append("sharedStrings.xml"); }
const std::unordered_map<std::string, std::string> &constants::get_namespaces()
{

View File

@ -26,10 +26,11 @@
#include <unordered_map>
#include <xlnt/cell/index_types.hpp>
#include <xlnt/utils/path.hpp>
namespace xlnt {
struct constants
struct XLNT_CLASS constants
{
/// <summary>
/// Returns the lowest allowable row index in a worksheet.
@ -54,67 +55,67 @@ struct constants
/// <summary>
/// Returns the URI of the directory containing package properties.
/// </summary>
static const std::string package_properties();
static const path package_properties();
/// <summary>
/// Returns the URI of the directory containing SpreatsheetML package parts.
/// </summary>
static const std::string package_xl();
static const path package_xl();
/// <summary>
/// Returns the URI of the directory containing root relationships package part.
/// </summary>
static const std::string package_root_rels();
static const path package_root_rels();
/// <summary>
/// Returns the URI of the directory containing package themes.
/// </summary>
static const std::string package_theme();
static const path package_theme();
/// <summary>
/// Returns the URI of the directory containing package worksheets.
/// </summary>
static const std::string package_worksheets();
static const path package_worksheets();
/// <summary>
/// Returns the URI of the content types package part.
/// </summary>
static const std::string part_content_types();
static const path part_content_types();
/// <summary>
/// Returns the URI of the core properties package part.
/// </summary>
static const std::string part_core();
static const path part_core();
/// <summary>
/// Returns the URI of the app properties package part.
/// </summary>
static const std::string part_app();
static const path part_app();
/// <summary>
/// Returns the URI of the workbook package part.
/// </summary>
static const std::string part_workbook();
static const path part_workbook();
/// <summary>
/// Returns the URI of the root relationships package part.
/// </summary>
static const std::string part_root_relationships();
static const path part_root_relationships();
/// <summary>
/// Returns the URI of the styles package part.
/// </summary>
static const std::string part_styles();
static const path part_styles();
/// <summary>
/// Returns the URI of the theme package part.
/// </summary>
static const std::string part_theme();
static const path part_theme();
/// <summary>
/// Returns the URI of the shared strings package part.
/// </summary>
static const std::string part_shared_strings();
static const path part_shared_strings();
/// <summary>
/// Returns an unordered_map mapping namespace names to namespaces.

View File

@ -70,9 +70,6 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
{
wb.clear();
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
if(!archive.has_file(xlnt::constants::part_content_types()))
{
throw xlnt::invalid_file("missing [Content Types].xml");
@ -112,7 +109,7 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
wb.create_root_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
}
auto workbook_relationships = relationship_serializer_.read_relationships(xlnt::constants::part_workbook());
auto workbook_relationships = relationship_serializer_.read_relationships(xlnt::constants::part_workbook().to_string('/'));
for (const auto &relationship : workbook_relationships)
{
@ -123,12 +120,17 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
xml.load(archive.read(xlnt::constants::part_workbook()).c_str());
auto root_node = xml.child("workbook");
auto workbook_pr_node = root_node.child("workbookPr");
wb.get_properties().excel_base_date =
(workbook_pr_node.attribute("date1904") && workbook_pr_node.attribute("date1904").value() != std::string("0"))
? xlnt::calendar::mac_1904
: xlnt::calendar::windows_1900;
if (workbook_pr_node.attribute("date1904"))
{
std::string value = workbook_pr_node.attribute("date1904").value();
if (value == "1" || value == "true")
{
wb.set_base_date(xlnt::calendar::mac_1904);
}
}
if(archive.has_file(xlnt::constants::part_shared_strings()))
{
@ -162,14 +164,15 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
ws.set_id(static_cast<std::size_t>(sheet_node.attribute("sheetId").as_ullong()));
xlnt::worksheet_serializer worksheet_serializer(ws);
pugi::xml_document worksheet_xml;
worksheet_xml.load(archive.read("xl/" + rel.get_target_uri()).c_str());
auto target_uri = xlnt::constants::package_xl().append(rel.get_target_uri());
worksheet_xml.load(archive.read(target_uri).c_str());
worksheet_serializer.read_worksheet(worksheet_xml, stylesheet);
}
if (archive.has_file("docProps/thumbnail.jpeg"))
if (archive.has_file(xlnt::path("docProps/thumbnail.jpeg")))
{
auto thumbnail_data = archive.read("docProps/thumbnail.jpeg");
auto thumbnail_data = archive.read(xlnt::path("docProps/thumbnail.jpeg"));
wb.set_thumbnail(std::vector<std::uint8_t>(thumbnail_data.begin(), thumbnail_data.end()));
}
@ -210,7 +213,7 @@ bool excel_serializer::load_stream_workbook(std::istream &stream, bool guess_typ
return load_virtual_workbook(bytes, guess_types, data_only);
}
bool excel_serializer::load_workbook(const std::string &filename, bool guess_types, bool data_only)
bool excel_serializer::load_workbook(const path &filename, bool guess_types, bool data_only)
{
try
{
@ -218,7 +221,7 @@ bool excel_serializer::load_workbook(const std::string &filename, bool guess_typ
}
catch (std::runtime_error)
{
throw invalid_file(filename);
throw invalid_file(filename.to_string());
}
return ::load_workbook(archive_, guess_types, data_only, workbook_, get_stylesheet());
@ -239,7 +242,7 @@ void excel_serializer::write_data(bool /*as_template*/)
{
relationship_serializer relationship_serializer_(archive_);
relationship_serializer_.write_relationships(workbook_.get_root_relationships(), "");
relationship_serializer_.write_relationships(workbook_.get_relationships(), constants::part_workbook());
relationship_serializer_.write_relationships(workbook_.get_relationships(), constants::part_workbook().to_string('/'));
pugi::xml_document properties_app_xml;
workbook_serializer workbook_serializer_(workbook_);
@ -248,7 +251,7 @@ void excel_serializer::write_data(bool /*as_template*/)
{
std::ostringstream ss;
properties_app_xml.save(ss);
archive_.writestr(constants::part_app(), ss.str());
archive_.write_string(ss.str(), constants::part_app());
}
pugi::xml_document properties_core_xml;
@ -257,17 +260,17 @@ void excel_serializer::write_data(bool /*as_template*/)
{
std::ostringstream ss;
properties_core_xml.save(ss);
archive_.writestr(constants::part_core(), ss.str());
archive_.write_string(ss.str(), constants::part_core());
}
pugi::xml_document theme_xml;
theme_serializer theme_serializer_;
theme_serializer_.write_theme(workbook_.get_loaded_theme(), theme_xml);
theme_serializer_.write_theme(workbook_.get_theme(), theme_xml);
{
std::ostringstream ss;
theme_xml.save(ss);
archive_.writestr(constants::part_theme(), ss.str());
archive_.write_string(ss.str(), constants::part_theme());
}
if (!workbook_.get_shared_strings().empty())
@ -278,7 +281,7 @@ void excel_serializer::write_data(bool /*as_template*/)
std::ostringstream ss;
shared_strings_xml.save(ss);
archive_.writestr(constants::part_shared_strings(), ss.str());
archive_.write_string(ss.str(), constants::part_shared_strings());
}
pugi::xml_document workbook_xml;
@ -287,7 +290,7 @@ void excel_serializer::write_data(bool /*as_template*/)
{
std::ostringstream ss;
workbook_xml.save(ss);
archive_.writestr(constants::part_workbook(), ss.str());
archive_.write_string(ss.str(), constants::part_workbook());
}
style_serializer style_serializer(workbook_.d_->stylesheet_);
@ -297,7 +300,7 @@ void excel_serializer::write_data(bool /*as_template*/)
{
std::ostringstream ss;
style_xml.save(ss);
archive_.writestr(constants::part_styles(), ss.str());
archive_.write_string(ss.str(), constants::part_styles());
}
manifest_serializer manifest_serializer_(workbook_.get_manifest());
@ -307,7 +310,7 @@ void excel_serializer::write_data(bool /*as_template*/)
{
std::ostringstream ss;
manifest_xml.save(ss);
archive_.writestr(constants::part_content_types(), ss.str());
archive_.write_string(ss.str(), constants::part_content_types());
}
write_worksheets();
@ -315,7 +318,7 @@ void excel_serializer::write_data(bool /*as_template*/)
if(!workbook_.get_thumbnail().empty())
{
const auto &thumbnail = workbook_.get_thumbnail();
archive_.writestr("docProps/thumbnail.jpeg", std::string(thumbnail.begin(), thumbnail.end()));
archive_.write_string(std::string(thumbnail.begin(), thumbnail.end()), path("docProps/thumbnail.jpeg"));
}
}
@ -332,12 +335,13 @@ void excel_serializer::write_worksheets()
if (rel.get_target_uri() != target) continue;
worksheet_serializer serializer_(ws);
std::string ws_filename = (rel.get_target_uri().substr(0, 3) != "xl/" ? "xl/" : "") + rel.get_target_uri();
path ws_path(rel.get_target_uri().substr(0, 3) != "xl/" ? "xl/" : "", '/');
ws_path.append(rel.get_target_uri());
std::ostringstream ss;
pugi::xml_document worksheet_xml;
serializer_.write_worksheet(worksheet_xml);
worksheet_xml.save(ss);
archive_.writestr(ws_filename, ss.str());
archive_.write_string(ss.str(), ws_path);
break;
}
@ -356,7 +360,7 @@ bool excel_serializer::save_stream_workbook(std::ostream &stream, bool as_templa
return true;
}
bool excel_serializer::save_workbook(const std::string &filename, bool as_template)
bool excel_serializer::save_workbook(const path &filename, bool as_template)
{
write_data(as_template);
archive_.save(filename);

View File

@ -62,7 +62,7 @@ public:
/// Create a ZIP file in memory, load archive from filename, then populate workbook
/// with data from archive.
/// </summary>
bool load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
bool load_workbook(const path &filename, bool guess_types = false, bool data_only = false);
/// <summary>
/// Create a ZIP file in memory, load archive from stream, then populate workbook
@ -81,7 +81,7 @@ public:
/// Create a ZIP file in memory, save workbook to this archive, then save archive
/// to filename.
/// </summary>
bool save_workbook(const std::string &filename, bool as_template = false);
bool save_workbook(const path &filename, bool as_template = false);
/// <summary>
/// Create a ZIP file in memory, save workbook to this archive, then assign ZIP file

View File

@ -46,7 +46,7 @@ void manifest_serializer::read_manifest(const pugi::xml_document &xml)
}
else if (child.name() == std::string("Override"))
{
manifest_.add_override_type(child.attribute("PartName").value(), child.attribute("ContentType").value());
manifest_.add_override_type(path(child.attribute("PartName").value()), child.attribute("ContentType").value());
}
}
}
@ -66,7 +66,7 @@ void manifest_serializer::write_manifest(pugi::xml_document &xml) const
for (const auto override_type : manifest_.get_override_types())
{
auto type_node = root_node.append_child("Override");
type_node.append_attribute("PartName").set_value(override_type.second.get_part_name().c_str());
type_node.append_attribute("PartName").set_value(override_type.second.get_part().to_string('/').c_str());
type_node.append_attribute("ContentType").set_value(override_type.second.get_content_type().c_str());
}
}

View File

@ -38,7 +38,7 @@ std::string make_rels_name(const std::string &target)
if (target.empty())
{
return xlnt::constants::part_root_relationships();
return xlnt::constants::part_root_relationships().to_string();
}
auto sep_pos = target.find_last_of(sep);
@ -58,7 +58,7 @@ relationship_serializer::relationship_serializer(zip_file &archive) : archive_(a
std::vector<relationship> relationship_serializer::read_relationships(const std::string &target)
{
pugi::xml_document xml;
xml.load(archive_.read(make_rels_name(target)).c_str());
xml.load(archive_.read(path(make_rels_name(target))).c_str());
auto root_node = xml.child("Relationships");
@ -101,7 +101,7 @@ bool relationship_serializer::write_relationships(const std::vector<relationship
std::ostringstream ss;
xml.save(ss);
archive_.writestr(make_rels_name(target), ss.str());
archive_.write_string(ss.str(), path(make_rels_name(target)));
return true;
}

View File

@ -23,13 +23,13 @@
#pragma once
#include <iterator>
#include <list>
#include <vector>
#include <detail/stylesheet.hpp>
#include <detail/worksheet_impl.hpp>
#include <xlnt/packaging/app_properties.hpp>
#include <xlnt/packaging/document_properties.hpp>
#include <xlnt/packaging/manifest.hpp>
#include <xlnt/utils/datetime.hpp>
#include <xlnt/workbook/theme.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
@ -42,7 +42,26 @@ struct worksheet_impl;
struct workbook_impl
{
workbook_impl();
workbook_impl()
: active_sheet_index_(0),
guess_types_(false),
data_only_(false),
has_theme_(false),
write_core_properties_(false),
created_(xlnt::datetime::now()),
modified_(xlnt::datetime::now()),
title_("Untitled"),
base_date_(calendar::windows_1900),
write_app_properties_(false),
application_("libxlnt"),
doc_security_(0),
scale_crop_(false),
links_up_to_date_(false),
shared_doc_(false),
hyperlinks_changed_(false),
app_version_("0.9")
{
}
workbook_impl(const workbook_impl &other)
: active_sheet_index_(other.active_sheet_index_),
@ -50,12 +69,32 @@ struct workbook_impl
relationships_(other.relationships_),
root_relationships_(other.root_relationships_),
shared_strings_(other.shared_strings_),
properties_(other.properties_),
app_properties_(other.app_properties_),
guess_types_(other.guess_types_),
data_only_(other.data_only_),
stylesheet_(other.stylesheet_),
manifest_(other.manifest_)
has_theme_(other.has_theme_),
theme_(other.theme_),
manifest_(other.manifest_),
write_core_properties_(other.write_core_properties_),
creator_(other.creator_),
last_modified_by_(other.last_modified_by_),
created_(other.created_),
modified_(other.modified_),
title_(other.title_),
subject_(other.subject_),
description_(other.description_),
keywords_(other.keywords_),
category_(other.category_),
company_(other.company_),
base_date_(other.base_date_),
write_app_properties_(other.write_app_properties_),
application_(other.application_),
doc_security_(other.doc_security_),
scale_crop_(other.scale_crop_),
links_up_to_date_(other.links_up_to_date_),
shared_doc_(other.shared_doc_),
hyperlinks_changed_(other.hyperlinks_changed_),
app_version_(other.app_version_)
{
}
@ -71,32 +110,78 @@ struct workbook_impl
std::back_inserter(root_relationships_));
shared_strings_.clear();
std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_));
properties_ = other.properties_;
app_properties_ = other.app_properties_;
guess_types_ = other.guess_types_;
data_only_ = other.data_only_;
has_theme_ = other.has_theme_;
theme_ = other.theme_;
manifest_ = other.manifest_;
write_core_properties_ = other.write_core_properties_;
creator_ = other.creator_;
last_modified_by_ = other.last_modified_by_;
created_ = other.created_;
modified_ = other.modified_;
title_ = other.title_;
subject_ = other.subject_;
description_ = other.description_;
keywords_ = other.keywords_;
category_ = other.category_;
company_ = other.company_;
base_date_ = other.base_date_;
write_app_properties_ = other.write_app_properties_;
application_ = other.application_;
doc_security_ = other.doc_security_;
scale_crop_ = other.scale_crop_;
links_up_to_date_ = other.links_up_to_date_;
shared_doc_ = other.shared_doc_;
hyperlinks_changed_ = other.hyperlinks_changed_;
app_version_ = other.app_version_;
return *this;
}
std::size_t active_sheet_index_;
std::vector<worksheet_impl> worksheets_;
std::list<worksheet_impl> worksheets_;
std::vector<relationship> relationships_;
std::vector<relationship> root_relationships_;
std::vector<text> shared_strings_;
document_properties properties_;
app_properties app_properties_;
bool guess_types_;
bool data_only_;
stylesheet stylesheet_;
manifest manifest_;
bool has_theme_;
theme theme_;
std::vector<std::uint8_t> thumbnail_;
// core properties
bool write_core_properties_;
std::string creator_;
std::string last_modified_by_;
datetime created_;
datetime modified_;
std::string title_;
std::string subject_;
std::string description_;
std::string keywords_;
std::string category_;
std::string company_;
calendar base_date_;
// application properties
bool write_app_properties_;
std::string application_;
int doc_security_;
bool scale_crop_;
bool links_up_to_date_;
bool shared_doc_;
bool hyperlinks_changed_;
std::string app_version_;
};
} // namespace detail

View File

@ -85,83 +85,78 @@ workbook_serializer::workbook_serializer(workbook &wb) : workbook_(wb)
void workbook_serializer::read_properties_core(const pugi::xml_document &xml)
{
auto &props = workbook_.get_properties();
auto root_node = xml.child("cp:coreProperties");
props.excel_base_date = calendar::windows_1900;
if (root_node.child("dc:creator"))
{
props.creator = root_node.child("dc:creator").text().get();
workbook_.set_creator(root_node.child("dc:creator").text().get());
}
if (root_node.child("cp:lastModifiedBy"))
{
props.last_modified_by = root_node.child("cp:lastModifiedBy").text().get();
workbook_.set_last_modified_by(root_node.child("cp:lastModifiedBy").text().get());
}
if (root_node.child("dcterms:created"))
{
std::string created_string = root_node.child("dcterms:created").text().get();
props.created = w3cdtf_to_datetime(created_string);
workbook_.set_created(w3cdtf_to_datetime(created_string));
}
if (root_node.child("dcterms:modified"))
{
std::string modified_string = root_node.child("dcterms:modified").text().get();
props.modified = w3cdtf_to_datetime(modified_string);
workbook_.set_modified(w3cdtf_to_datetime(modified_string));
}
}
void workbook_serializer::read_properties_app(const pugi::xml_document &xml)
{
auto &props = workbook_.get_app_properties();
auto root_node = xml.child("Properties");
if(root_node.child("Application"))
{
props.application = root_node.child("Application").text().get();
workbook_.set_application(root_node.child("Application").text().get());
}
if(root_node.child("DocSecurity"))
{
props.doc_security = std::stoi(root_node.child("DocSecurity").text().get());
workbook_.set_doc_security(std::stoi(root_node.child("DocSecurity").text().get()));
}
if(root_node.child("ScaleCrop"))
{
props.scale_crop = root_node.child("ScaleCrop").text().get() == std::string("true");
workbook_.set_scale_crop(root_node.child("ScaleCrop").text().get() == std::string("true"));
}
if(root_node.child("Company"))
{
props.company = root_node.child("Company").text().get();
workbook_.set_company(root_node.child("Company").text().get());
}
if(root_node.child("ScaleCrop"))
{
props.links_up_to_date = root_node.child("ScaleCrop").text().get() == std::string("true");
workbook_.set_links_up_to_date(root_node.child("ScaleCrop").text().get() == std::string("true"));
}
if(root_node.child("SharedDoc"))
{
props.shared_doc = root_node.child("SharedDoc").text().get() == std::string("true");
workbook_.set_shared_doc(root_node.child("SharedDoc").text().get() == std::string("true"));
}
if(root_node.child("HyperlinksChanged"))
{
props.hyperlinks_changed = root_node.child("HyperlinksChanged").text().get() == std::string("true");
workbook_.set_hyperlinks_changed(root_node.child("HyperlinksChanged").text().get() == std::string("true"));
}
if(root_node.child("AppVersion"))
{
props.app_version = root_node.child("AppVersion").text().get();
workbook_.set_app_version(root_node.child("AppVersion").text().get());
}
}
void workbook_serializer::write_properties_core(pugi::xml_document &xml) const
{
auto &props = workbook_.get_properties();
auto root_node = xml.append_child("cp:coreProperties");
root_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
@ -170,13 +165,13 @@ void workbook_serializer::write_properties_core(pugi::xml_document &xml) const
root_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");
root_node.append_child("dc:creator").text().set(props.creator.c_str());
root_node.append_child("cp:lastModifiedBy").text().set(props.last_modified_by.c_str());
root_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(props.created).c_str());
root_node.append_child("dc:creator").text().set(workbook_.get_creator().c_str());
root_node.append_child("cp:lastModifiedBy").text().set(workbook_.get_last_modified_by().c_str());
root_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(workbook_.get_created()).c_str());
root_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF");
root_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(props.modified).c_str());
root_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(workbook_.get_modified()).c_str());
root_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF");
root_node.append_child("dc:title").text().set(props.title.c_str());
root_node.append_child("dc:title").text().set(workbook_.get_title().c_str());
root_node.append_child("dc:description");
root_node.append_child("dc:subject");
root_node.append_child("cp:keywords");
@ -190,23 +185,21 @@ void workbook_serializer::write_properties_app(pugi::xml_document &xml) const
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");
const auto &properties = workbook_.get_app_properties();
root_node.append_child("Application").text().set(properties.application.c_str());
root_node.append_child("DocSecurity").text().set(std::to_string(properties.doc_security).c_str());
root_node.append_child("ScaleCrop").text().set(properties.scale_crop ? "true" : "false");
root_node.append_child("Application").text().set(workbook_.get_application().c_str());
root_node.append_child("DocSecurity").text().set(std::to_string(workbook_.get_doc_security()).c_str());
root_node.append_child("ScaleCrop").text().set(workbook_.get_scale_crop() ? "true" : "false");
auto company_node = root_node.append_child("Company");
if (!properties.company.empty())
if (!workbook_.get_company().empty())
{
company_node.text().set(properties.company.c_str());
company_node.text().set(workbook_.get_company().c_str());
}
root_node.append_child("LinksUpToDate").text().set(properties.links_up_to_date ? "true" : "false");
root_node.append_child("SharedDoc").text().set(properties.shared_doc ? "true" : "false");
root_node.append_child("HyperlinksChanged").text().set(properties.hyperlinks_changed ? "true" : "false");
root_node.append_child("AppVersion").text().set(properties.app_version.c_str());
root_node.append_child("LinksUpToDate").text().set(workbook_.links_up_to_date() ? "true" : "false");
root_node.append_child("SharedDoc").text().set(workbook_.is_shared_doc() ? "true" : "false");
root_node.append_child("HyperlinksChanged").text().set(workbook_.hyperlinks_changed() ? "true" : "false");
root_node.append_child("AppVersion").text().set(workbook_.get_app_version().c_str());
// TODO what is this stuff?
@ -261,7 +254,7 @@ void workbook_serializer::write_workbook(pugi::xml_document &xml) const
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");
workbook_pr_node.append_attribute("date1904").set_value(workbook_.get_properties().excel_base_date == calendar::mac_1904 ? "1" : "0");
workbook_pr_node.append_attribute("date1904").set_value(workbook_.get_base_date() == calendar::mac_1904 ? "1" : "0");
auto book_views_node = root_node.append_child("bookViews");
auto workbook_view_node = book_views_node.append_child("workbookView");

View File

@ -0,0 +1,322 @@
#include <detail/xlsx_writer.hpp>
#include <detail/constants.hpp>
#include <detail/include_pugixml.hpp>
#include <xlnt/utils/path.hpp>
#include <xlnt/packaging/zip_file.hpp>
#include <xlnt/workbook/const_worksheet_iterator.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/worksheet.hpp>
namespace {
void write_document_to_archive(const pugi::xml_document &document,
const xlnt::path &archive_path, xlnt::zip_file &archive)
{
std::ostringstream out_stream;
document.save(out_stream);
archive.write_string(out_stream.str(), archive_path);
}
// Package Parts
void write_package_relationships(const xlnt::workbook &target, xlnt::zip_file &archive)
{
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");
for (const auto &relationship : target.get_root_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(relationship.get_type_string().c_str());
relationship_node.append_attribute("Target").set_value(relationship.get_target_uri().c_str());
}
write_document_to_archive(document, xlnt::constants::part_root_relationships(), archive);
}
void write_content_types(const xlnt::workbook &target, xlnt::zip_file &archive)
{
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");
auto default_node = types_node.append_child("Default");
default_node.append_attribute("Extension").set_value("rels");
default_node.append_attribute("ContentType").set_value("application/vnd.openxmlformats-package.relationships+xml");
auto workbook_override_node = types_node.append_child("Override");
workbook_override_node.append_attribute("PartName").set_value("/workbook.xml");
workbook_override_node.append_attribute("ContentType").set_value("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
auto sheet_override_node = types_node.append_child("Default");
sheet_override_node.append_attribute("PartName").set_value("/sheet1.xml");
sheet_override_node.append_attribute("ContentType").set_value("application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
write_document_to_archive(document, xlnt::constants::part_content_types(), archive);
}
void write_app_properties(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("appProperties");
write_document_to_archive(document, xlnt::constants::part_app(), archive);
}
void write_core_properties(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("coreProperties");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_custom_file_properties(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("customFileProperties");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
// SpreadsheetML Package Parts
void write_workbook(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto workbook_node = document.append_child("workbook");
workbook_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
workbook_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto sheets_node = workbook_node.append_child("sheets");
auto sheet_node = sheets_node.append_child("sheet");
sheet_node.append_attribute("name").set_value(1);
sheet_node.append_attribute("sheetId").set_value(1);
sheet_node.append_attribute("r:id").set_value("rId1");
write_document_to_archive(document, xlnt::path("workbook.xml"), archive);
}
void write_workbook_relationships(const xlnt::workbook &target, xlnt::zip_file &archive)
{
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"), archive);
}
// Workbook Relationship Target Parts
void write_calculation_chain(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("calcChain");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_chartsheet(const xlnt::worksheet &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("chartsheet");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_connections(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("connections");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_custom_property(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("customProperty");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_custom_xml_mappings(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("connections");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_dialogsheet(const xlnt::worksheet &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("dialogsheet");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_external_workbook_references(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("externalLink");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_metadata(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("metadata");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_pivot_table(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("pivotTableDefinition");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_shared_string_table(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("sst");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_shared_workbook_revision_headers(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("headers");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_shared_workbook(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("revisions");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_shared_workbook_user_data(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("users");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_styles(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("styleSheet");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_theme(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("theme");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_volatile_dependencies(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("volTypes");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_worksheet(const xlnt::worksheet &target, xlnt::zip_file &archive)
{
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:r").set_value("http://schemas.openxmlformats.org/package/2006/relationships");
worksheet_node.append_child("sheetData");
write_document_to_archive(document, xlnt::path("sheet1.xml"), archive);
}
// Sheet Relationship Target Parts
void write_comments(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("comments");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_drawings(const xlnt::worksheet &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("wsDr");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
// Unknown Parts
void write_unknown_parts(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("relationships");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
void write_unknown_relationships(const xlnt::workbook &target, xlnt::zip_file &archive)
{
pugi::xml_document document;
auto root_node = document.append_child("Relationships");
write_document_to_archive(document, xlnt::constants::part_core(), archive);
}
} // namespace
namespace xlnt
{
xlsx_writer::xlsx_writer(const workbook &target) : target_(target)
{
}
void xlsx_writer::write(const path &destination)
{
xlnt::zip_file archive;
populate_archive(archive);
archive.save(destination);
}
void xlsx_writer::write(std::ostream &destination)
{
xlnt::zip_file archive;
populate_archive(archive);
archive.save(destination);
}
void xlsx_writer::write(std::vector<std::uint8_t> &destination)
{
xlnt::zip_file archive;
populate_archive(archive);
archive.save(destination);
}
void xlsx_writer::populate_archive(zip_file &archive)
{
write_package_relationships(target_, archive);
write_content_types(target_, archive);
write_workbook(target_, archive);
write_workbook_relationships(target_, archive);
for (auto ws : target_)
{
write_worksheet(ws, archive);
}
}
} // namepsace xlnt

View File

@ -0,0 +1,65 @@
// Copyright (c) 2014-2016 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
#include <cstdint>
#include <iostream>
#include <vector>
#include <xlnt/xlnt_config.hpp>
namespace xlnt {
class path;
class workbook;
class zip_file;
/// <summary>
/// Handles writing a workbook into an XLSX file.
/// </summary>
class XLNT_CLASS xlsx_writer
{
public:
xlsx_writer(const workbook &target);
void write(const path &destination);
void write(std::ostream &destination);
void write(std::vector<std::uint8_t> &destination);
private:
/// <summary>
/// Write all files needed to create a valid XLSX file which represents all
/// data contained in workbook.
/// </summary>
void populate_archive(zip_file &archive);
/// <summary>
/// A reference to the workbook which is the object of read/write operations.
/// </summary>
const workbook &target_;
};
} // namespace xlnt

View File

@ -39,9 +39,9 @@ bool manifest::has_default_type(const std::string &extension) const
return default_types_.find(extension) != default_types_.end();
}
bool manifest::has_override_type(const std::string &part_name) const
bool manifest::has_override_type(const path &part) const
{
auto absolute = part_name.front() == '/' ? part_name : ("/" + part_name);
auto absolute = part.is_absolute() ? part : part.make_absolute(path("/"));
return override_types_.find(absolute) != override_types_.end();
}
@ -50,9 +50,9 @@ void manifest::add_default_type(const std::string &extension, const std::string
default_types_[extension] = default_type(extension, content_type);
}
void manifest::add_override_type(const std::string &part_name, const std::string &content_type)
void manifest::add_override_type(const path &part, const std::string &content_type)
{
auto absolute = part_name.front() == '/' ? part_name : ("/" + part_name);
auto absolute = part.is_absolute() ? part : part.make_absolute(path("/"));
override_types_[absolute] = override_type(absolute, content_type);
}
@ -61,18 +61,18 @@ std::string manifest::get_default_type(const std::string &extension) const
return default_types_.at(extension).get_content_type();
}
std::string manifest::get_override_type(const std::string &part_name) const
std::string manifest::get_override_type(const path &part) const
{
auto absolute = part_name.front() == '/' ? part_name : ("/" + part_name);
auto absolute = part.is_absolute() ? part : part.make_absolute(path("/"));
return override_types_.at(absolute).get_content_type();
}
const std::unordered_map<std::string, default_type> &manifest::get_default_types() const
const manifest::default_types_container &manifest::get_default_types() const
{
return default_types_;
}
const std::unordered_map<std::string, override_type> &manifest::get_override_types() const
const manifest::override_types_container &manifest::get_override_types() const
{
return override_types_;
}

View File

@ -29,27 +29,27 @@ override_type::override_type()
{
}
override_type::override_type(const std::string &part_name, const std::string &content_type)
: part_name_(part_name), content_type_(content_type)
override_type::override_type(const path &part, const std::string &content_type)
: part_(part), content_type_(content_type)
{
}
override_type::override_type(const override_type &other)
: part_name_(other.part_name_), content_type_(other.content_type_)
: part_(other.part_), content_type_(other.content_type_)
{
}
override_type &override_type::operator=(const override_type &other)
{
part_name_ = other.part_name_;
part_ = other.part_;
content_type_ = other.content_type_;
return *this;
}
std::string override_type::get_part_name() const
path override_type::get_part() const
{
return part_name_;
return part_;
}
std::string override_type::get_content_type() const

View File

@ -14,19 +14,18 @@ class test_core : public CxxTest::TestSuite
public:
void test_read_properties_core()
{
auto path = path_helper::get_data_directory() + "/genuine/empty.xlsx";
xlnt::workbook wb;
wb.load(path);
auto &prop = wb.get_properties();
TS_ASSERT_EQUALS(prop.creator, "*.*");
TS_ASSERT_EQUALS(prop.last_modified_by, "Charlie Clark");
TS_ASSERT_EQUALS(prop.created, xlnt::datetime(2010, 4, 9, 20, 43, 12));
TS_ASSERT_EQUALS(prop.modified, xlnt::datetime(2014, 1, 2, 14, 53, 6));
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()
{
auto path = path_helper::get_data_directory() + "/genuine/empty.xlsx";
auto path = path_helper::get_data_directory("genuine/empty.xlsx");
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
@ -43,58 +42,49 @@ public:
void test_read_properties_core_libre()
{
xlnt::zip_file archive(path_helper::get_data_directory() + "/genuine/empty_libre.xlsx");
auto content = archive.read("docProps/core.xml");
xlnt::workbook wb;
xlnt::workbook_serializer serializer(wb);
pugi::xml_document xml;
serializer.read_properties_core(xml);
auto &prop = wb.get_properties();
TS_ASSERT_EQUALS(prop.excel_base_date, xlnt::calendar::windows_1900);
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()
{
auto path = path_helper::get_data_directory() + "/genuine/empty_libre.xlsx";
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
std::size_t i = 0;
xlnt::workbook wb;
wb.load(path);
wb.load(path_helper::get_data_directory("genuine/empty_libre.xlsx"));
auto title_iter = expected_titles.begin();
for(auto sheet : wb)
for(auto ws : wb)
{
TS_ASSERT_EQUALS(sheet.get_title(), expected_titles.at(i++));
TS_ASSERT_EQUALS(ws.get_title(), *(title_iter++));
}
}
void test_write_properties_core()
{
xlnt::workbook wb;
xlnt::document_properties &prop = wb.get_properties();
prop.creator = "TEST_USER";
prop.last_modified_by = "SOMEBODY";
prop.created = xlnt::datetime(2010, 4, 1, 20, 30, 00);
prop.modified = xlnt::datetime(2010, 4, 5, 14, 5, 30);
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));
xlnt::workbook_serializer serializer(wb);
pugi::xml_document xml;
serializer.write_properties_core(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/core.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/core.xml"), xml));
}
void test_write_properties_app()
{
xlnt::workbook wb;
wb.get_app_properties().application = "Microsoft Excel";
wb.get_app_properties().app_version = "12.0000";
wb.get_app_properties().company = "Company";
wb.set_application("Microsoft Excel");
wb.set_app_version("12.0000");
wb.set_company("Company");
wb.create_sheet();
wb.create_sheet();
xlnt::workbook_serializer serializer(wb);
pugi::xml_document xml;
serializer.write_properties_app(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/app.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/app.xml"), xml));
}
};

View File

@ -38,104 +38,23 @@
#include <detail/include_windows.hpp>
#include <xlnt/packaging/zip_file.hpp>
#include <xlnt/utils/path.hpp>
namespace {
std::string get_working_directory()
void mkdir_recursive(const xlnt::path &path)
{
#ifdef _WIN32
TCHAR buffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buffer);
std::basic_string<TCHAR> working_directory(buffer);
return std::string(working_directory.begin(), working_directory.end());
#else
char buffer[2048];
getcwd(buffer, 2048);
return std::string(buffer);
#endif
}
if (path.exists()) return;
#ifdef _WIN32
char directory_separator = '\\';
char alt_directory_separator = '/';
#else
char directory_separator = '/';
char alt_directory_separator = '\\';
#endif
auto parent = path.parent();
std::string join_path(const std::vector<std::string> &parts)
{
std::string joined;
std::size_t i = 0;
for (auto part : parts)
if (!parent.is_root())
{
joined.append(part);
if (i++ != parts.size() - 1)
{
joined.append(1, '/');
}
}
return joined;
}
std::vector<std::string> split_path(const std::string &path, char delim = directory_separator)
{
std::vector<std::string> split;
std::string::size_type previous_index = 0;
auto separator_index = path.find(delim);
while (separator_index != std::string::npos)
{
auto part = path.substr(previous_index, separator_index - previous_index);
if (part != "..")
{
split.push_back(part);
}
else
{
split.pop_back();
}
previous_index = separator_index + 1;
separator_index = path.find(delim, previous_index);
}
split.push_back(path.substr(previous_index));
if (split.size() == 1 && delim == directory_separator)
{
auto alternative = split_path(path, alt_directory_separator);
if (alternative.size() > 1)
{
return alternative;
}
}
return split;
}
bool directory_exists(const std::string path)
{
struct stat info;
return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}
void mkdir_recursive(const std::string path)
{
if (directory_exists(path)) return;
auto split = split_path(path);
auto last = split.back();
split.pop_back();
if (!split.empty())
{
auto parent = join_path(split);
mkdir_recursive(parent);
}
#ifdef _WIN32
_mkdir(path.c_str());
_mkdir(path.to_string().c_str());
#else
mkdir(path.c_str(), 0755);
#endif
@ -249,7 +168,7 @@ zip_file::zip_file() : archive_(new mz_zip_archive())
reset();
}
zip_file::zip_file(const std::string &filename) : zip_file()
zip_file::zip_file(const path &filename) : zip_file()
{
load(filename);
}
@ -277,10 +196,10 @@ void zip_file::load(std::istream &stream)
start_read();
}
void zip_file::load(const std::string &filename)
void zip_file::load(const path &filename)
{
filename_ = filename;
std::ifstream stream(filename, std::ios::binary);
std::ifstream stream(filename.to_string(), std::ios::binary);
load(stream);
}
@ -292,10 +211,10 @@ void zip_file::load(const std::vector<unsigned char> &bytes)
start_read();
}
void zip_file::save(const std::string &filename)
void zip_file::save(const path &filename)
{
filename_ = filename;
std::ofstream stream(filename, std::ios::binary);
std::ofstream stream(filename.to_string(), std::ios::binary);
save(stream);
}
@ -409,14 +328,14 @@ void zip_file::reset()
mz_zip_writer_end(archive_.get());
}
zip_info zip_file::getinfo(const std::string &name)
zip_info zip_file::getinfo(const path &name)
{
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
{
start_read();
}
int index = mz_zip_reader_locate_file(archive_.get(), name.c_str(), nullptr, 0);
int index = mz_zip_reader_locate_file(archive_.get(), name.to_string('/').c_str(), nullptr, 0);
if (index == -1)
{
@ -438,7 +357,7 @@ zip_info zip_file::getinfo(int index)
zip_info result;
result.filename = std::string(stat.m_filename, stat.m_filename + std::strlen(stat.m_filename));
result.filename = path(std::string(stat.m_filename, stat.m_filename + std::strlen(stat.m_filename)));
result.comment = std::string(stat.m_comment, stat.m_comment + stat.m_comment_size);
result.compress_size = static_cast<std::size_t>(stat.m_comp_size);
result.file_size = static_cast<std::size_t>(stat.m_uncomp_size);
@ -531,40 +450,54 @@ void zip_file::start_write()
mz_zip_writer_init(archive_.get(), 0);
}
void zip_file::write(const std::string &filename)
void zip_file::write_file(const path &filename)
{
auto split = split_path(filename);
if (split.size() > 1)
path arcname(filename);
if (filename.is_absolute())
{
split.erase(split.begin());
arcname = path();
bool first = true;
for (auto part : filename)
{
if (first)
{
first = false;
continue;
}
auto arcname = join_path(split);
write(filename, arcname);
arcname.append(part);
}
}
write_file(filename, arcname);
}
void zip_file::write(const std::string &filename, const std::string &arcname)
void zip_file::write_file(const path &filename, const path &arcname)
{
std::fstream file(filename, std::ios::binary | std::ios::in);
std::fstream file(filename.to_string(), std::ios::binary | std::ios::in);
std::stringstream ss;
ss << file.rdbuf();
std::string bytes = ss.str();
writestr(arcname, bytes);
write_string(ss.str(), arcname);
}
void zip_file::writestr(const std::string &arcname, const std::string &bytes)
void zip_file::write_string(const std::string &bytes, const path &arcname)
{
if (archive_->m_zip_mode != MZ_ZIP_MODE_WRITING)
{
start_write();
}
mz_zip_writer_add_mem(archive_.get(), arcname.c_str(), bytes.data(), bytes.size(), MZ_BEST_COMPRESSION);
mz_zip_writer_add_mem(archive_.get(), arcname.to_string('/').c_str(),
bytes.data(), bytes.size(), MZ_BEST_COMPRESSION);
}
void zip_file::writestr(const zip_info &info, const std::string &bytes)
void zip_file::write_string(const std::string &bytes, const zip_info &info)
{
if (info.filename.empty() || info.date_time.year < 1980)
if (info.filename.to_string().empty() || info.date_time.year < 1980)
{
throw std::runtime_error("must specify a filename and valid date (year >= 1980");
}
@ -576,7 +509,7 @@ void zip_file::writestr(const zip_info &info, const std::string &bytes)
auto crc = crc32buf(bytes.c_str(), bytes.size());
mz_zip_writer_add_mem_ex(archive_.get(), info.filename.c_str(), bytes.data(), bytes.size(),
mz_zip_writer_add_mem_ex(archive_.get(), info.filename.to_string('/').c_str(), bytes.data(), bytes.size(),
info.comment.c_str(), static_cast<mz_uint16>(info.comment.size()),
MZ_BEST_COMPRESSION, 0, crc);
}
@ -584,30 +517,34 @@ void zip_file::writestr(const zip_info &info, const std::string &bytes)
std::string zip_file::read(const zip_info &info)
{
std::size_t size;
char *data =
static_cast<char *>(mz_zip_reader_extract_file_to_heap(archive_.get(), info.filename.c_str(), &size, 0));
if (data == nullptr)
void *data_raw = mz_zip_reader_extract_file_to_heap(archive_.get(),
info.filename.to_string('/').c_str(), &size, 0);
if (data_raw == nullptr)
{
throw std::runtime_error("file couldn't be read");
}
auto data = static_cast<char *>(data_raw);
std::string extracted(data, data + size);
mz_free(data);
return extracted;
}
std::string zip_file::read(const std::string &name)
std::string zip_file::read(const path &name)
{
return read(getinfo(name));
}
bool zip_file::has_file(const std::string &name)
bool zip_file::has_file(const path &name)
{
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
{
start_read();
}
int index = mz_zip_reader_locate_file(archive_.get(), name.c_str(), nullptr, 0);
int index = mz_zip_reader_locate_file(archive_.get(), name.to_string('/').c_str(), nullptr, 0);
return index != -1;
}
@ -634,9 +571,9 @@ std::vector<zip_info> zip_file::infolist()
return info;
}
std::vector<std::string> zip_file::namelist()
std::vector<path> zip_file::namelist()
{
std::vector<std::string> names;
std::vector<path> names;
for (auto &info : infolist())
{
@ -646,7 +583,7 @@ std::vector<std::string> zip_file::namelist()
return names;
}
std::ostream &zip_file::open(const std::string &name)
std::ostream &zip_file::open(const path &name)
{
return open(getinfo(name));
}
@ -659,118 +596,7 @@ std::ostream &zip_file::open(const zip_info &name)
return open_stream_;
}
void zip_file::extract(const std::string &member)
{
extract(member, get_working_directory());
}
void zip_file::extract(const std::string &member, const std::string &path)
{
auto member_split = split_path(member);
auto fullpath_parts = split_path(path);
fullpath_parts.insert(fullpath_parts.end(), member_split.begin(), member_split.end());
auto fullpath = join_path(fullpath_parts);
auto directory = fullpath_parts;
directory.pop_back();
mkdir_recursive(join_path(directory));
std::fstream stream(fullpath, std::ios::binary | std::ios::out);
stream << open(member).rdbuf();
}
void zip_file::extract(const zip_info &member)
{
extract(member, get_working_directory());
}
void zip_file::extract(const zip_info &member, const std::string &path)
{
std::fstream stream(join_path({ path, member.filename }), std::ios::binary | std::ios::out);
stream << open(member).rdbuf();
}
void zip_file::extractall(const std::string &path)
{
extractall(path, infolist());
}
void zip_file::extractall(const std::string &path, const std::vector<std::string> &members)
{
for (auto &member : members)
{
extract(member, path);
}
}
void zip_file::extractall(const std::string &path, const std::vector<zip_info> &members)
{
for (auto &member : members)
{
extract(member, path);
}
}
void zip_file::printdir()
{
printdir(std::cout);
}
void zip_file::printdir(std::ostream &stream)
{
stream << " Length "
<< " "
<< " "
<< "Date"
<< " "
<< " "
<< "Time "
<< " "
<< "Name" << std::endl;
stream << "--------- ---------- ----- ----" << std::endl;
std::size_t sum_length = 0;
std::size_t file_count = 0;
for (auto &member : infolist())
{
sum_length += member.file_size;
file_count++;
std::string length_string = std::to_string(member.file_size);
while (length_string.length() < 9)
{
length_string = " " + length_string;
}
stream << length_string;
stream << " ";
stream << (member.date_time.month < 10 ? "0" : "") << member.date_time.month;
stream << "/";
stream << (member.date_time.day < 10 ? "0" : "") << member.date_time.day;
stream << "/";
stream << member.date_time.year;
stream << " ";
stream << (member.date_time.hours < 10 ? "0" : "") << member.date_time.hours;
stream << ":";
stream << (member.date_time.minutes < 10 ? "0" : "") << member.date_time.minutes;
stream << " ";
stream << member.filename;
stream << std::endl;
}
stream << "--------- -------" << std::endl;
std::string length_string = std::to_string(sum_length);
while (length_string.length() < 9)
{
length_string = " " + length_string;
}
stream << length_string << " " << file_count << " " << (file_count == 1 ? "file" : "files");
stream << std::endl;
}
std::pair<bool, std::string> zip_file::testzip()
bool zip_file::check_crc()
{
if (archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
{
@ -784,14 +610,14 @@ std::pair<bool, std::string> zip_file::testzip()
if (crc != file.crc)
{
return { false, file.filename };
return true;
}
}
return { true, "" };
return false;
}
std::string zip_file::get_filename() const
path zip_file::get_filename() const
{
return filename_;
}

View File

@ -17,11 +17,12 @@ public:
void test_from_simple()
{
pugi::xml_document doc;
auto xml = path_helper::read_file(path_helper::get_data_directory("/reader/styles/simple-styles.xml"));
doc.load(xml.c_str());
doc.load_file(path_helper::get_data_directory("reader/styles/simple-styles.xml").to_string().c_str());
xlnt::workbook wb;
xlnt::excel_serializer e(wb);
xlnt::style_serializer s(e.get_stylesheet());
TS_ASSERT(s.read_stylesheet(doc));
TS_ASSERT_EQUALS(e.get_stylesheet().number_formats.size(), 1);
}
@ -29,11 +30,12 @@ public:
void test_from_complex()
{
pugi::xml_document doc;
auto xml = path_helper::read_file(path_helper::get_data_directory("/reader/styles/complex-styles.xml"));
doc.load(xml.c_str());
doc.load_file(path_helper::get_data_directory("reader/styles/complex-styles.xml").to_string().c_str());
xlnt::workbook wb;
xlnt::excel_serializer e(wb);
xlnt::style_serializer s(e.get_stylesheet());
TS_ASSERT(s.read_stylesheet(doc));
TS_ASSERT_EQUALS(e.get_stylesheet().borders.size(), 7);
TS_ASSERT_EQUALS(e.get_stylesheet().fills.size(), 6);

391
source/utils/path.cpp Normal file
View File

@ -0,0 +1,391 @@
// Copyright (c) 2014-2016 Thomas Fussell
//
// 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
#include <fstream>
#include <sstream>
#include <detail/include_windows.hpp>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <sys/stat.h>
#elif defined(_MSC_VER)
#include <Shlwapi.h>
#elif defined(__linux)
#include <unistd.h>
#include <linux/limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <xlnt/utils/path.hpp>
namespace {
#ifdef _MSC_VER
char system_separator()
{
return '\\';
}
bool is_root(const std::string &part)
{
return part.size() == 2 && part.back() == ':'
&& part.front() >= 'A' && part.front() <= 'Z';
}
bool file_exists(const std::string &path_string)
{
std::wstring path_wide(path_string.begin(), path_string.end());
return PathFileExists(path_wide.c_str()) && !PathIsDirectory(path_wide.c_str());
}
std::string get_working_directory()
{
TCHAR buffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buffer);
std::basic_string<TCHAR> working_directory(buffer);
return std::string(working_directory.begin(), working_directory.end());
}
#else
char system_separator()
{
return '/';
}
bool is_root(const std::string &part)
{
return part.empty();
}
bool file_exists(const std::string &path_string)
{
try
{
struct stat fileAtt;
if (stat(path.c_str(), &fileAtt) == 0)
{
return S_ISREG(fileAtt.st_mode);
}
}
catch (...) {}
return false;
}
std::string get_working_directory()
{
std::size_t buffer_size = 100;
std::vector<char> buffer(buffer_size, '\0');
while (getcwd(&buffer[0], buffer_size) == nullptr)
{
buffer_size *= 2;
buffer.resize(buffer_size, '\0');
}
return std::string(&buffer[0]);
}
#endif
std::vector<std::string> split_path(const std::string &path, char delim)
{
std::vector<std::string> split;
std::string::size_type previous_index = 0;
auto separator_index = path.find(delim);
while (separator_index != std::string::npos)
{
auto part = path.substr(previous_index, separator_index - previous_index);
split.push_back(part);
previous_index = separator_index + 1;
separator_index = path.find(delim, previous_index);
}
// Don't add trailing slash
if (previous_index < path.size())
{
split.push_back(path.substr(previous_index));
}
return split;
}
std::vector<std::string> split_path_universal(const std::string &path)
{
auto initial = split_path(path, system_separator());
if (initial.size() == 1 && system_separator() == '\\')
{
auto alternative = split_path(path, '/');
if (alternative.size() > 1)
{
return alternative;
}
}
return initial;
}
bool directory_exists(const std::string path)
{
struct stat info;
return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}
} // namespace
namespace xlnt {
char path::separator()
{
return system_separator();
}
path::path()
{
}
path::path(const std::string &path_string) : parts_(split_path_universal(path_string))
{
}
path::path(const std::string &path_string, char sep) : parts_(split_path(path_string, sep))
{
}
// general attributes
bool path::is_relative() const
{
return !is_absolute();
}
bool path::is_absolute() const
{
return !parts_.empty() && ::is_root(parts_.front());
}
bool path::is_root() const
{
return parts_.size() == 1 && ::is_root(parts_.front());
}
path path::parent() const
{
path result;
result.parts_ = parts_;
if (result.parts_.size() > 1)
{
result.parts_.pop_back();
}
return result;
}
std::string path::basename()
{
return parts_.empty() ? "" : parts_.back();
}
// conversion
std::string path::to_string(char sep) const
{
if (parts_.empty()) return "";
std::string result;
for (const auto &part : parts_)
{
result.append(part);
result.push_back(sep);
}
result.pop_back();
return result;
}
path path::make_absolute(const path &base_path) const
{
if (is_absolute())
{
return *this;
}
path copy = base_path;
for (const auto &part : parts_)
{
if (part == ".")
{
continue;
}
else if (part == ".." && copy.parts_.size() > 1)
{
copy.parts_.pop_back();
}
else
{
copy.parts_.push_back(part);
}
}
return copy;
}
// filesystem attributes
bool path::exists() const
{
return is_file() || is_directory();
}
bool path::is_directory() const
{
return directory_exists(to_string());
}
bool path::is_file() const
{
return file_exists(to_string());
}
// filesystem
std::string path::read_contents() const
{
std::ifstream f(to_string());
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
// mutators
path &path::append(const std::string &to_append)
{
parts_.push_back(to_append);
return *this;
}
path path::append(const std::string &to_append) const
{
path copy(*this);
copy.append(to_append);
return copy;
}
path &path::append(const path &to_append)
{
parts_.insert(parts_.end(), to_append.begin(), to_append.end());
return *this;
}
path path::append(const path &to_append) const
{
path copy(*this);
copy.append(to_append);
return copy;
}
// iterators
path::iterator path::begin()
{
return parts_.begin();
}
path::iterator path::end()
{
return parts_.end();
}
path::const_iterator path::begin() const
{
return cbegin();
}
path::const_iterator path::end() const
{
return cend();
}
path::const_iterator path::cbegin() const
{
return parts_.cbegin();
}
path::const_iterator path::cend() const
{
return parts_.cend();
}
path::reverse_iterator path::rbegin()
{
return parts_.rbegin();
}
path::reverse_iterator path::rend()
{
return parts_.rend();
}
path::const_reverse_iterator path::rbegin() const
{
return crbegin();
}
path::const_reverse_iterator path::rend() const
{
return crend();
}
path::const_reverse_iterator path::crbegin() const
{
return parts_.crbegin();
}
path::const_reverse_iterator path::crend() const
{
return parts_.crend();
}
std::string path::to_hash_string() const
{
return to_string('/');
}
} // namespace xlnt

View File

@ -11,7 +11,7 @@ class test_zip_file : public CxxTest::TestSuite
public:
test_zip_file()
{
existing_file = path_helper::get_data_directory("/genuine/empty.xlsx");
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 =
@ -35,31 +35,19 @@ public:
" 22849 14 files\n";
}
void remove_temp_file()
bool files_equal(const xlnt::path &left, const xlnt::path &right)
{
std::remove(temp_file.get_filename().c_str());
}
void make_temp_directory()
{
}
void remove_temp_directory()
{
}
bool files_equal(const std::string &a, const std::string &b)
{
if(a == b)
if(left.to_string() == right.to_string())
{
return true;
}
std::ifstream stream_a(a, std::ios::binary), stream_b(a, std::ios::binary);
std::ifstream stream_left(left.to_string(), std::ios::binary);
std::ifstream stream_right(right.to_string(), std::ios::binary);
while(stream_a && stream_b)
while(stream_left && stream_right)
{
if(stream_a.get() != stream_b.get())
if(stream_left.get() != stream_right.get())
{
return false;
}
@ -70,46 +58,46 @@ public:
void test_load_file()
{
remove_temp_file();
temporary_file temp_file;
xlnt::zip_file f(existing_file);
f.save(temp_file.get_filename());
TS_ASSERT(files_equal(existing_file, temp_file.get_filename()));
remove_temp_file();
f.save(temp_file.get_path());
TS_ASSERT(files_equal(existing_file, temp_file.get_path()));
}
void test_load_stream()
{
remove_temp_file();
{
std::ifstream in_stream(existing_file, std::ios::binary);
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_file.get_filename(), std::ios::binary);
std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary);
f.save(out_stream);
}
TS_ASSERT(files_equal(existing_file, temp_file.get_filename()));
remove_temp_file();
out_stream.close();
TS_ASSERT(files_equal(existing_file, temp.get_path()));
}
void test_load_bytes()
{
remove_temp_file();
std::vector<unsigned char> source_bytes, result_bytes;
std::ifstream in_stream(existing_file, std::ios::binary);
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((unsigned char)in_stream.get());
source_bytes.push_back(static_cast<std::uint8_t>(in_stream.get()));
}
xlnt::zip_file f(source_bytes);
f.save(temp_file.get_filename());
f.save(temp_file.get_path());
xlnt::zip_file f2;
f2.load(temp_file.get_filename());
result_bytes = std::vector<unsigned char>();
f2.load(temp_file.get_path());
std::vector<std::uint8_t> result_bytes;
f2.save(result_bytes);
TS_ASSERT(source_bytes == result_bytes);
remove_temp_file();
}
void test_reset()
@ -120,7 +108,7 @@ public:
try
{
f.read("[Content_Types].xml");
f.read(xlnt::path("[Content_Types].xml"));
}
catch(std::exception e)
{
@ -133,28 +121,28 @@ public:
try
{
f.read("[Content_Types].xml");
f.read(xlnt::path("[Content_Types].xml"));
TS_ASSERT(false);
}
catch(std::exception e)
{
}
f.writestr("a", "b");
f.write_string("b", xlnt::path("a"));
f.reset();
TS_ASSERT(f.namelist().empty());
f.writestr("a", "b");
f.write_string("b", xlnt::path("a"));
TS_ASSERT_DIFFERS(f.getinfo("a").file_size, 0);
TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0);
}
void test_getinfo()
{
xlnt::zip_file f(existing_file);
auto info = f.getinfo("[Content_Types].xml");
TS_ASSERT(info.filename == "[Content_Types].xml");
auto info = f.getinfo(xlnt::path("[Content_Types].xml"));
TS_ASSERT(info.filename.to_string() == "[Content_Types].xml");
}
void test_infolist()
@ -173,7 +161,7 @@ public:
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open("[Content_Types].xml").rdbuf();
ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_content_types_string);
}
@ -182,149 +170,84 @@ public:
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open("[Content_Types].xml").rdbuf();
ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf();
std::string result = ss.str();
TS_ASSERT(result == expected_content_types_string);
}
void test_extract_current_directory()
{
xlnt::zip_file f(existing_file);
}
void test_extract_path()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_current_directory()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_path()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_members_name()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_members_info()
{
xlnt::zip_file f(existing_file);
}
void test_printdir()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
f.printdir(ss);
auto printed = ss.str();
TS_ASSERT_EQUALS(printed, expected_printdir_string);
}
void test_read()
{
xlnt::zip_file f(existing_file);
TS_ASSERT(f.read("[Content_Types].xml") == expected_content_types_string);
TS_ASSERT(f.read(f.getinfo("[Content_Types].xml")) == expected_content_types_string);
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.testzip().first);
TS_ASSERT(!f.check_crc());
}
void test_write()
void test_write_file()
{
remove_temp_file();
temporary_file temp_file;
xlnt::zip_file f;
auto text_file = path_helper::get_data_directory("/reader/sharedStrings.xml");
f.write(text_file);
f.write(text_file, "sharedStrings2.xml");
f.save(temp_file.get_filename());
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_filename());
xlnt::zip_file f2(temp_file.get_path());
for(auto &info : f2.infolist())
{
if(info.filename == "sharedStrings2.xml")
if(info.filename.to_string() == "sharedStrings2.xml")
{
TS_ASSERT(f2.read(info) == expected_atxt_string);
}
else if(info.filename.substr(info.filename.size() - 17) == "sharedStrings.xml")
else if(info.filename.basename() == "sharedStrings.xml")
{
TS_ASSERT(f2.read(info) == expected_atxt_string);
}
}
remove_temp_file();
}
void test_writestr()
void test_write_string()
{
remove_temp_file();
xlnt::zip_file f;
f.writestr("a.txt", "a\na");
f.write_string("a\na", xlnt::path("a.txt"));
xlnt::zip_info info;
info.filename = "b.txt";
info.filename = xlnt::path("b.txt");
info.date_time.year = 2014;
f.writestr(info, "b\nb");
f.save(temp_file.get_filename());
f.write_string("b\nb", info);
xlnt::zip_file f2(temp_file.get_filename());
TS_ASSERT(f2.read("a.txt") == "a\na");
TS_ASSERT(f2.read(f2.getinfo("b.txt")) == "b\nb");
temporary_file temp_file;
f.save(temp_file.get_path());
remove_temp_file();
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()
{
remove_temp_file();
xlnt::zip_file f;
f.comment = "comment";
f.save(temp_file.get_filename());
temporary_file temp_file;
f.save(temp_file.get_path());
xlnt::zip_file f2(temp_file.get_filename());
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);
remove_temp_file();
}
void test_extract()
{
xlnt::zip_file f;
f.load(path_helper::get_data_directory("/genuine/empty.xlsx"));
auto expected = path_helper::get_working_directory() + "/xl/styles.xml";
TS_ASSERT(!path_helper::file_exists(expected));
f.extract("xl/styles.xml");
TS_ASSERT(path_helper::file_exists(expected));
path_helper::delete_file(expected);
auto info = f.getinfo("xl/styles.xml");
TS_ASSERT(!path_helper::file_exists(expected));
f.extract(info);
TS_ASSERT(path_helper::file_exists(expected));
path_helper::delete_file(expected);
}
private:
temporary_file temp_file;
std::string existing_file;
xlnt::path existing_file;
std::string expected_content_types_string;
std::string expected_atxt_string;
std::string expected_printdir_string;

View File

@ -21,7 +21,7 @@ public:
xlnt::workbook standard_workbook()
{
xlnt::workbook wb;
auto path = path_helper::get_data_directory("/genuine/empty.xlsx");
auto path = path_helper::get_data_directory("genuine/empty.xlsx");
wb.load(path);
return wb;
@ -34,8 +34,8 @@ public:
void test_read_standard_workbook_from_fileobj()
{
auto path = path_helper::get_data_directory("/genuine/empty.xlsx");
std::ifstream fo(path, std::ios::binary);
auto path = path_helper::get_data_directory("genuine/empty.xlsx");
std::ifstream fo(path.to_string(), std::ios::binary);
xlnt::workbook wb;
TS_ASSERT(wb.load(fo));
@ -55,7 +55,7 @@ public:
void test_read_nostring_workbook()
{
auto path = path_helper::get_data_directory("/genuine/empty-no-string.xlsx");
auto path = path_helper::get_data_directory("genuine/empty-no-string.xlsx");
xlnt::workbook wb;
TS_ASSERT_THROWS_NOTHING(wb.load(path));
@ -63,40 +63,26 @@ public:
void test_read_empty_file()
{
auto path = path_helper::get_data_directory("/reader/null_file.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.load_workbook(path), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_file.xlsx")), xlnt::invalid_file);
}
void test_read_empty_archive()
{
auto path = path_helper::get_data_directory("/reader/null_archive.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.load_workbook(path), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_archive.xlsx")), xlnt::invalid_file);
}
void test_read_workbook_with_no_properties()
{
auto path = path_helper::get_data_directory("/genuine/empty_with_no_properties.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path);
TS_ASSERT_THROWS_NOTHING(wb.load(path_helper::get_data_directory("genuine/empty_with_no_properties.xlsx")));
}
xlnt::workbook workbook_with_styles()
{
auto path = path_helper::get_data_directory("/genuine/empty-with-styles.xlsx");
xlnt::workbook wb;
wb.load(path);
wb.load(path_helper::get_data_directory("genuine/empty-with-styles.xlsx"));
return wb;
}
@ -148,12 +134,9 @@ public:
void test_read_charset_excel()
{
auto path = path_helper::get_data_directory("/reader/charset-excel.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path);
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>();
@ -162,12 +145,9 @@ public:
void test_read_shared_strings_max_range()
{
auto path = path_helper::get_data_directory("/reader/shared_strings-max_range.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path);
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>();
@ -176,12 +156,10 @@ public:
void test_read_shared_strings_multiple_r_nodes()
{
auto path = path_helper::get_data_directory("/reader/shared_strings-multiple_r_nodes.xlsx");
auto path = path_helper::get_data_directory("reader/shared_strings-multiple_r_nodes.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path);
wb.load(path);
auto ws = wb["Sheet1"];
auto val = ws.get_cell("A1").get_value<std::string>();
@ -190,20 +168,16 @@ public:
xlnt::workbook date_mac_1904()
{
auto path = path_helper::get_data_directory("/reader/date_1904.xlsx");
xlnt::workbook wb;
wb.load(path);
wb.load(path_helper::get_data_directory("reader/date_1904.xlsx"));
return wb;
}
xlnt::workbook date_std_1900()
{
auto path = path_helper::get_data_directory("/reader/date_1900.xlsx");
xlnt::workbook wb;
wb.load(path);
wb.load(path_helper::get_data_directory("reader/date_1900.xlsx"));
return wb;
}
@ -211,13 +185,13 @@ public:
void test_read_win_base_date()
{
auto wb = date_std_1900();
TS_ASSERT_EQUALS(wb.get_properties().excel_base_date, xlnt::calendar::windows_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_properties().excel_base_date, xlnt::calendar::mac_1904);
TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::mac_1904);
}
void test_read_date_style_win()
@ -260,7 +234,7 @@ public:
void test_read_no_theme()
{
auto path = path_helper::get_data_directory("/genuine/libreoffice_nrt.xlsx");
auto path = path_helper::get_data_directory("genuine/libreoffice_nrt.xlsx");
xlnt::workbook wb;
TS_ASSERT_THROWS_NOTHING(wb.load(path));
@ -269,7 +243,7 @@ public:
void _test_read_complex_formulae()
{
/*
auto path = PathHelper::GetDataDirectory("/reader/formulae.xlsx");
auto path = PathHelper::GetDataDirectory("reader/formulae.xlsx");
auto wb = xlnt::reader::load_workbook(path);
auto ws = wb.get_active_sheet();
@ -325,12 +299,11 @@ public:
void test_data_only()
{
auto path = path_helper::get_data_directory("/reader/formulae.xlsx");
auto path = path_helper::get_data_directory("reader/formulae.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path, false, true);
wb.set_data_only(true);
wb.load(path);
auto ws = wb.get_active_sheet();
@ -362,7 +335,7 @@ public:
{xlnt::relationship::type::styles, "rId4", "styles.xml"}
};
auto path = path_helper::get_data_directory("/reader/bug137.xlsx");
auto path = path_helper::get_data_directory("reader/bug137.xlsx");
xlnt::zip_file archive(path);
xlnt::relationship_serializer serializer(archive);
@ -382,7 +355,7 @@ public:
{xlnt::relationship::type::theme, "rId4", "/xl/theme/theme.xml"}
};
auto path = path_helper::get_data_directory("/reader/bug304.xlsx");
auto path = path_helper::get_data_directory("reader/bug304.xlsx");
xlnt::zip_file archive(path);
xlnt::relationship_serializer serializer(archive);
@ -410,7 +383,7 @@ public:
{"/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"}
};
auto path = path_helper::get_data_directory("/reader/contains_chartsheets.xlsx");
auto path = path_helper::get_data_directory("reader/contains_chartsheets.xlsx");
xlnt::workbook wb;
wb.load(path);
@ -425,8 +398,8 @@ public:
for(std::size_t i = 0; i < expected.size(); i++)
{
TS_ASSERT(wb.get_manifest().has_override_type(expected[i].first));
TS_ASSERT_EQUALS(wb.get_manifest().get_override_type(expected[i].first), expected[i].second);
TS_ASSERT(wb.get_manifest().has_override_type(xlnt::path(expected[i].first)));
TS_ASSERT_EQUALS(wb.get_manifest().get_override_type(xlnt::path(expected[i].first)), expected[i].second);
}
}
@ -439,12 +412,11 @@ public:
for(const auto &expected : test_cases)
{
std::tie(guess, dtype) = expected;
auto path = path_helper::get_data_directory("/genuine/guess_types.xlsx");
auto path = path_helper::get_data_directory("genuine/guess_types.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path, guess);
wb.set_guess_types(guess);
wb.load(path);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_cell("D2").get_data_type() == dtype);
@ -453,12 +425,10 @@ public:
void test_read_autofilter()
{
auto path = path_helper::get_data_directory("/reader/bug275.xlsx");
auto path = path_helper::get_data_directory("reader/bug275.xlsx");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
serializer.load_workbook(path);
wb.load(path);
auto ws = wb.get_active_sheet();
TS_ASSERT_EQUALS(ws.get_auto_filter().to_string(), "A1:B6");
@ -466,32 +436,23 @@ public:
void test_bad_formats_xlsb()
{
auto path = path_helper::get_data_directory("/genuine/a.xlsb");
auto path = path_helper::get_data_directory("genuine/a.xlsb");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.load_workbook(path), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file);
}
void test_bad_formats_xls()
{
auto path = path_helper::get_data_directory("/genuine/a.xls");
auto path = path_helper::get_data_directory("genuine/a.xls");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.load_workbook(path), xlnt::invalid_file);
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");
auto path = path_helper::get_data_directory("genuine/a.no-format");
xlnt::workbook wb;
xlnt::excel_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.load_workbook(path), xlnt::invalid_file);
TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file);
}
@ -550,7 +511,7 @@ public:
void test_read_inlinestr()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("/genuine/empty.xlsx"));
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");
}

View File

@ -14,7 +14,7 @@ public:
void test_complex_formatting()
{
xlnt::workbook wb;
wb.load(path_helper::get_data_directory("/reader/formatting.xlsx"));
wb.load(path_helper::get_data_directory("reader/formatting.xlsx"));
// border_style
auto ws = wb.get_active_sheet();

View File

@ -3,25 +3,23 @@
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <detail/style_serializer.hpp>
#include <detail/stylesheet.hpp>
#include <detail/workbook_impl.hpp>
#include <helpers/path_helper.hpp>
#include <detail/constants.hpp>
class test_style_writer : public CxxTest::TestSuite
{
public:
bool style_xml_matches(const std::string &expected_string, xlnt::workbook &wb)
{
xlnt::excel_serializer excel_serializer(wb);
xlnt::style_serializer style_serializer(excel_serializer.get_stylesheet());
pugi::xml_document observed;
style_serializer.write_stylesheet(observed);
pugi::xml_document expected;
expected.load(expected_string.c_str());
std::vector<std::uint8_t> bytes;
wb.save(bytes);
auto comparison = xml_helper::compare_xml(expected.root(), observed.root());
return (bool)comparison;
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()
@ -100,8 +98,8 @@ public:
b.set_side(xlnt::border::side::top, prop);
ws.get_cell("D10").set_border(b);
std::string expected = path_helper::read_file(path_helper::get_data_directory("/writer/expected/simple-styles.xml"));
TS_ASSERT(style_xml_matches(expected, wb));
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()

View File

@ -16,7 +16,8 @@ public:
xlnt::workbook wb;
xlnt::theme_serializer serializer;
pugi::xml_document xml;
serializer.write_theme(wb.get_loaded_theme(), xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/theme1.xml", xml));
serializer.write_theme(wb.get_theme(), xml);
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/theme1.xml"), xml));
}
};

View File

@ -181,10 +181,10 @@ public:
auto sheet = book.get_active_sheet();
sheet.get_cell("A1").set_value(today);
temporary_file temp_file;
book.save(temp_file.get_filename());
book.save(temp_file.get_path());
xlnt::workbook test_book;
test_book.load(temp_file.get_filename());
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);
@ -198,10 +198,10 @@ public:
auto sheet = book.get_active_sheet();
sheet.get_cell("A1").set_value(float_value);
temporary_file temp_file;
book.save(temp_file.get_filename());
book.save(temp_file.get_path());
xlnt::workbook test_book;
test_book.load(temp_file.get_filename());
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);
@ -265,13 +265,13 @@ public:
xlnt::override_type o;
TS_ASSERT(o.get_content_type().empty());
TS_ASSERT(o.get_part_name().empty());
TS_ASSERT(o.get_part().to_string().empty());
xlnt::manifest m;
TS_ASSERT(!m.has_default_type("xml"));
TS_ASSERT_THROWS(m.get_default_type("xml"), std::out_of_range);
TS_ASSERT(!m.has_override_type("xl/workbook.xml"));
TS_ASSERT_THROWS(m.get_override_type("xl/workbook.xml"), std::out_of_range);
TS_ASSERT(!m.has_override_type(xlnt::path("xl/workbook.xml")));
TS_ASSERT_THROWS(m.get_override_type(xlnt::path("xl/workbook.xml")), std::out_of_range);
}
void test_get_bad_relationship()
@ -322,19 +322,13 @@ public:
const auto &wb_const = wb;
//TODO these aren't tests...
wb_const.get_app_properties();
wb_const.get_manifest();
TS_ASSERT(!wb.has_loaded_theme());
TS_ASSERT(wb.has_theme());
wb.create_style("style1");
wb.get_style("style1");
wb_const.get_style("style1");
wb.get_style_by_id(0);
}
void test_limits()
{
}
};

View File

@ -21,16 +21,16 @@ public:
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_filename());
wbk.save(temp_file.get_path());
if(path_helper::file_exists(temp_file.get_filename()))
if(temp_file.get_path().exists())
{
path_helper::delete_file(temp_file.get_filename());
path_helper::delete_file(temp_file.get_path());
}
TS_ASSERT(!path_helper::file_exists(temp_file.get_filename()));
wb_.save(temp_file.get_filename());
TS_ASSERT(path_helper::file_exists(temp_file.get_filename()));
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()
@ -50,9 +50,9 @@ public:
xlnt::relationship_serializer serializer(archive);
serializer.write_relationships(wb.get_relationships(), "xl/workbook.xml");
pugi::xml_document xml;
xml.load(archive.read("xl/_rels/workbook.xml.rels").c_str());
xml.load(archive.read(xlnt::path("xl/_rels/workbook.xml.rels")).c_str());
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/workbook.xml.rels", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/workbook.xml.rels"), xml));
}
void test_write_workbook()
@ -62,7 +62,7 @@ public:
pugi::xml_document xml;
serializer.write_workbook(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/workbook.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/workbook.xml"), xml));
}
void test_write_string_table()
@ -77,7 +77,7 @@ public:
pugi::xml_document xml;
xlnt::shared_strings_serializer::write_shared_strings(wb.get_shared_strings(), xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sharedStrings.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sharedStrings.xml"), xml));
}
void test_write_worksheet()
@ -89,7 +89,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1.xml"), xml));
}
void test_write_hidden_worksheet()
@ -102,7 +102,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1.xml"), xml));
}
void test_write_bool()
@ -115,7 +115,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_bool.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_bool.xml"), xml));
}
void test_write_formula()
@ -129,7 +129,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_formula.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_formula.xml"), xml));
}
void test_write_height()
@ -142,7 +142,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_height.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_height.xml"), xml));
}
void test_write_hyperlink()
@ -157,7 +157,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_hyperlink.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml"), xml));
}
void test_write_hyperlink_rels()
@ -175,9 +175,9 @@ public:
xlnt::relationship_serializer serializer(archive);
serializer.write_relationships(ws.get_relationships(), "xl/worksheets/sheet1.xml");
pugi::xml_document xml;
xml.load(archive.read("xl/worksheets/_rels/sheet1.xml.rels").c_str());
xml.load(archive.read(xlnt::path("xl/worksheets/_rels/sheet1.xml.rels")).c_str());
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_hyperlink.xml.rels", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml.rels"), xml));
}
void _test_write_hyperlink_image_rels()
@ -205,13 +205,13 @@ public:
pugi::xml_document xml;
ws_serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_auto_filter.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_auto_filter.xml"), xml));
xlnt::workbook_serializer wb_serializer(wb);
pugi::xml_document xml2;
wb_serializer.write_workbook(xml2);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/workbook_auto_filter.xml", xml2));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"), xml2));
}
void test_write_auto_filter_filter_column()
@ -234,7 +234,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_freeze_panes_horiz.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_horiz.xml"), xml));
}
void test_freeze_panes_vert()
@ -247,7 +247,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_freeze_panes_vert.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_vert.xml"), xml));
}
void test_freeze_panes_both()
@ -260,7 +260,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/sheet1_freeze_panes_both.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_both.xml"), xml));
}
void test_long_number()
@ -272,7 +272,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/long_number.xml", xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/long_number.xml"), xml));
}
void test_short_number()
@ -284,12 +284,7 @@ public:
pugi::xml_document xml;
serializer.write_worksheet(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory() + "/writer/expected/short_number.xml", xml));
}
void _test_write_images()
{
TS_SKIP("not implemented");
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/short_number.xml"), xml));
}
void test_write_page_setup()
@ -318,7 +313,7 @@ public:
xlnt::zip_file archive;
archive.load(bytes);
auto worksheet_xml_string = archive.read("xl/worksheets/sheet1.xml");
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());
@ -342,7 +337,7 @@ public:
" <pageSetup orientation=\"landscape\" paperSize=\"11\" fitToHeight=\"1\" fitToWidth=\"1\" />"
"</worksheet>";
TS_ASSERT(xml_helper::compare_xml(expected, worksheet_xml));
TS_ASSERT(xml_helper::string_matches_document(expected, worksheet_xml));
}
private:

View File

@ -3,6 +3,7 @@
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <detail/xlsx_writer.hpp>
#include <detail/shared_strings_serializer.hpp>
#include <detail/workbook_serializer.hpp>
#include <helpers/path_helper.hpp>
@ -22,7 +23,7 @@ public:
pugi::xml_document xml;
serializer.write_workbook(xml);
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory("/writer/expected/workbook_auto_filter.xml"), xml));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"), xml));
}
void test_write_hidden_worksheet()
@ -53,7 +54,7 @@ public:
pugi::xml_document expected;
expected.load(expected_string.c_str());
TS_ASSERT(xml_helper::compare_xml(expected.child("workbook").child("sheets"),
TS_ASSERT(xml_helper::compare_xml_nodes(expected.child("workbook").child("sheets"),
xml.child("workbook").child("sheets")));
}
@ -74,10 +75,10 @@ public:
xlnt::workbook wb;
temporary_file file;
TS_ASSERT(!file.get_path().exists())
xlnt::excel_serializer serializer(wb);
serializer.save_workbook(file.get_filename());
TS_ASSERT(path_helper::file_exists(file.get_filename()));
wb.save(file.get_path());
TS_ASSERT(file.get_path().exists());
}
void test_write_virtual_workbook()
@ -102,9 +103,9 @@ public:
xlnt::relationship_serializer serializer(archive);
serializer.write_relationships(wb.get_relationships(), "xl/workbook.xml");
pugi::xml_document observed;
observed.load(archive.read("xl/_rels/workbook.xml.rels").c_str());
observed.load(archive.read(xlnt::path("xl/_rels/workbook.xml.rels")).c_str());
TS_ASSERT(xml_helper::compare_xml(path_helper::get_data_directory("/writer/expected/workbook.xml.rels"), observed));
TS_ASSERT(xml_helper::file_matches_document(path_helper::get_data_directory("writer/expected/workbook.xml.rels"), observed));
}
void test_write_workbook_part()
@ -113,9 +114,9 @@ public:
xlnt::workbook_serializer serializer(wb);
pugi::xml_document xml;
serializer.write_workbook(xml);
auto filename = path_helper::get_data_directory("/writer/expected/workbook.xml");
TS_ASSERT(xml_helper::compare_xml(filename, xml));
auto filename = path_helper::get_data_directory("writer/expected/workbook.xml");
TS_ASSERT(xml_helper::file_matches_document(filename, xml));
}
void test_write_named_range()
@ -132,7 +133,7 @@ public:
"<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::compare_xml(expected, xml));
TS_ASSERT(xml_helper::string_matches_document(expected, xml));
}
void test_read_workbook_code_name()
@ -165,7 +166,7 @@ public:
pugi::xml_document expected_xml;
expected_xml.load(expected.c_str());
TS_ASSERT(xml_helper::compare_xml(expected_xml.child("workbook").child("workbookPr"),
TS_ASSERT(xml_helper::compare_xml_nodes(expected_xml.child("workbook").child("workbookPr"),
xml.child("workbook").child("workbookPr")));
}
@ -176,7 +177,7 @@ public:
xlnt::relationship_serializer serializer(archive);
serializer.write_relationships(wb.get_root_relationships(), "");
pugi::xml_document observed;
observed.load(archive.read("_rels/.rels").c_str());
observed.load(archive.read(xlnt::path("_rels/.rels")).c_str());
std::string expected =
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
@ -185,7 +186,7 @@ public:
" <Relationship Id=\"rId3\" Target=\"docProps/app.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"/>"
"</Relationships>";
TS_ASSERT(xml_helper::compare_xml(expected, observed));
TS_ASSERT(xml_helper::string_matches_document(expected, observed));
}
void test_write_shared_strings_with_runs()
@ -223,44 +224,27 @@ public:
" </si>"
"</sst>";
TS_ASSERT(xml_helper::compare_xml(expected, xml));
TS_ASSERT(xml_helper::string_matches_document(expected, xml));
}
void test_write_worksheet_order()
{
auto path = path_helper::get_data_directory("/genuine/tab_order.xlsx");
auto path = path_helper::get_data_directory("genuine/tab_order.xlsx");
// Load an original workbook produced by Excel
xlnt::workbook wb_src;
{
xlnt::excel_serializer serializer(wb_src);
serializer.load_workbook(path);
}
wb_src.load(path);
// Save it to a new file, unmodified
temporary_file file;
{
xlnt::excel_serializer serializer(wb_src);
serializer.save_workbook(file.get_filename());
TS_ASSERT(path_helper::file_exists(file.get_filename()));
}
wb_src.save(file.get_path());
TS_ASSERT(file.get_path().exists());
// Load it again
xlnt::workbook wb_dst;
{
xlnt::excel_serializer serializer(wb_dst);
serializer.load_workbook(file.get_filename());
}
wb_dst.load(file.get_path());
// Make sure the number of worksheets is the same
auto count_src = std::distance(wb_src.begin(), wb_src.end());
auto count_dst = std::distance(wb_dst.begin(), wb_dst.end());
TS_ASSERT(count_src == count_dst);
// Make sure the title of the first sheet matches
auto ws1title_src = wb_src[0].get_title();
auto ws1title_dst = wb_dst[0].get_title();
TS_ASSERT(ws1title_src.compare(ws1title_dst) == 0);
TS_ASSERT_EQUALS(wb_src.get_sheet_titles(), wb_dst.get_sheet_titles());
}
private:

View File

@ -48,6 +48,7 @@
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/protection.hpp>
#include <xlnt/utils/exceptions.hpp>
#include <xlnt/utils/path.hpp>
#include <xlnt/workbook/const_worksheet_iterator.hpp>
#include <xlnt/workbook/named_range.hpp>
#include <xlnt/workbook/theme.hpp>
@ -57,39 +58,64 @@
#include <xlnt/worksheet/worksheet.hpp>
namespace xlnt {
namespace detail {
workbook_impl::workbook_impl()
: active_sheet_index_(0),
guess_types_(false),
data_only_(false)
workbook workbook::minimal()
{
auto impl = new detail::workbook_impl();
workbook wb(impl);
return wb;
}
} // namespace detail
workbook::workbook() : d_(new detail::workbook_impl())
workbook workbook::empty_excel()
{
create_sheet();
auto impl = new detail::workbook_impl();
xlnt::workbook wb(impl);
create_relationship("rId2", "styles.xml", relationship::type::styles);
create_relationship("rId3", "theme/theme1.xml", relationship::type::theme);
wb.set_application("Microsoft Excel");
wb.create_sheet();
wb.add_format(format());
wb.create_style("Normal");
wb.set_theme(theme());
d_->manifest_.add_default_type("rels", "application/vnd.openxmlformats-package.relationships+xml");
d_->manifest_.add_default_type("xml", "application/xml");
auto &manifest = wb.d_->manifest_;
manifest.add_override_type(constants::part_workbook(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
manifest.add_override_type(constants::part_theme(), "application/vnd.openxmlformats-officedocument.theme+xml");
manifest.add_override_type(constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
manifest.add_override_type(constants::part_core(), "application/vnd.openxmlformats-package.core-properties+xml");
manifest.add_override_type(constants::part_app(), "application/vnd.openxmlformats-officedocument.extended-properties+xml");
d_->manifest_.add_override_type("/" + constants::part_workbook(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
d_->manifest_.add_override_type("/" + constants::part_theme(), "application/vnd.openxmlformats-officedocument.theme+xml");
d_->manifest_.add_override_type("/" + constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
d_->manifest_.add_override_type("/" + constants::part_core(), "application/vnd.openxmlformats-package.core-properties+xml");
d_->manifest_.add_override_type("/" + constants::part_app(), "application/vnd.openxmlformats-officedocument.extended-properties+xml");
add_format(format());
create_style("Normal");
d_->stylesheet_.format_styles.front() = "Normal";
wb.d_->stylesheet_.format_styles.front() = "Normal";
xlnt::fill gray125 = xlnt::fill::pattern(xlnt::pattern_fill::type::gray125);
d_->stylesheet_.fills.push_back(gray125);
wb.d_->stylesheet_.fills.push_back(gray125);
return wb;
}
workbook workbook::empty_libre_office()
{
auto impl = new detail::workbook_impl();
workbook wb(impl);
return wb;
}
workbook workbook::empty_numbers()
{
auto impl = new detail::workbook_impl();
workbook wb(impl);
return wb;
}
workbook::workbook()
{
swap(*this, empty_excel());
}
workbook::workbook(detail::workbook_impl *impl) : d_(impl)
{
}
const worksheet workbook::get_sheet_by_name(const std::string &name) const
@ -120,17 +146,29 @@ worksheet workbook::get_sheet_by_name(const std::string &name)
worksheet workbook::get_sheet_by_index(std::size_t index)
{
return worksheet(&d_->worksheets_[index]);
auto iter = d_->worksheets_.begin();
for (std::size_t i = 0; i < index; ++i, ++iter)
{
}
return worksheet(&*iter);
}
const worksheet workbook::get_sheet_by_index(std::size_t index) const
{
return worksheet(&d_->worksheets_.at(index));
auto iter = d_->worksheets_.begin();
for (std::size_t i = 0; i < index; ++i, ++iter)
{
}
return worksheet(&*iter);
}
worksheet workbook::get_active_sheet()
{
return worksheet(&d_->worksheets_[d_->active_sheet_index_]);
return get_sheet_by_index(d_->active_sheet_index_);
}
bool workbook::has_named_range(const std::string &name) const
@ -157,13 +195,13 @@ worksheet workbook::create_sheet()
auto sheet_id = d_->worksheets_.size() + 1;
std::string sheet_filename = "sheet" + std::to_string(sheet_id) + ".xml";
path sheet_path = constants::package_worksheets().append(sheet_filename);
d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
create_relationship("rId" + std::to_string(sheet_id),
"worksheets/" + sheet_filename,
relationship::type::worksheet);
d_->manifest_.add_override_type("/" + constants::package_worksheets()
+ "/" + sheet_filename,
d_->manifest_.add_override_type(sheet_path,
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
return worksheet(&d_->worksheets_.back());
@ -185,7 +223,13 @@ void workbook::copy_sheet(xlnt::worksheet worksheet, std::size_t index)
if (index != d_->worksheets_.size() - 1)
{
d_->worksheets_.insert(d_->worksheets_.begin() + index, d_->worksheets_.back());
auto iter = d_->worksheets_.begin();
for (std::size_t i = 0; i < index; ++i, ++iter)
{
}
d_->worksheets_.insert(iter, d_->worksheets_.back());
d_->worksheets_.pop_back();
}
}
@ -256,6 +300,11 @@ bool workbook::load(const std::vector<unsigned char> &data)
}
bool workbook::load(const std::string &filename)
{
return load(path(filename));
}
bool workbook::load(const path &filename)
{
excel_serializer serializer_(*this);
serializer_.load_workbook(filename);
@ -320,11 +369,17 @@ worksheet workbook::create_sheet(std::size_t index)
if (index != d_->worksheets_.size() - 1)
{
d_->worksheets_.insert(d_->worksheets_.begin() + index, d_->worksheets_.back());
auto iter = d_->worksheets_.begin();
for (std::size_t i = 0; i < index; ++i, ++iter)
{
}
d_->worksheets_.insert(iter, d_->worksheets_.back());
d_->worksheets_.pop_back();
}
return worksheet(&d_->worksheets_[index]);
return get_sheet_by_index(index);
}
worksheet workbook::create_sheet_with_rel(const std::string &title, const relationship &rel)
@ -384,7 +439,7 @@ worksheet workbook::operator[](const std::string &name)
worksheet workbook::operator[](std::size_t index)
{
return worksheet(&d_->worksheets_.at(index));
return get_sheet_by_index(index);
}
void workbook::clear()
@ -392,7 +447,6 @@ void workbook::clear()
d_->worksheets_.clear();
d_->relationships_.clear();
d_->active_sheet_index_ = 0;
d_->properties_ = document_properties();
d_->manifest_.clear();
clear_styles();
clear_formats();
@ -407,6 +461,11 @@ bool workbook::save(std::vector<unsigned char> &data)
}
bool workbook::save(const std::string &filename)
{
return save(path(filename));
}
bool workbook::save(const path &filename)
{
excel_serializer serializer(*this);
serializer.save_workbook(filename);
@ -429,41 +488,26 @@ const std::vector<relationship> &xlnt::workbook::get_relationships() const
return d_->relationships_;
}
document_properties &workbook::get_properties()
{
return d_->properties_;
}
const document_properties &workbook::get_properties() const
{
return d_->properties_;
}
app_properties &workbook::get_app_properties()
{
return d_->app_properties_;
}
const app_properties &workbook::get_app_properties() const
{
return d_->app_properties_;
}
void swap(workbook &left, workbook &right)
{
using std::swap;
swap(left.d_, right.d_);
if (left.d_ != nullptr)
{
for (auto ws : left)
{
ws.set_parent(left);
}
}
if (right.d_ != nullptr)
{
for (auto ws : right)
{
ws.set_parent(right);
}
}
}
workbook &workbook::operator=(workbook other)
@ -472,7 +516,7 @@ workbook &workbook::operator=(workbook other)
return *this;
}
workbook::workbook(workbook &&other) : workbook()
workbook::workbook(workbook &&other) : workbook(nullptr)
{
swap(*this, other);
}
@ -505,16 +549,42 @@ void workbook::set_code_name(const std::string & /*code_name*/)
{
}
bool workbook::has_loaded_theme() const
bool workbook::has_theme() const
{
return false;
return d_->has_theme_;
}
const theme &workbook::get_loaded_theme() const
const theme &workbook::get_theme() const
{
return d_->theme_;
}
void workbook::set_theme(const theme &value)
{
if (!d_->has_theme_)
{
bool has_theme_relationship = false;
for (const auto &rel : get_relationships())
{
if (rel.get_type() == relationship::type::theme)
{
has_theme_relationship = true;
break;
}
}
if (!has_theme_relationship)
{
create_relationship(next_relationship_id(), "theme/theme1.xml", relationship::type::theme);
d_->manifest_.add_override_type(constants::part_theme(), "application/vnd.openxmlformats-officedocument.spreadsheetml.theme+xml");
}
}
d_->has_theme_ = true;
d_->theme_ = value;
}
std::vector<named_range> workbook::get_named_ranges() const
{
std::vector<named_range> named_ranges;
@ -532,11 +602,51 @@ std::vector<named_range> workbook::get_named_ranges() const
std::size_t workbook::add_format(const format &to_add)
{
if (d_->stylesheet_.formats.empty())
{
bool has_style_relationship = false;
for (const auto &rel : get_relationships())
{
if (rel.get_type() == relationship::type::styles)
{
has_style_relationship = true;
break;
}
}
if (!has_style_relationship)
{
create_relationship(next_relationship_id(), "styles.xml", relationship::type::styles);
d_->manifest_.add_override_type(constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
}
}
return d_->stylesheet_.add_format(to_add);
}
std::size_t workbook::add_style(const style &to_add)
{
if (d_->stylesheet_.formats.empty())
{
bool has_style_relationship = false;
for (const auto &rel : get_relationships())
{
if (rel.get_type() == relationship::type::styles)
{
has_style_relationship = true;
break;
}
}
if (!has_style_relationship)
{
create_relationship(next_relationship_id(), "styles.xml", relationship::type::styles);
d_->manifest_.add_override_type(constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
}
}
return d_->stylesheet_.add_style(to_add);
}
@ -604,11 +714,11 @@ const std::vector<relationship> &workbook::get_root_relationships() const
if (d_->root_relationships_.empty())
{
d_->root_relationships_.push_back(
relationship(relationship::type::core_properties, "rId1", constants::part_core()));
relationship(relationship::type::core_properties, "rId1", constants::part_core().to_string()));
d_->root_relationships_.push_back(
relationship(relationship::type::extended_properties, "rId2", constants::part_app()));
relationship(relationship::type::extended_properties, "rId2", constants::part_app().to_string()));
d_->root_relationships_.push_back(
relationship(relationship::type::office_document, "rId3", constants::part_workbook()));
relationship(relationship::type::office_document, "rId3", constants::part_workbook().to_string()));
}
return d_->root_relationships_;
@ -642,7 +752,7 @@ void workbook::add_shared_string(const text &shared, bool allow_duplicates)
if (!has_shared_strings)
{
create_relationship(next_relationship_id(), "sharedStrings.xml", relationship::type::shared_strings);
d_->manifest_.add_override_type("/" + constants::part_shared_strings(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
d_->manifest_.add_override_type(constants::part_shared_strings(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
}
}
@ -723,4 +833,146 @@ std::string workbook::next_relationship_id() const
return "rId" + std::to_string(i);
}
std::string workbook::get_application() const
{
return d_->application_;
}
void workbook::set_application(const std::string &application)
{
d_->write_app_properties_ = true;
d_->application_ = application;
}
calendar workbook::get_base_date() const
{
return d_->base_date_;
}
void workbook::set_base_date(calendar base_date)
{
d_->base_date_ = base_date;
}
std::string workbook::get_creator() const
{
return d_->creator_;
}
void workbook::set_creator(const std::string &creator)
{
d_->creator_ = creator;
}
std::string workbook::get_last_modified_by() const
{
return d_->last_modified_by_;
}
void workbook::set_last_modified_by(const std::string &last_modified_by)
{
d_->last_modified_by_ = last_modified_by;
}
datetime workbook::get_created() const
{
return d_->created_;
}
void workbook::set_created(const datetime &when)
{
d_->created_ = when;
}
datetime workbook::get_modified() const
{
return d_->modified_;
}
void workbook::set_modified(const datetime &when)
{
d_->modified_ = when;
}
int workbook::get_doc_security() const
{
return d_->doc_security_;
}
void workbook::set_doc_security(int doc_security)
{
d_->doc_security_ = doc_security;
}
bool workbook::get_scale_crop() const
{
return d_->scale_crop_;
}
void workbook::set_scale_crop(bool scale_crop)
{
d_->scale_crop_ = scale_crop;
}
std::string workbook::get_company() const
{
return d_->company_;
}
void workbook::set_company(const std::string &company)
{
d_->company_ = company;
}
bool workbook::links_up_to_date() const
{
return d_->links_up_to_date_;
}
void workbook::set_links_up_to_date(bool links_up_to_date)
{
d_->links_up_to_date_ = links_up_to_date;
}
bool workbook::is_shared_doc() const
{
return d_->shared_doc_;
}
void workbook::set_shared_doc(bool shared_doc)
{
d_->shared_doc_ = shared_doc;
}
bool workbook::hyperlinks_changed() const
{
return d_->hyperlinks_changed_;
}
void workbook::set_hyperlinks_changed(bool hyperlinks_changed)
{
d_->hyperlinks_changed_ = hyperlinks_changed;
}
std::string workbook::get_app_version() const
{
return d_->app_version_;
}
void workbook::set_app_version(const std::string &version)
{
d_->app_version_ = version;
}
std::string workbook::get_title() const
{
return d_->title_;
}
void workbook::set_title(const std::string &title)
{
d_->title_ = title;
}
} // namespace xlnt

View File

@ -22,39 +22,8 @@
class path_helper
{
public:
static std::string read_file(const std::string &filename)
static xlnt::path get_executable_directory()
{
std::ifstream f(filename);
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
static std::string windows_to_universal_path(const std::string &windows_path)
{
std::string fixed;
std::stringstream ss(windows_path);
std::string part;
while(std::getline(ss, part, '\\'))
{
if(fixed == "")
{
fixed = part;
}
else
{
fixed += "/" + part;
}
}
return fixed;
}
static std::string get_executable_directory()
{
#ifdef __APPLE__
std::array<char, 1024> path;
uint32_t size = static_cast<uint32_t>(path.size());
@ -74,7 +43,8 @@ public:
{
throw std::runtime_error("GetModuleFileName failed or buffer was too small");
}
return windows_to_universal_path(std::string(buffer.begin(), buffer.begin() + result - 13)) + "/";
return xlnt::path(std::string(buffer.begin(), buffer.begin() + result - 13));
#else
char arg1[20];
char exepath[PATH_MAX + 1] = {0};
@ -86,13 +56,17 @@ public:
#endif
}
static std::string get_working_directory()
static xlnt::path get_working_directory(const std::string &append = "")
{
#ifdef _WIN32
#ifdef _MSC_VER
TCHAR buffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buffer);
std::basic_string<TCHAR> working_directory(buffer);
return windows_to_universal_path(std::string(working_directory.begin(), working_directory.end()));
std::string working_directory_narrow(working_directory.begin(), working_directory.end());
return xlnt::path(working_directory_narrow)
.append(xlnt::path(append));
#else
char buffer[PATH_MAX];
@ -105,59 +79,28 @@ public:
#endif
}
static std::string get_data_directory(const std::string &append = "")
static xlnt::path get_data_directory(const std::string &append = "")
{
auto path = get_executable_directory() + "../../tests/data";
if (!append.empty())
{
if (append.front() != '/')
{
path.push_back('/');
return xlnt::path("../../tests/data")
.make_absolute(get_executable_directory())
.append(xlnt::path(append));
}
path.append(append);
}
return path;
}
static void copy_file(const std::string &source, const std::string &destination, bool overwrite)
static void copy_file(const xlnt::path &source, const xlnt::path &destination, bool overwrite)
{
if(!overwrite && file_exists(destination))
if(!overwrite && destination.exists())
{
throw std::runtime_error("destination file already exists and overwrite==false");
}
std::ifstream src(source, std::ios::binary);
std::ofstream dst(destination, std::ios::binary);
std::ifstream src(source.to_string(), std::ios::binary);
std::ofstream dst(destination.to_string(), std::ios::binary);
dst << src.rdbuf();
}
static void delete_file(const std::string &path)
static void delete_file(const xlnt::path &path)
{
std::remove(path.c_str());
}
static bool file_exists(const std::string &path)
{
#ifdef _MSC_VER
std::wstring path_wide(path.begin(), path.end());
return PathFileExists(path_wide.c_str()) && !PathIsDirectory(path_wide.c_str());
#else
try
{
struct stat fileAtt;
if (stat(path.c_str(), &fileAtt) == 0)
{
return S_ISREG(fileAtt.st_mode);
}
}
catch(...) {}
return false;
#endif
std::remove(path.to_string().c_str());
}
};

View File

@ -7,47 +7,50 @@
#include <detail/include_windows.hpp>
#include <helpers/path_helper.hpp>
class temporary_file
namespace {
static std::string create_temporary_filename()
{
public:
static std::string create()
{
#ifdef _MSC_VER
std::array<TCHAR, MAX_PATH> buffer;
DWORD result = GetTempPath(static_cast<DWORD>(buffer.size()), buffer.data());
if(result > MAX_PATH)
if (result > MAX_PATH)
{
throw std::runtime_error("buffer is too small");
}
if(result == 0)
if (result == 0)
{
throw std::runtime_error("GetTempPath failed");
}
std::string directory(buffer.begin(), buffer.begin() + result);
return path_helper::windows_to_universal_path(directory + "xlnt.xlsx");
return std::string(buffer.begin(), buffer.begin() + result) + "xlnt.xlsx";
#else
return "/tmp/xlnt.xlsx";
#endif
}
temporary_file() : filename_(create())
} // namespace
class temporary_file
{
public:
temporary_file() : path_(create_temporary_filename())
{
if(path_helper::file_exists(get_filename()))
if(path_.exists())
{
std::remove(filename_.c_str());
std::remove(path_.to_string().c_str());
}
}
~temporary_file()
{
std::remove(filename_.c_str());
std::remove(path_.to_string().c_str());
}
std::string get_filename() const { return filename_; }
xlnt::path get_path() const { return path_; }
private:
const std::string filename_;
const xlnt::path path_;
};

View File

@ -32,46 +32,56 @@ public:
}
};
static comparison_result compare_xml(const pugi::xml_document &expected, const pugi::xml_document &observed)
static bool documents_match(const pugi::xml_document &expected,
const pugi::xml_document &observed)
{
return compare_xml(expected.root(), observed.root());
auto result = compare_xml_nodes(expected.root(), observed.root());
if (!result)
{
std::cout << "documents don't match" << std::endl;
std::cout << "expected:" << std::endl;
expected.save(std::cout);
std::cout << std::endl;
std::cout << "observed:" << std::endl;
observed.save(std::cout);
std::cout << std::endl;
return false;
}
static comparison_result compare_xml(const std::string &expected, const pugi::xml_document &observed)
{
std::string expected_contents = expected;
if(path_helper::file_exists(expected))
{
std::ifstream f(expected);
std::ostringstream s;
f >> s.rdbuf();
expected_contents = s.str();
return true;
}
pugi::xml_document expected_xml;
expected_xml.load(expected_contents.c_str());
std::ostringstream ss;
observed.save(ss);
auto observed_string = ss.str();
return compare_xml(expected_xml.root(), observed.root());
static bool file_matches_document(const xlnt::path &expected,
const pugi::xml_document &observed)
{
return string_matches_document(expected.read_contents(), observed);
}
static comparison_result compare_xml(const std::string &left_contents, const std::string &right_contents)
static bool string_matches_document(const std::string &expected_string,
const pugi::xml_document &observed_document)
{
pugi::xml_document expected_document;
expected_document.load(expected_string.c_str());
return documents_match(expected_document, observed_document);
}
static bool strings_match(const std::string &expected_string, const std::string &observed_string)
{
pugi::xml_document left_xml;
left_xml.load(left_contents.c_str());
left_xml.load(expected_string.c_str());
pugi::xml_document right_xml;
right_xml.load(right_contents.c_str());
right_xml.load(observed_string.c_str());
return compare_xml(left_xml.root(), right_xml.root());
return documents_match(left_xml, right_xml);
}
static comparison_result compare_xml(const pugi::xml_node &left, const pugi::xml_node &right)
static comparison_result compare_xml_nodes(const pugi::xml_node &left, const pugi::xml_node &right)
{
std::string left_temp = left.name();
std::string right_temp = right.name();
@ -136,7 +146,7 @@ public:
auto right_child = *right_child_iter;
right_child_iter++;
auto child_comparison_result = compare_xml(left_child, right_child);
auto child_comparison_result = compare_xml_nodes(left_child, right_child);
if(!child_comparison_result)
{