almost done. phew...

This commit is contained in:
Thomas Fussell 2015-10-30 18:54:04 -04:00
parent 84e9dd099e
commit 9dce7b8f0c
42 changed files with 1211 additions and 641 deletions

View File

@ -30,21 +30,20 @@
namespace xlnt { namespace xlnt {
class xml_node; class xml_document;
class comment_serializer class comment_serializer
{ {
comment_serializer(worksheet sheet); comment_serializer(worksheet sheet);
void read_comments(const xml_node &xml); void read_comments(const xml_document &xml);
void read_comments_vml(const xml_node &xml); void read_comments_vml(const xml_document &xml);
xml_node write_comments(); xml_document write_comments() const;
xml_node write_comments_vml(); xml_document write_comments_vml() const;
private: private:
worksheet sheet_; worksheet sheet_;
std::vector<comment> comments_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -34,41 +34,148 @@ namespace xlnt {
class workbook; class workbook;
/// <summary>
/// Handles reading and writing workbooks from an actual XLSX archive
/// using other serializers.
/// </summary>
class excel_serializer class excel_serializer
{ {
public: public:
/// <summary>
///
/// </summary>
static const std::string central_directory_signature(); static const std::string central_directory_signature();
/// <summary>
///
/// </summary>
static std::string repair_central_directory(const std::string &original); static std::string repair_central_directory(const std::string &original);
/// <summary>
/// Construct an excel_serializer which operates on wb.
/// </summary>
excel_serializer(workbook &wb); excel_serializer(workbook &wb);
/// <summary>
/// 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 std::string &filename, bool guess_types = false, bool data_only = false);
/// <summary>
/// Create a ZIP file in memory, load archive from stream, then populate workbook
/// with data from archive.
/// </summary>
bool load_stream_workbook(std::istream &stream, bool guess_types = false, bool data_only = false); bool load_stream_workbook(std::istream &stream, bool guess_types = false, bool data_only = false);
/// <summary>
/// Create a ZIP file in memory, load archive from bytes, then populate workbook
/// with data from archive.
/// </summary>
bool load_virtual_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types = false, bool data_only = false); bool load_virtual_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types = false, bool data_only = false);
/// <summary>
/// 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 std::string &filename, bool as_template = false);
/// <summary>
/// Create a ZIP file in memory, save workbook to this archive, then assign ZIP file
/// binary data to bytes.
/// </summary>
bool save_virtual_workbook(std::vector<std::uint8_t> &bytes, bool as_template = false); bool save_virtual_workbook(std::vector<std::uint8_t> &bytes, bool as_template = false);
/// <summary>
/// Create a ZIP file in memory, save workbook to this archive, then copy ZIP file
/// binary data to stream.
/// </summary>
bool save_stream_workbook(std::ostream &stream, bool as_template = false); bool save_stream_workbook(std::ostream &stream, bool as_template = false);
private: private:
/// <summary>
/// Reads all files in archive and populates workbook with associated data
/// using other appropriate serializers such as workbook_serializer.
/// </summary>
void read_data(bool guess_types, bool data_only); void read_data(bool guess_types, bool data_only);
/// <summary>
/// Read xl/sharedStrings.xml from internal archive and add shared strings to workbook.
/// </summary>
void read_shared_strings(); void read_shared_strings();
/// <summary>
///
/// </summary>
void read_images(); void read_images();
/// <summary>
///
/// </summary>
void read_charts(); void read_charts();
/// <summary>
///
/// </summary>
void read_chartsheets(); void read_chartsheets();
/// <summary>
///
/// </summary>
void read_worksheets(); void read_worksheets();
/// <summary>
///
/// </summary>
void read_external_links(); void read_external_links();
/// <summary>
/// Write all files needed to create a valid XLSX file which represents all
/// data contained in workbook.
/// </summary>
void write_data(bool as_template); void write_data(bool as_template);
/// <summary>
/// Write xl/sharedStrings.xml to internal archive based on shared strings in workbook.
/// </summary>
void write_shared_strings(); void write_shared_strings();
/// <summary>
///
/// </summary>
void write_images(); void write_images();
/// <summary>
///
/// </summary>
void write_charts(); void write_charts();
/// <summary>
///
/// </summary>
void write_chartsheets(); void write_chartsheets();
/// <summary>
///
/// </summary>
void write_worksheets(); void write_worksheets();
/// <summary>
///
/// </summary>
void write_external_links(); void write_external_links();
workbook &wb_; /// <summary>
std::vector<std::string> shared_strings_; /// A reference to the workbook which is the object of read/write operations.
/// </summary>
workbook &workbook_;
/// <summary>
/// The internal archive which holds files representing workbook_ during
/// read/write operations.
/// </summary>
zip_file archive_; zip_file archive_;
}; };

View File

@ -5,17 +5,17 @@
namespace xlnt { namespace xlnt {
class manifest; class manifest;
class workbook;
class xml_document; class xml_document;
class zip_file;
class manifest_serializer class manifest_serializer
{ {
public: public:
manifest_serializer(manifest &m); manifest_serializer(manifest &m);
bool read_manifest(const xml_document &xml); void read_manifest(const xml_document &xml);
bool write_manifest(xml_document &xml); xml_document write_manifest() const;
std::string determine_document_type() const;
private: private:
manifest &manifest_; manifest &manifest_;

View File

@ -6,13 +6,13 @@
namespace xlnt { namespace xlnt {
class relationship; class relationship;
class xml_document; class zip_file;
class relationship_serializer class relationship_serializer
{ {
public: public:
static bool read_relationships(const xml_document &xml, const std::string &dir, std::vector<relationship> &relationships); static std::vector<relationship> read_relationships(zip_file &archive, const std::string &target);
static bool write_relationships(const std::vector<relationship> &relationships, const std::string &dir, xml_document &xml); static bool write_relationships(const std::vector<relationship> &relationships, const std::string &target, zip_file &archive);
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -33,8 +33,8 @@ class xml_document;
class shared_strings_serializer class shared_strings_serializer
{ {
public: public:
bool read_strings(const xml_document &xml, std::vector<std::string> &strings); static bool read_shared_strings(const xml_document &xml, std::vector<std::string> &strings);
bool write_strings(const std::vector<std::string> &strings, xml_document &xml); static xml_document write_shared_strings(const std::vector<std::string> &strings);
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -56,6 +56,10 @@ public:
/// with styles from an existing styles.xml. /// with styles from an existing styles.xml.
style_serializer(workbook &wb); style_serializer(workbook &wb);
//
// Primary methods
//
/// <summary> /// <summary>
/// Load all styles from xml_document into workbook given in constructor. /// Load all styles from xml_document into workbook given in constructor.
/// </summary> /// </summary>
@ -65,28 +69,118 @@ public:
/// Populate parameter xml with an XML tree representing the styles contained in the workbook /// Populate parameter xml with an XML tree representing the styles contained in the workbook
/// given in the constructor. /// given in the constructor.
/// </summary> /// </summary>
bool write_stylesheet(xml_document &xml) const; xml_document write_stylesheet() const;
private: //TODO: These need to be public for unit tests. Could also make test class a friend?
/// <summary> //private:
/// Read a single style from the given node. In styles.xml, this is an "xf" element.
/// A style has an optional border id, fill id, font id, and number format id where //
/// each id is an index in the corresponding list of borders, etc. A style also has // Static element readers (i.e. readers that don't use internal state)
/// optional alignment and protection children. A style also defines whether each of //
/// these is "applied". For example, a style with a defined font id, font=# but with
/// "applyFont=0" will not use the font in formatting.
/// </summary>
style read_style(const xml_node &style_node) const;
/// <summary> /// <summary>
/// Read and return an alignment from alignment_node. /// Read and return an alignment from alignment_node.
/// </summary> /// </summary>
alignment read_alignment(const xml_node &alignment_node) const; static alignment read_alignment(const xml_node &alignment_node);
/// <summary> /// <summary>
/// Read and return a border side from side_node. /// Read and return a border side from side_node.
/// </summary> /// </summary>
side read_side(const xml_node &side_node) const; static side read_side(const xml_node &side_node);
/// <summary>
/// Read and return a border from border_node.
/// </summary>
static border read_border(const xml_node &border_node);
/// <summary>
/// Read and return a fill from fill_node.
/// </summary>
static fill read_fill(const xml_node &fill_node);
/// <summary>
/// Read and return a font from font_node.
/// </summary>
static font read_font(const xml_node &font_node);
/// <summary>
/// Read and return a number format from number_format_node.
/// </summary>
static number_format read_number_format(const xml_node &number_format_node);
/// <summary>
/// Read and return a protection from protection_node.
/// </summary>
static protection read_protection(const xml_node &protection_node);
/// <summary>
/// Read and return all indexed colors from indexed_colors_node.
/// </summary>
static std::vector<color> read_indexed_colors(const xml_node &indexed_colors_node);
/// <summary>
/// Read and return a color from color_node.
/// </summary>
static color read_color(const xml_node &color_node);
/// <summary>
/// Read and return a conditional format, dxf, from conditional_format_node.
/// </summary>
static conditional_format read_conditional_format(const xml_node &conditional_formats_node);
/// <summary>
/// Read and return a pair containing a name and corresponding style from named_style_node.
/// </summary>
static std::pair<std::string, style> read_named_style(const xml_node &named_style_node);
//
// Static element writers (i.e. writers that don't use internal state)
//
/// <summary>
/// Build and return an xml tree representing alignment_.
/// </summary>
static xml_node write_alignment(const alignment &alignment_);
/// <summary>
/// Build and return an xml tree representing border_.
/// </summary>
static xml_node write_border(const border &border_);
/// <summary>
/// Build and return an xml tree representing conditional_format_.
/// </summary>
static xml_node write_conditional_format(const conditional_format &conditional_format_);
/// <summary>
/// Build and return an xml tree representing fill_.
/// </summary>
static xml_node write_fill(const fill &fill_);
/// <summary>
/// Build and return an xml tree representing font_.
/// </summary>
static xml_node write_font(const font &font_);
/// <summary>
/// Build and return two xml trees, first=cell style and second=named style, representing named_style_.
/// </summary>
static std::pair<xml_node, xml_node> write_named_style(const named_style &named_style_);
/// <summary>
/// Build an xml tree representing all named styles in workbook into named_styles_node and cell_styles_node.
/// Returns true on success.
/// </summary>
static std::pair<xml_node, xml_node> write_named_styles();
/// <summary>
/// Build and return an xml tree representing number_format_.
/// </summary>
static xml_node write_number_format(const number_format &number_format_);
//
// Non-static element readers (i.e. readers that modify internal state)
//
/// <summary> /// <summary>
/// Read all borders from borders_node and add them to workbook. /// Read all borders from borders_node and add them to workbook.
@ -94,70 +188,24 @@ private:
/// </summary> /// </summary>
bool read_borders(const xml_node &borders_node); bool read_borders(const xml_node &borders_node);
/// <summary>
/// Read and return a border from border_node.
/// </summary>
border read_border(const xml_node &border_node) const;
/// <summary> /// <summary>
/// Read all fills from fills_node and add them to workbook. /// Read all fills from fills_node and add them to workbook.
/// Return true on success. /// Return true on success.
/// </summary> /// </summary>
bool read_fills(const xml_node &fills_node); bool read_fills(const xml_node &fills_node);
/// <summary>
/// Read and return a fill from fill_node.
/// </summary>
fill read_fill(const xml_node &fill_node) const;
/// <summary> /// <summary>
/// Read all fonts from fonts_node and add them to workbook. /// Read all fonts from fonts_node and add them to workbook.
/// Return true on success. /// Return true on success.
/// </summary> /// </summary>
bool read_fonts(const xml_node &fonts_node); bool read_fonts(const xml_node &fonts_node);
/// <summary>
/// Read and return a font from font_node.
/// </summary>
font read_font(const xml_node &font_node) const;
/// <summary>
/// Read all borders from number_formats_node and add them to workbook.
/// Return true on success.
/// </summary>
bool read_number_formats(const xml_node &number_formats_node);
/// <summary>
/// Read and return a number format from number_format_node.
/// </summary>
number_format read_number_format(const xml_node &number_format_node) const;
/// <summary>
/// Read and return a protection from protection_node.
/// </summary>
protection read_protection(const xml_node &protection_node) const;
/// <summary> /// <summary>
/// Read all colors from colors_node and add them to workbook. /// Read all colors from colors_node and add them to workbook.
/// Return true on success. /// Return true on success.
/// </summary> /// </summary>
bool read_colors(const xml_node &colors_node); bool read_colors(const xml_node &colors_node);
/// <summary>
/// Read and return all indexed colors from indexed_colors_node.
/// </summary>
std::vector<color> read_indexed_colors(const xml_node &indexed_colors_node) const;
/// <summary>
/// Read and return a color from color_node.
/// </summary>
color read_color(const xml_node &color_node) const;
/// <summary>
/// Read and return a conditional format, dxf, from conditional_format_node.
/// </summary>
conditional_format read_conditional_format(const xml_node &conditional_formats_node) const;
/// <summary> /// <summary>
/// Read all named styles from named_style_node and cell_styles_node and add them to workbook. /// Read all named styles from named_style_node and cell_styles_node and add them to workbook.
/// Return true on success. /// Return true on success.
@ -165,19 +213,24 @@ private:
bool read_named_styles(const xml_node &named_styles_node); bool read_named_styles(const xml_node &named_styles_node);
/// <summary> /// <summary>
/// Read and return a pair containing a name and corresponding style from named_style_node. /// Read all borders from number_formats_node and add them to workbook.
/// Return true on success.
/// </summary> /// </summary>
std::pair<std::string, style> read_named_style(const xml_node &named_style_node) const; bool read_number_formats(const xml_node &number_formats_node);
/// <summary> /// <summary>
/// Build and return an xml tree representing alignment_. /// Read a single style from the given node. In styles.xml, this is an "xf" element.
/// A style has an optional border id, fill id, font id, and number format id where
/// each id is an index in the corresponding list of borders, etc. A style also has
/// optional alignment and protection children. A style also defines whether each of
/// these is "applied". For example, a style with a defined font id, font=# but with
/// "applyFont=0" will not use the font in formatting.
/// </summary> /// </summary>
xml_node write_alignment(const alignment &alignment_) const; style read_style(const xml_node &style_node);
/// <summary> //
/// Build and return an xml tree representing border_. // Non-static element writers (i.e. writers that modify internal workbook)
/// </summary> //
xml_node write_border(const border &border_) const;
/// <summary> /// <summary>
/// Build an xml tree representing all borders in workbook into borders_node. /// Build an xml tree representing all borders in workbook into borders_node.
@ -186,9 +239,10 @@ private:
bool write_borders(xml_node &borders_node) const; bool write_borders(xml_node &borders_node) const;
/// <summary> /// <summary>
/// Build and return an xml tree representing fill_. /// Build an xml tree representing all conditional formats in workbook into conditional_formats_node.
/// Returns true on success.
/// </summary> /// </summary>
xml_node write_fill(const fill &fill_) const; bool write_conditional_formats(xml_node &conditional_formats_node) const;
/// <summary> /// <summary>
/// Build an xml tree representing all fills in workbook into fills_node. /// Build an xml tree representing all fills in workbook into fills_node.
@ -196,38 +250,17 @@ private:
/// </summary> /// </summary>
bool write_fills(xml_node &fills_node) const; bool write_fills(xml_node &fills_node) const;
/// <summary>
/// Build and return an xml tree representing font_.
/// </summary>
xml_node write_font(const font &font_) const;
/// <summary> /// <summary>
/// Build an xml tree representing all fonts in workbook into fonts_node. /// Build an xml tree representing all fonts in workbook into fonts_node.
/// Returns true on success. /// Returns true on success.
/// </summary> /// </summary>
bool write_fonts(xml_node &fonts_node) const; bool write_fonts(xml_node &fonts_node) const;
/// <summary>
/// Build and return an xml tree representing number_format_.
/// </summary>
xml_node write_number_format(const number_format &number_format_) const;
/// <summary> /// <summary>
/// Build an xml tree representing all number formats in workbook into number_formats_node. /// Build an xml tree representing all number formats in workbook into number_formats_node.
/// Returns true on success. /// Returns true on success.
/// </summary> /// </summary>
bool write_number_formats(xml_node &fonts_node) const; bool write_number_formats(xml_node fonts_node) const;
/// <summary>
/// Build and return two xml trees, first=cell style and second=named style, representing named_style_.
/// </summary>
std::pair<xml_node, xml_node> write_named_style(const named_style &named_style_) const;
/// <summary>
/// Build an xml tree representing all named styles in workbook into named_styles_node and cell_styles_node.
/// Returns true on success.
/// </summary>
void write_named_styles(xml_node &named_styles_node, xml_node &cell_styles_node) const;
/// <summary> /// <summary>
/// Build an xml tree representing the given style into style_node. /// Build an xml tree representing the given style into style_node.
@ -235,21 +268,11 @@ private:
/// </summary> /// </summary>
bool write_style(const style &style_, xml_node &style_node) const; bool write_style(const style &style_, xml_node &style_node) const;
/// <summary> private:
/// Build and return an xml tree representing conditional_format_.
/// </summary>
xml_node write_conditional_format(const conditional_format &conditional_format_) const;
/// <summary>
/// Build an xml tree representing all conditional formats in workbook into conditional_formats_node.
/// Returns true on success.
/// </summary>
bool write_conditional_formats(xml_node &conditional_formats_node) const;
/// <summary> /// <summary>
/// Set in the constructor, this workbook is used as the source or target for all writing or reading, respectively. /// Set in the constructor, this workbook is used as the source or target for all writing or reading, respectively.
/// </summary> /// </summary>
workbook &wb_; workbook &workbook_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -33,8 +33,10 @@ class xml_document;
class theme_serializer class theme_serializer
{ {
public: public:
//theme read_theme(const xml_document &xml); bool read_theme(const xml_document &xml, theme &t);
xml_document write_theme(const theme &theme_); xml_document write_theme(const theme &t) const;
private:
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -40,8 +40,7 @@ class xml_node;
class workbook_serializer class workbook_serializer
{ {
public: public:
//TODO: does this go here? using string_pair = std::pair<std::string, std::string>;
static std::string determine_document_type(const manifest &manifest_);
workbook_serializer(workbook &wb); workbook_serializer(workbook &wb);
@ -53,15 +52,13 @@ public:
xml_document write_properties_app() const; xml_document write_properties_app() const;
xml_document write_properties_core() const; xml_document write_properties_core() const;
using string_pair = std::pair<std::string, std::string>;
std::vector<string_pair> read_sheets(); std::vector<string_pair> read_sheets();
std::vector<string_pair> detect_worksheets(); std::vector<string_pair> detect_worksheets();
bool write_named_ranges(xml_node &named_ranges_node); xml_node write_named_ranges() const;
private: private:
workbook &wb_; workbook &workbook_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -34,15 +34,14 @@ class relationship;
class workbook; class workbook;
class worksheet; class worksheet;
class xml_document; class xml_document;
class xml_node;
class worksheet_serializer class worksheet_serializer
{ {
public: public:
worksheet_serializer(worksheet sheet); worksheet_serializer(worksheet sheet);
bool read_worksheet(const xml_document &xml, const std::vector<std::string> &shared_strings, const relationship &rel); bool read_worksheet(const xml_document &xml);
bool write_worksheet(const std::vector<std::string> &string_table, xml_document &xml); xml_document write_worksheet() const;
private: private:
worksheet sheet_; worksheet sheet_;

View File

@ -1,25 +1,48 @@
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <xlnt/s11n/xml_node.hpp>
namespace xlnt { namespace xlnt {
namespace detail {
struct xml_document_impl;
} // namespace detail
class xml_node;
class xml_serializer;
class xml_document class xml_document
{ {
public: public:
using string_pair = std::pair<std::string, std::string>;
xml_document();
xml_document(const xml_document &other);
xml_document(xml_document &&other);
~xml_document();
xml_document &operator=(const xml_document &other);
xml_document &operator=(xml_document &&other);
void set_encoding(const std::string &encoding); void set_encoding(const std::string &encoding);
void add_namespace(const std::string &id, const std::string &uri); void add_namespace(const std::string &id, const std::string &uri);
xml_node &root(); xml_node add_child(const xml_node &child);
const xml_node &root() const; xml_node add_child(const std::string &child_name);
xml_node get_root();
const xml_node get_root() const;
xml_node get_child(const std::string &child_name);
const xml_node get_child(const std::string &child_name) const;
std::string to_string() const;
xml_document &from_string(const std::string &xml_string);
private: private:
xml_node root_; friend class xml_serializer;
std::string encoding_; std::unique_ptr<detail::xml_document_impl> d_;
std::vector<string_pair> namespaces_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -1,17 +1,26 @@
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
namespace xlnt { namespace xlnt {
namespace detail {
struct xml_node_impl;
} // namespace detail
using string_pair = std::pair<std::string, std::string>; class xml_document;
class xml_node class xml_node
{ {
public: public:
using string_pair = std::pair<std::string, std::string>;
xml_node(); xml_node();
explicit xml_node(const std::string &name); xml_node(const xml_node &other);
~xml_node();
xml_node &operator=(const xml_node &other);
std::string get_name() const; std::string get_name() const;
void set_name(const std::string &name); void set_name(const std::string &name);
@ -20,24 +29,25 @@ public:
std::string get_text() const; std::string get_text() const;
void set_text(const std::string &text); void set_text(const std::string &text);
const std::vector<xml_node> &get_children() const; const std::vector<xml_node> get_children() const;
bool has_child(const std::string &child_name) const; bool has_child(const std::string &child_name) const;
xml_node &get_child(const std::string &child_name); xml_node get_child(const std::string &child_name);
const xml_node &get_child(const std::string &child_name) const; const xml_node get_child(const std::string &child_name) const;
xml_node &add_child(const xml_node &child); xml_node add_child(const xml_node &child);
xml_node &add_child(const std::string &child_name); xml_node add_child(const std::string &child_name);
const std::vector<string_pair> &get_attributes() const; const std::vector<string_pair> get_attributes() const;
bool has_attribute(const std::string &attribute_name) const; bool has_attribute(const std::string &attribute_name) const;
std::string get_attribute(const std::string &attribute_name) const; std::string get_attribute(const std::string &attribute_name) const;
void add_attribute(const std::string &name, const std::string &value); void add_attribute(const std::string &name, const std::string &value);
std::string to_string() const;
private: private:
std::string name_; friend class xml_document;
bool has_text_ = false; friend class xml_serializer;
std::string text_; xml_node(const detail::xml_node_impl &d);
std::vector<string_pair> attributes_; std::unique_ptr<detail::xml_node_impl> d_;
std::vector<xml_node> children_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -5,12 +5,15 @@
namespace xlnt { namespace xlnt {
class xml_document; class xml_document;
class xml_node;
class xml_serializer class xml_serializer
{ {
public: public:
static std::string serialize(const xml_document &xml); static std::string serialize(const xml_document &xml);
static xml_document deserialize(const std::string &xml_string); static xml_document deserialize(const std::string &xml_string);
static std::string serialize_node(const xml_node &xml);
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -118,7 +118,7 @@ public:
{ {
if(type_ != type::rgb) if(type_ != type::rgb)
{ {
throw std::runtime_error("not theme color"); throw std::runtime_error("not rgb color");
} }
return rgb_string_; return rgb_string_;

View File

@ -249,6 +249,10 @@ public:
const std::vector<relationship> &get_root_relationships() const; const std::vector<relationship> &get_root_relationships() const;
void add_shared_string(const std::string &shared);
std::vector<std::string> &get_shared_strings();
const std::vector<std::string> &get_shared_strings() const;
private: private:
friend class worksheet; friend class worksheet;
std::shared_ptr<detail::workbook_impl> d_; std::shared_ptr<detail::workbook_impl> d_;

View File

@ -361,7 +361,7 @@ section parse_section(const std::string &section_string)
static const std::vector<std::string> bracket_times = { "h", "hh", "m", "mm", "s", "ss" }; static const std::vector<std::string> bracket_times = { "h", "hh", "m", "mm", "s", "ss" };
for(int i = 0; i < section_string.size(); i++) for(std::size_t i = 0; i < section_string.size(); i++)
{ {
if(!in_quotes && section_string[i] == '"') if(!in_quotes && section_string[i] == '"')
{ {

View File

@ -16,6 +16,7 @@ struct workbook_impl
relationships_(other.relationships_), relationships_(other.relationships_),
root_relationships_(other.root_relationships_), root_relationships_(other.root_relationships_),
drawings_(other.drawings_), drawings_(other.drawings_),
shared_strings_(other.shared_strings_),
properties_(other.properties_), properties_(other.properties_),
guess_types_(other.guess_types_), guess_types_(other.guess_types_),
data_only_(other.data_only_), data_only_(other.data_only_),
@ -40,6 +41,8 @@ struct workbook_impl
std::copy(other.root_relationships_.begin(), other.root_relationships_.end(), std::back_inserter(root_relationships_)); std::copy(other.root_relationships_.begin(), other.root_relationships_.end(), std::back_inserter(root_relationships_));
drawings_.clear(); drawings_.clear();
std::copy(other.drawings_.begin(), other.drawings_.end(), back_inserter(drawings_)); std::copy(other.drawings_.begin(), other.drawings_.end(), back_inserter(drawings_));
shared_strings_.clear();
std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_));
properties_ = other.properties_; properties_ = other.properties_;
guess_types_ = other.guess_types_; guess_types_ = other.guess_types_;
data_only_ = other.data_only_; data_only_ = other.data_only_;
@ -59,6 +62,7 @@ struct workbook_impl
std::vector<relationship> relationships_; std::vector<relationship> relationships_;
std::vector<relationship> root_relationships_; std::vector<relationship> root_relationships_;
std::vector<drawing> drawings_; std::vector<drawing> drawings_;
std::vector<std::string> shared_strings_;
document_properties properties_; document_properties properties_;

View File

@ -0,0 +1,17 @@
#pragma once
#include <string>
#include <detail/include_pugixml.hpp>
namespace xlnt {
namespace detail {
struct xml_document_impl
{
std::string encoding;
pugi::xml_document doc;
};
} // namespace detail
} // namespace xlnt

View File

@ -0,0 +1,16 @@
#pragma once
#include <detail/include_pugixml.hpp>
namespace xlnt {
namespace detail {
struct xml_node_impl
{
xml_node_impl() {}
explicit xml_node_impl(pugi::xml_node n) : node(n) {}
pugi::xml_node node;
};
} // namespace detail
} // namespace xlnt

View File

@ -8,6 +8,7 @@
#include <xlnt/s11n/workbook_serializer.hpp> #include <xlnt/s11n/workbook_serializer.hpp>
#include <xlnt/s11n/worksheet_serializer.hpp> #include <xlnt/s11n/worksheet_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp> #include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include <xlnt/s11n/xml_serializer.hpp> #include <xlnt/s11n/xml_serializer.hpp>
#include <xlnt/workbook/document_properties.hpp> #include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/manifest.hpp> #include <xlnt/workbook/manifest.hpp>
@ -43,44 +44,49 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
xlnt::manifest_serializer ms(wb.get_manifest()); xlnt::manifest_serializer ms(wb.get_manifest());
ms.read_manifest(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcContentTypes))); ms.read_manifest(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcContentTypes)));
if(xlnt::workbook_serializer::determine_document_type(wb.get_manifest()) != "excel") if(ms.determine_document_type() != "excel")
{ {
throw xlnt::invalid_file_exception(""); throw xlnt::invalid_file_exception("");
} }
wb.clear(); wb.clear();
std::vector<xlnt::relationship> workbook_relationships; auto workbook_relationships = xlnt::relationship_serializer::read_relationships(archive, xlnt::constants::ArcWorkbook);
xlnt::relationship_serializer::read_relationships(xlnt::xml_serializer::deserialize(archive.read("xl/_rels/workbook.xml.rels")), "", workbook_relationships);
for(auto relationship : workbook_relationships) for(const auto &relationship : workbook_relationships)
{ {
wb.create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type()); wb.create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
} }
auto xml = xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcWorkbook)); auto xml = xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcWorkbook));
auto &root_node = xml.root(); auto root_node = xml.get_child("workbook");
auto &workbook_pr_node = root_node.get_child("workbookPr"); auto workbook_pr_node = root_node.get_child("workbookPr");
wb.get_properties().excel_base_date = (workbook_pr_node.has_attribute("date1904") && workbook_pr_node.get_attribute("date1904") != "0") ? xlnt::calendar::mac_1904 : xlnt::calendar::windows_1900; wb.get_properties().excel_base_date = (workbook_pr_node.has_attribute("date1904") && workbook_pr_node.get_attribute("date1904") != "0") ? xlnt::calendar::mac_1904 : xlnt::calendar::windows_1900;
xlnt::shared_strings_serializer shared_strings_serializer_; xlnt::shared_strings_serializer shared_strings_serializer_;
std::vector<std::string> shared_strings; std::vector<std::string> shared_strings;
shared_strings_serializer_.read_strings(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcSharedString)), shared_strings); shared_strings_serializer_.read_shared_strings(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcSharedString)), shared_strings);
for(auto shared_string : shared_strings)
{
wb.add_shared_string(shared_string);
}
xlnt::style_serializer style_reader_(wb); xlnt::style_serializer style_reader_(wb);
style_reader_.read_stylesheet(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcStyles))); style_reader_.read_stylesheet(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcStyles)));
auto &sheets_node = root_node.get_child("sheets"); auto sheets_node = root_node.get_child("sheets");
for(auto sheet_node : sheets_node.get_children()) for(auto sheet_node : sheets_node.get_children())
{ {
auto rel = wb.get_relationship(sheet_node.get_attribute("r:id")); auto rel = wb.get_relationship(sheet_node.get_attribute("r:id"));
auto ws = wb.create_sheet(sheet_node.get_attribute("name"), rel); auto ws = wb.create_sheet(sheet_node.get_attribute("name"), rel);
auto ws_filename = "xl/" + rel.get_target_uri();
xlnt::worksheet_serializer worksheet_serializer(ws); xlnt::worksheet_serializer worksheet_serializer(ws);
worksheet_serializer.read_worksheet(xlnt::xml_serializer::deserialize(archive.read(rel.get_target_uri())), shared_strings, rel); worksheet_serializer.read_worksheet(xlnt::xml_serializer::deserialize(archive.read(ws_filename)));
} }
return true; return true;
@ -126,52 +132,45 @@ bool excel_serializer::load_workbook(const std::string &filename, bool guess_typ
throw invalid_file_exception(filename); throw invalid_file_exception(filename);
} }
return ::load_workbook(archive_, guess_types, data_only, wb_); return ::load_workbook(archive_, guess_types, data_only, workbook_);
} }
bool excel_serializer::load_virtual_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types, bool data_only) bool excel_serializer::load_virtual_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types, bool data_only)
{ {
archive_.load(bytes); archive_.load(bytes);
return ::load_workbook(archive_, guess_types, data_only, wb_); return ::load_workbook(archive_, guess_types, data_only, workbook_);
} }
excel_serializer::excel_serializer(workbook &wb) : wb_(wb) excel_serializer::excel_serializer(workbook &wb) : workbook_(wb)
{ {
} }
void excel_serializer::write_data(bool as_template) void excel_serializer::write_data(bool /*as_template*/)
{ {
relationship_serializer relationship_serializer_; relationship_serializer relationship_serializer_;
xlnt::xml_document root_rels_xml; relationship_serializer_.write_relationships(workbook_.get_root_relationships(), "", archive_);
relationship_serializer_.write_relationships(wb_.get_root_relationships(), "", root_rels_xml); relationship_serializer_.write_relationships(workbook_.get_relationships(), constants::ArcWorkbook, archive_);
archive_.writestr(constants::ArcRootRels, xml_serializer::serialize(root_rels_xml));
xml_document workbook_rels_xml;
relationship_serializer_.write_relationships(wb_.get_relationships(), "", workbook_rels_xml);
archive_.writestr(constants::ArcWorkbookRels, xml_serializer::serialize(workbook_rels_xml));
xml_document properties_app_xml; xml_document properties_app_xml;
workbook_serializer workbook_serializer_(wb_); workbook_serializer workbook_serializer_(workbook_);
archive_.writestr(constants::ArcApp, xml_serializer::serialize(workbook_serializer_.write_properties_app())); archive_.writestr(constants::ArcApp, xml_serializer::serialize(workbook_serializer_.write_properties_app()));
archive_.writestr(constants::ArcCore, xml_serializer::serialize(workbook_serializer_.write_properties_core())); archive_.writestr(constants::ArcCore, xml_serializer::serialize(workbook_serializer_.write_properties_core()));
theme_serializer theme_serializer_; theme_serializer theme_serializer_;
xml_document theme_xml = theme_serializer_.write_theme(wb_.get_loaded_theme()); archive_.writestr(constants::ArcTheme, theme_serializer_.write_theme(workbook_.get_loaded_theme()).to_string());
archive_.writestr(constants::ArcTheme, xml_serializer::serialize(theme_xml));
xlnt::shared_strings_serializer shared_strings_serializer_;
archive_.writestr(constants::ArcSharedString, xml_serializer::serialize(shared_strings_serializer_.write_shared_strings(workbook_.get_shared_strings())));
archive_.writestr(constants::ArcWorkbook, xml_serializer::serialize(workbook_serializer_.write_workbook())); archive_.writestr(constants::ArcWorkbook, xml_serializer::serialize(workbook_serializer_.write_workbook()));
style_serializer style_serializer_(wb_); style_serializer style_serializer_(workbook_);
xml_document style_xml; archive_.writestr(constants::ArcStyles, style_serializer_.write_stylesheet().to_string());
style_serializer_.write_stylesheet(style_xml);
archive_.writestr(constants::ArcStyles, xml_serializer::serialize(style_xml));
manifest_serializer manifest_serializer_(wb_.get_manifest()); manifest_serializer manifest_serializer_(workbook_.get_manifest());
xml_document manifest_xml; archive_.writestr(constants::ArcContentTypes, manifest_serializer_.write_manifest().to_string());
manifest_serializer_.write_manifest(manifest_xml);
archive_.writestr(constants::ArcContentTypes, xml_serializer::serialize(manifest_xml));
write_worksheets(); write_worksheets();
} }
@ -180,17 +179,16 @@ void excel_serializer::write_worksheets()
{ {
std::size_t index = 0; std::size_t index = 0;
for(auto ws : wb_) for(auto ws : workbook_)
{ {
for(auto relationship : wb_.get_relationships()) for(auto relationship : workbook_.get_relationships())
{ {
if(relationship.get_type() == relationship::type::worksheet && if(relationship.get_type() == relationship::type::worksheet &&
workbook::index_from_ws_filename(relationship.get_target_uri()) == index) workbook::index_from_ws_filename(relationship.get_target_uri()) == index)
{ {
worksheet_serializer serializer_(ws); worksheet_serializer serializer_(ws);
xml_document xml; std::string ws_filename = "xl/" + relationship.get_target_uri();
serializer_.write_worksheet(shared_strings_, xml); archive_.writestr(ws_filename, serializer_.write_worksheet().to_string());
archive_.writestr(relationship.get_target_uri(), xml_serializer::serialize(xml));
break; break;
} }
} }

View File

@ -1,33 +1,81 @@
#include <xlnt/s11n/manifest_serializer.hpp> #include <xlnt/s11n/manifest_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp> #include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include <xlnt/workbook/manifest.hpp> #include <xlnt/workbook/manifest.hpp>
#include "detail/constants.hpp" #include "detail/constants.hpp"
namespace xlnt { namespace xlnt {
bool manifest_serializer::write_manifest(xml_document &xml) manifest_serializer::manifest_serializer(manifest &m) : manifest_(m)
{ {
}
void manifest_serializer::read_manifest(const xml_document &xml)
{
const auto root_node = xml.get_child("Types");
for(const auto child : root_node.get_children())
{
if(child.get_name() == "Default")
{
manifest_.add_default_type(child.get_attribute("Extension"), child.get_attribute("ContentType"));
}
else if(child.get_name() == "Override")
{
manifest_.add_override_type(child.get_attribute("PartName"), child.get_attribute("ContentType"));
}
}
}
xml_document manifest_serializer::write_manifest() const
{
xml_document xml;
auto root_node = xml.add_child("Types");
xml.add_namespace("", constants::Namespaces.at("content-types")); xml.add_namespace("", constants::Namespaces.at("content-types"));
auto root_node = xml.root(); for(const auto default_type : manifest_.get_default_types())
root_node.set_name("Types");
for(const auto &default_type : manifest_.get_default_types())
{ {
auto type_node = root_node.add_child("Default"); auto type_node = root_node.add_child("Default");
type_node.add_attribute("Extension", default_type.get_extension()); type_node.add_attribute("Extension", default_type.get_extension());
type_node.add_attribute("ContentType", default_type.get_extension()); type_node.add_attribute("ContentType", default_type.get_content_type());
} }
for(const auto &override_type : manifest_.get_override_types()) for(const auto override_type : manifest_.get_override_types())
{ {
auto type_node = root_node.add_child("Override"); auto type_node = root_node.add_child("Override");
type_node.add_attribute("PartName", override_type.get_part_name()); type_node.add_attribute("PartName", override_type.get_part_name());
type_node.add_attribute("ContentType", override_type.get_content_type()); type_node.add_attribute("ContentType", override_type.get_content_type());
} }
return true; return xml;
}
std::string manifest_serializer::determine_document_type() const
{
if(!manifest_.has_override_type(constants::ArcWorkbook))
{
return "unsupported";
}
std::string type = manifest_.get_override_type(constants::ArcWorkbook);
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
{
return "excel";
}
else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
{
return "powerpoint";
}
else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
{
return "word";
}
return "unsupported";
} }
} // namespace xlnt } // namespace xlnt

View File

@ -1,17 +1,42 @@
#include <xlnt/s11n/relationship_serializer.hpp> #include <xlnt/s11n/relationship_serializer.hpp>
#include <xlnt/common/relationship.hpp> #include <xlnt/common/relationship.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/s11n/xml_document.hpp> #include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp> #include <xlnt/s11n/xml_node.hpp>
#include "detail/constants.hpp" #include "detail/constants.hpp"
namespace {
std::string make_rels_name(const std::string &target)
{
const char sep = '/';
if(target.empty() || target.back() == sep)
{
return target + "_rels/.rels";
}
auto sep_pos = target.find_last_of(sep);
auto first_part = target.substr(0, sep_pos + 1);
auto last_part = target.substr(sep_pos + 1);
return first_part + "_rels/" + last_part + ".rels";
}
}
namespace xlnt { namespace xlnt {
bool relationship_serializer::read_relationships(const xml_document &xml, const std::string &dir, std::vector<relationship> &relationships) std::vector<relationship> relationship_serializer::read_relationships(zip_file &archive, const std::string &target)
{ {
auto root_node = xml.root(); xml_document xml;
root_node.set_name("Relationships"); xml.from_string(archive.read(make_rels_name(target)));
auto root_node = xml.get_child("Relationships");
std::vector<relationship> relationships;
for(auto relationship_node : root_node.get_children()) for(auto relationship_node : root_node.get_children())
{ {
@ -27,25 +52,21 @@ bool relationship_serializer::read_relationships(const xml_document &xml, const
relationships.push_back(xlnt::relationship(type, id, target)); relationships.push_back(xlnt::relationship(type, id, target));
} }
return true; return relationships;
} }
bool relationship_serializer::write_relationships(const std::vector<relationship> &relationships, const std::string &dir, xml_document &xml) bool relationship_serializer::write_relationships(const std::vector<relationship> &relationships, const std::string &target, zip_file &archive)
{ {
xml.add_namespace("", constants::Namespaces.at("relationships")); xml_document xml;
auto root_node = xml.root(); auto root_node = xml.add_child("Relationships");
root_node.set_name("Relationships");
xml.add_namespace("", constants::Namespaces.at("relationships"));
for(const auto &relationship : relationships) for(const auto &relationship : relationships)
{ {
auto target = relationship.get_target_uri(); auto target = relationship.get_target_uri();
if (dir != "" && target.substr(0, dir.size()) == dir)
{
target = target.substr(dir.size());
}
auto relationship_node = root_node.add_child("Relationship"); auto relationship_node = root_node.add_child("Relationship");
relationship_node.add_attribute("Id", relationship.get_id()); relationship_node.add_attribute("Id", relationship.get_id());
@ -58,6 +79,8 @@ bool relationship_serializer::write_relationships(const std::vector<relationship
} }
} }
archive.writestr(make_rels_name(target), xml.to_string());
return true; return true;
} }

View File

@ -4,18 +4,47 @@
namespace xlnt { namespace xlnt {
bool shared_strings_serializer::read_strings(const xml_document &xml, std::vector<std::string> &strings) xml_document shared_strings_serializer::write_shared_strings(const std::vector<std::string> &strings)
{
xml_document xml;
auto root_node = xml.add_child("sst");
xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.add_attribute("uniqueCount", std::to_string(strings.size()));
for(const auto &string : strings)
{
root_node.add_child("si").add_child("t").set_text(string);
}
return xml;
}
bool shared_strings_serializer::read_shared_strings(const xml_document &xml, std::vector<std::string> &strings)
{ {
strings.clear(); strings.clear();
auto root_node = xml.root(); auto root_node = xml.get_child("sst");
root_node.set_name("sst");
auto unique_count = std::stoull(root_node.get_attribute("uniqueCount")); auto unique_count = std::stoull(root_node.get_attribute("uniqueCount"));
for(const auto &si_node : root_node.get_children()) for(const auto &si_node : root_node.get_children())
{ {
strings.push_back(si_node.get_child("t").get_text()); if(si_node.get_name() != "si")
{
continue;
}
if(si_node.has_child("t"))
{
strings.push_back(si_node.get_child("t").get_text());
}
else if(si_node.has_child("r"))
{
strings.push_back(si_node.get_child("r").get_child("t").get_text());
}
} }
if(unique_count != strings.size()) if(unique_count != strings.size())

View File

@ -132,11 +132,11 @@ xlnt::border_style border_style_from_string(const std::string &border_style_stri
namespace xlnt { namespace xlnt {
style_serializer::style_serializer(workbook &wb) : wb_(wb) style_serializer::style_serializer(workbook &wb) : workbook_(wb)
{ {
} }
protection style_serializer::read_protection(const xml_node &protection_node) const protection style_serializer::read_protection(const xml_node &protection_node)
{ {
protection prot; protection prot;
@ -146,7 +146,7 @@ protection style_serializer::read_protection(const xml_node &protection_node) co
return prot; return prot;
} }
alignment style_serializer::read_alignment(const xml_node &alignment_node) const alignment style_serializer::read_alignment(const xml_node &alignment_node)
{ {
alignment align; alignment align;
@ -219,7 +219,7 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node) const
return align; return align;
} }
style style_serializer::read_style(const xml_node &style_node) const style style_serializer::read_style(const xml_node &style_node)
{ {
style s; style s;
@ -228,9 +228,9 @@ style style_serializer::read_style(const xml_node &style_node) const
bool builtin_format = true; bool builtin_format = true;
for(auto num_fmt : wb_.get_number_formats()) for(auto num_fmt : workbook_.get_number_formats())
{ {
if(num_fmt.get_id() == s.get_number_format_id()) if(static_cast<std::size_t>(num_fmt.get_id()) == s.get_number_format_id())
{ {
s.number_format_ = num_fmt; s.number_format_ = num_fmt;
builtin_format = false; builtin_format = false;
@ -245,15 +245,15 @@ style style_serializer::read_style(const xml_node &style_node) const
s.apply_font(is_true(style_node.get_attribute("applyFont"))); s.apply_font(is_true(style_node.get_attribute("applyFont")));
s.font_id_ = style_node.has_attribute("fontId") ? std::stoull(style_node.get_attribute("fontId")) : 0; s.font_id_ = style_node.has_attribute("fontId") ? std::stoull(style_node.get_attribute("fontId")) : 0;
s.font_ = wb_.get_font(s.font_id_); s.font_ = workbook_.get_font(s.font_id_);
s.apply_fill(is_true(style_node.get_attribute("applyFill"))); s.apply_fill(is_true(style_node.get_attribute("applyFill")));
s.fill_id_ = style_node.has_attribute("fillId") ? std::stoull(style_node.get_attribute("fillId")) : 0; s.fill_id_ = style_node.has_attribute("fillId") ? std::stoull(style_node.get_attribute("fillId")) : 0;
s.fill_ = wb_.get_fill(s.fill_id_); s.fill_ = workbook_.get_fill(s.fill_id_);
s.apply_border(is_true(style_node.get_attribute("applyBorder"))); s.apply_border(is_true(style_node.get_attribute("applyBorder")));
s.border_id_ = style_node.has_attribute("borderId") ? std::stoull(style_node.get_attribute("borderId")) : 0; s.border_id_ = style_node.has_attribute("borderId") ? std::stoull(style_node.get_attribute("borderId")) : 0;
s.border_ = wb_.get_border(s.border_id_); s.border_ = workbook_.get_border(s.border_id_);
s.apply_protection(style_node.has_attribute("protection")); s.apply_protection(style_node.has_attribute("protection"));
@ -276,7 +276,7 @@ style style_serializer::read_style(const xml_node &style_node) const
bool style_serializer::read_stylesheet(const xml_document &xml) bool style_serializer::read_stylesheet(const xml_document &xml)
{ {
auto stylesheet_node = xml.root(); auto stylesheet_node = xml.get_child("styleSheet");
read_borders(stylesheet_node.get_child("borders")); read_borders(stylesheet_node.get_child("borders"));
read_fills(stylesheet_node.get_child("fills")); read_fills(stylesheet_node.get_child("fills"));
@ -293,7 +293,7 @@ bool style_serializer::read_stylesheet(const xml_document &xml)
continue; continue;
} }
wb_.add_style(read_style(xf_node)); workbook_.add_style(read_style(xf_node));
} }
return true; return true;
@ -320,7 +320,7 @@ bool style_serializer::read_number_formats(const xml_node &number_formats_node)
nf.set_format_string(format_string); nf.set_format_string(format_string);
nf.set_id(std::stoull(num_fmt_node.get_attribute("numFmtId"))); nf.set_id(std::stoull(num_fmt_node.get_attribute("numFmtId")));
wb_.add_number_format(nf); workbook_.add_number_format(nf);
} }
return true; return true;
@ -330,13 +330,13 @@ bool style_serializer::read_fonts(const xml_node &fonts_node)
{ {
for(auto font_node : fonts_node.get_children()) for(auto font_node : fonts_node.get_children())
{ {
wb_.add_font(read_font(font_node)); workbook_.add_font(read_font(font_node));
} }
return true; return true;
} }
font style_serializer::read_font(const xlnt::xml_node &font_node) const font style_serializer::read_font(const xlnt::xml_node &font_node)
{ {
font new_font; font new_font;
@ -390,7 +390,7 @@ bool style_serializer::read_colors(const xml_node &colors_node)
for(const auto &indexed_color : indexed_colors) for(const auto &indexed_color : indexed_colors)
{ {
wb_.add_color(indexed_color); workbook_.add_color(indexed_color);
} }
} }
@ -401,13 +401,13 @@ bool style_serializer::read_fills(const xml_node &fills_node)
{ {
for(auto fill_node : fills_node.get_children()) for(auto fill_node : fills_node.get_children())
{ {
wb_.add_fill(read_fill(fill_node)); workbook_.add_fill(read_fill(fill_node));
} }
return true; return true;
} }
fill style_serializer::read_fill(const xml_node &fill_node) const fill style_serializer::read_fill(const xml_node &fill_node)
{ {
fill new_fill; fill new_fill;
@ -432,7 +432,7 @@ fill style_serializer::read_fill(const xml_node &fill_node) const
return new_fill; return new_fill;
} }
side style_serializer::read_side(const xml_node &side_node) const side style_serializer::read_side(const xml_node &side_node)
{ {
side new_side; side new_side;
@ -453,13 +453,13 @@ bool style_serializer::read_borders(const xml_node &borders_node)
{ {
for(auto border_node : borders_node.get_children()) for(auto border_node : borders_node.get_children())
{ {
wb_.add_border(read_border(border_node)); workbook_.add_border(read_border(border_node));
} }
return true; return true;
} }
border style_serializer::read_border(const xml_node &border_node) const border style_serializer::read_border(const xml_node &border_node)
{ {
border new_border; border new_border;
@ -520,21 +520,56 @@ border style_serializer::read_border(const xml_node &border_node) const
return new_border; return new_border;
} }
bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const std::vector<color> style_serializer::read_indexed_colors(const xml_node &indexed_colors_node)
{ {
std::vector<color> colors;
for(auto color_node : indexed_colors_node.get_children())
{
colors.push_back(read_color(color_node));
}
return colors;
}
color style_serializer::read_color(const xml_node &color_node)
{
if(color_node.has_attribute("rgb"))
{
return color(color::type::rgb, color_node.get_attribute("rgb"));
}
else if(color_node.has_attribute("theme"))
{
return color(color::type::theme, std::stoull(color_node.get_attribute("theme")));
}
else if(color_node.has_attribute("indexed"))
{
return color(color::type::indexed, std::stoull(color_node.get_attribute("indexed")));
}
else if(color_node.has_attribute("auto"))
{
return color(color::type::auto_, std::stoull(color_node.get_attribute("auto")));
}
throw std::runtime_error("bad color");
return color();
}
xml_document style_serializer::write_stylesheet() const
{
xml_document xml;
auto style_sheet_node = xml.add_child("styleSheet");
xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
xml.add_namespace("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); xml.add_namespace("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006");
style_sheet_node.add_attribute("mc:Ignorable", "x14ac");
xml.add_namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); xml.add_namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
auto &style_sheet_node = xml.root();
style_sheet_node.set_name("styleSheet");
style_sheet_node.add_attribute("mc:Ignorable", "x14ac");
auto num_fmts_node = style_sheet_node.add_child("numFmts"); auto num_fmts_node = style_sheet_node.add_child("numFmts");
auto num_fmts = wb_.get_number_formats(); auto num_fmts = workbook_.get_number_formats();
num_fmts_node.add_attribute("count", std::to_string(num_fmts.size())); num_fmts_node.add_attribute("count", std::to_string(num_fmts.size()));
for(auto &num_fmt : num_fmts) for(const auto &num_fmt : num_fmts)
{ {
auto num_fmt_node = num_fmts_node.add_child("numFmt"); auto num_fmt_node = num_fmts_node.add_child("numFmt");
num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id())); num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id()));
@ -542,7 +577,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
} }
auto fonts_node = style_sheet_node.add_child("fonts"); auto fonts_node = style_sheet_node.add_child("fonts");
auto fonts = wb_.get_fonts(); auto fonts = workbook_.get_fonts();
if(fonts.empty()) if(fonts.empty())
{ {
@ -620,7 +655,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
} }
auto fills_node = style_sheet_node.add_child("fills"); auto fills_node = style_sheet_node.add_child("fills");
const auto &fills = wb_.get_fills(); const auto &fills = workbook_.get_fills();
fills_node.add_attribute("count", std::to_string(fills.size())); fills_node.add_attribute("count", std::to_string(fills.size()));
for(auto &fill_ : fills) for(auto &fill_ : fills)
@ -635,7 +670,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
if(fill_.has_foreground_color()) if(fill_.has_foreground_color())
{ {
auto &fg_color_node = pattern_fill_node.add_child("fgColor"); auto fg_color_node = pattern_fill_node.add_child("fgColor");
switch(fill_.get_foreground_color().get_type()) switch(fill_.get_foreground_color().get_type())
{ {
@ -689,7 +724,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
} }
auto borders_node = style_sheet_node.add_child("borders"); auto borders_node = style_sheet_node.add_child("borders");
const auto &borders = wb_.get_borders(); const auto &borders = workbook_.get_borders();
borders_node.add_attribute("count", std::to_string(borders.size())); borders_node.add_attribute("count", std::to_string(borders.size()));
for(const auto &border_ : borders) for(const auto &border_ : borders)
@ -764,17 +799,17 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
} }
} }
auto &cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs"); auto cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs");
cell_style_xfs_node.add_attribute("count", "1"); cell_style_xfs_node.add_attribute("count", "1");
auto &style_xf_node = cell_style_xfs_node.add_child("xf"); auto style_xf_node = cell_style_xfs_node.add_child("xf");
style_xf_node.add_attribute("numFmtId", "0"); style_xf_node.add_attribute("numFmtId", "0");
style_xf_node.add_attribute("fontId", "0"); style_xf_node.add_attribute("fontId", "0");
style_xf_node.add_attribute("fillId", "0"); style_xf_node.add_attribute("fillId", "0");
style_xf_node.add_attribute("borderId", "0"); style_xf_node.add_attribute("borderId", "0");
auto cell_xfs_node = style_sheet_node.add_child("cellXfs"); auto cell_xfs_node = style_sheet_node.add_child("cellXfs");
const auto &styles = wb_.get_styles(); const auto &styles = workbook_.get_styles();
cell_xfs_node.add_attribute("count", std::to_string(styles.size())); cell_xfs_node.add_attribute("count", std::to_string(styles.size()));
for(auto &style : styles) for(auto &style : styles)
@ -876,7 +911,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
auto colors_node = style_sheet_node.add_child("colors"); auto colors_node = style_sheet_node.add_child("colors");
auto indexed_colors_node = colors_node.add_child("indexedColors"); auto indexed_colors_node = colors_node.add_child("indexedColors");
for(auto &c : wb_.get_colors()) for(auto &c : workbook_.get_colors())
{ {
indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string()); indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string());
} }
@ -887,16 +922,16 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
ext_node.add_attribute("xmlns:x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"); ext_node.add_attribute("xmlns:x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
ext_node.add_child("x14:slicerStyles").add_attribute("defaultSlicerStyle", "SlicerStyleLight1"); ext_node.add_child("x14:slicerStyles").add_attribute("defaultSlicerStyle", "SlicerStyleLight1");
return true; return xml;
} }
bool style_serializer::write_number_formats(xml_node &number_formats_node) const bool style_serializer::write_number_formats(xml_node number_formats_node) const
{ {
number_formats_node.add_attribute("count", std::to_string(wb_.get_number_formats().size())); number_formats_node.add_attribute("count", std::to_string(workbook_.get_number_formats().size()));
for(const auto &num_fmt : wb_.get_number_formats()) for(const auto &num_fmt : workbook_.get_number_formats())
{ {
auto &num_fmt_node = number_formats_node.add_child("numFmt"); auto num_fmt_node = number_formats_node.add_child("numFmt");
num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id())); num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id()));
num_fmt_node.add_attribute("formatCode", num_fmt.get_format_string()); num_fmt_node.add_attribute("formatCode", num_fmt.get_format_string());
} }

View File

@ -7,17 +7,16 @@
namespace xlnt { namespace xlnt {
//I have no idea what this stuff is. I hope it was worth it. //I have no idea what this stuff is. I hope it was worth it.
xml_document theme_serializer::write_theme(const theme &theme_) xml_document theme_serializer::write_theme(const theme &) const
{ {
xml_document xml; xml_document xml;
auto theme_node = xml.add_child("a:theme");
xml.add_namespace("a", constants::Namespaces.at("drawingml")); xml.add_namespace("a", constants::Namespaces.at("drawingml"));
auto &theme_node = xml.root();
theme_node.set_name("a:theme");
theme_node.add_attribute("name", "Office Theme"); theme_node.add_attribute("name", "Office Theme");
auto &theme_elements_node = theme_node.add_child("a:themeElements");
auto &clr_scheme_node = theme_elements_node.add_child("a:clrScheme"); auto theme_elements_node = theme_node.add_child("a:themeElements");
auto clr_scheme_node = theme_elements_node.add_child("a:clrScheme");
clr_scheme_node.add_attribute("name", "Office"); clr_scheme_node.add_attribute("name", "Office");
struct scheme_element struct scheme_element
@ -112,19 +111,19 @@ xml_document theme_serializer::write_theme(const theme &theme_)
{ {
if(scheme.typeface) if(scheme.typeface)
{ {
auto &major_font_node = major_fonts_node.add_child(scheme.script); auto major_font_node = major_fonts_node.add_child(scheme.script);
major_font_node.add_attribute("typeface", scheme.major); major_font_node.add_attribute("typeface", scheme.major);
auto &minor_font_node = minor_fonts_node.add_child(scheme.script); auto minor_font_node = minor_fonts_node.add_child(scheme.script);
minor_font_node.add_attribute("typeface", scheme.minor); minor_font_node.add_attribute("typeface", scheme.minor);
} }
else else
{ {
auto &major_font_node = major_fonts_node.add_child("a:font"); auto major_font_node = major_fonts_node.add_child("a:font");
major_font_node.add_attribute("script", scheme.script); major_font_node.add_attribute("script", scheme.script);
major_font_node.add_attribute("typeface", scheme.major); major_font_node.add_attribute("typeface", scheme.major);
auto &minor_font_node = minor_fonts_node.add_child("a:font"); auto minor_font_node = minor_fonts_node.add_child("a:font");
minor_font_node.add_attribute("script", scheme.script); minor_font_node.add_attribute("script", scheme.script);
minor_font_node.add_attribute("typeface", scheme.minor); minor_font_node.add_attribute("typeface", scheme.minor);
} }
@ -141,7 +140,7 @@ xml_document theme_serializer::write_theme(const theme &theme_)
auto grad_fill_list = grad_fill_node.add_child("a:gsLst"); auto grad_fill_list = grad_fill_node.add_child("a:gsLst");
auto gs_node = grad_fill_list.add_child("a:gs"); auto gs_node = grad_fill_list.add_child("a:gs");
gs_node.add_attribute("pos", 0); gs_node.add_attribute("pos", "0");
auto scheme_color_node = gs_node.add_child("a:schemeClr"); auto scheme_color_node = gs_node.add_child("a:schemeClr");
scheme_color_node.add_attribute("val", "phClr"); scheme_color_node.add_attribute("val", "phClr");
scheme_color_node.add_child("a:tint").add_attribute("val", "50000"); scheme_color_node.add_child("a:tint").add_attribute("val", "50000");

View File

@ -51,9 +51,13 @@ std::string datetime_to_w3cdtf(const xlnt::datetime &dt)
namespace xlnt { namespace xlnt {
/* workbook_serializer::workbook_serializer(workbook &wb) : workbook_(wb)
std::vector<std::pair<std::string, std::string>> workbook_serializer::read_sheets(zip_file &archive)
{ {
}
std::vector<workbook_serializer::string_pair> workbook_serializer::read_sheets()
{
/*
std::string ns; std::string ns;
for(auto child : doc.children()) for(auto child : doc.children())
@ -72,9 +76,9 @@ std::vector<std::pair<std::string, std::string>> workbook_serializer::read_sheet
auto root_node = doc.get_child(with_ns("workbook")); auto root_node = doc.get_child(with_ns("workbook"));
auto sheets_node = root_node.get_child(with_ns("sheets")); auto sheets_node = root_node.get_child(with_ns("sheets"));
*/
std::vector<std::pair<std::string, std::string>> sheets; std::vector<string_pair> sheets;
/*
// store temp because pugixml iteration uses the internal char array multiple times // store temp because pugixml iteration uses the internal char array multiple times
auto sheet_element_name = with_ns("sheet"); auto sheet_element_name = with_ns("sheet");
@ -84,15 +88,14 @@ std::vector<std::pair<std::string, std::string>> workbook_serializer::read_sheet
std::string name = sheet_node.attribute("name").as_string(); std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name)); sheets.push_back(std::make_pair(id, name));
} }
*/
return sheets; return sheets;
} }
*/
void workbook_serializer::read_properties_core(const xml_document &xml) void workbook_serializer::read_properties_core(const xml_document &xml)
{ {
auto &props = wb_.get_properties(); auto &props = workbook_.get_properties();
auto root_node = xml.root(); auto root_node = xml.get_child("dc:coreProperties");
props.excel_base_date = calendar::windows_1900; props.excel_base_date = calendar::windows_1900;
@ -116,44 +119,19 @@ void workbook_serializer::read_properties_core(const xml_document &xml)
} }
} }
std::string workbook_serializer::determine_document_type(const manifest &manifest)
{
if(!manifest.has_override_type(constants::ArcWorkbook))
{
return "unsupported";
}
std::string type = manifest.get_override_type(constants::ArcWorkbook);
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
{
return "excel";
}
else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
{
return "powerpoint";
}
else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
{
return "word";
}
return "unsupported";
}
/// <summary> /// <summary>
/// Return a list of worksheets. /// Return a list of worksheets.
/// content types has a list of paths but no titles /// content types has a list of paths but no titles
/// workbook has a list of titles and relIds but no paths /// workbook has a list of titles and relIds but no paths
/// workbook_rels has a list of relIds and paths but no titles /// workbook_rels has a list of relIds and paths but no titles
/// </summary> /// </summary>
std::vector<string_pair> workbook_serializer::detect_worksheets() std::vector<workbook_serializer::string_pair> workbook_serializer::detect_worksheets()
{ {
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
std::vector<std::string> valid_sheets; std::vector<std::string> valid_sheets;
for(const auto &content_type : wb_.get_manifest().get_override_types()) for(const auto &content_type : workbook_.get_manifest().get_override_types())
{ {
if(content_type.get_content_type() == ValidWorksheet) if(content_type.get_content_type() == ValidWorksheet)
{ {
@ -161,7 +139,7 @@ std::vector<string_pair> workbook_serializer::detect_worksheets()
} }
} }
auto &workbook_relationships = wb_.get_relationships(); auto &workbook_relationships = workbook_.get_relationships();
std::vector<std::pair<std::string, std::string>> result; std::vector<std::pair<std::string, std::string>> result;
for(const auto &ws : read_sheets()) for(const auto &ws : read_sheets())
@ -180,19 +158,18 @@ std::vector<string_pair> workbook_serializer::detect_worksheets()
xml_document workbook_serializer::write_properties_core() const xml_document workbook_serializer::write_properties_core() const
{ {
auto &props = wb_.get_properties(); auto &props = workbook_.get_properties();
xml_document xml; xml_document xml;
auto root_node = xml.add_child("cp:coreProperties");
xml.add_namespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); xml.add_namespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
xml.add_namespace("dc", "http://purl.org/dc/elements/1.1/"); xml.add_namespace("dc", "http://purl.org/dc/elements/1.1/");
xml.add_namespace("dcmitype", "http://purl.org/dc/dcmitype/"); xml.add_namespace("dcmitype", "http://purl.org/dc/dcmitype/");
xml.add_namespace("dcterms", "http://purl.org/dc/terms/"); xml.add_namespace("dcterms", "http://purl.org/dc/terms/");
xml.add_namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); xml.add_namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
auto &root_node = xml.root();
root_node.set_name("cp:coreProperties");
root_node.add_child("dc:creator").set_text(props.creator); root_node.add_child("dc:creator").set_text(props.creator);
root_node.add_child("cp:lastModifiedBy").set_text(props.last_modified_by); root_node.add_child("cp:lastModifiedBy").set_text(props.last_modified_by);
root_node.add_child("dcterms:created").set_text(datetime_to_w3cdtf(props.created)); root_node.add_child("dcterms:created").set_text(datetime_to_w3cdtf(props.created));
@ -212,11 +189,10 @@ xml_document workbook_serializer::write_properties_app() const
{ {
xml_document xml; xml_document xml;
xml.add_namespace("xmlns", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); auto root_node = xml.add_child("Properties");
xml.add_namespace("xmlns:vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
auto &root_node = xml.root(); xml.add_namespace("", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties");
root_node.set_name("Properties"); xml.add_namespace("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
root_node.add_child("Application").set_text("Microsoft Excel"); root_node.add_child("Application").set_text("Microsoft Excel");
root_node.add_child("DocSecurity").set_text("0"); root_node.add_child("DocSecurity").set_text("0");
@ -232,14 +208,14 @@ xml_document workbook_serializer::write_properties_app() const
heading_pairs_vector_node.add_attribute("baseType", "variant"); heading_pairs_vector_node.add_attribute("baseType", "variant");
heading_pairs_vector_node.add_attribute("size", "2"); heading_pairs_vector_node.add_attribute("size", "2");
heading_pairs_vector_node.add_child("vt:variant").add_child("vt:lpstr").set_text("Worksheets"); heading_pairs_vector_node.add_child("vt:variant").add_child("vt:lpstr").set_text("Worksheets");
heading_pairs_vector_node.add_child("vt:variant").add_child("vt:i4").set_text(std::to_string(wb_.get_sheet_names().size())); heading_pairs_vector_node.add_child("vt:variant").add_child("vt:i4").set_text(std::to_string(workbook_.get_sheet_names().size()));
auto titles_of_parts_node = root_node.add_child("TitlesOfParts"); auto titles_of_parts_node = root_node.add_child("TitlesOfParts");
auto titles_of_parts_vector_node = titles_of_parts_node.add_child("vt:vector"); auto titles_of_parts_vector_node = titles_of_parts_node.add_child("vt:vector");
titles_of_parts_vector_node.add_attribute("baseType", "lpstr"); titles_of_parts_vector_node.add_attribute("baseType", "lpstr");
titles_of_parts_vector_node.add_attribute("size", std::to_string(wb_.get_sheet_names().size())); titles_of_parts_vector_node.add_attribute("size", std::to_string(workbook_.get_sheet_names().size()));
for(auto ws : wb_) for(auto ws : workbook_)
{ {
titles_of_parts_vector_node.add_child("vt:lpstr").set_text(ws.get_title()); titles_of_parts_vector_node.add_child("vt:lpstr").set_text(ws.get_title());
} }
@ -251,7 +227,7 @@ xml_document workbook_serializer::write_workbook() const
{ {
std::size_t num_visible = 0; std::size_t num_visible = 0;
for(auto ws : wb_) for(auto ws : workbook_)
{ {
if(ws.get_page_setup().get_sheet_state() == xlnt::page_setup::sheet_state::visible) if(ws.get_page_setup().get_sheet_state() == xlnt::page_setup::sheet_state::visible)
{ {
@ -266,22 +242,21 @@ xml_document workbook_serializer::write_workbook() const
xml_document xml; xml_document xml;
xml.add_namespace("xmlns", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); auto root_node = xml.add_child("workbook");
xml.add_namespace("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto &root_node = xml.root(); xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
root_node.set_name("workbook"); xml.add_namespace("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
auto &file_version_node = root_node.add_child("fileVersion"); auto file_version_node = root_node.add_child("fileVersion");
file_version_node.add_attribute("appName", "xl"); file_version_node.add_attribute("appName", "xl");
file_version_node.add_attribute("lastEdited", "4"); file_version_node.add_attribute("lastEdited", "4");
file_version_node.add_attribute("lowestEdited", "4"); file_version_node.add_attribute("lowestEdited", "4");
file_version_node.add_attribute("rupBuild", "4505"); file_version_node.add_attribute("rupBuild", "4505");
auto &workbook_pr_node = root_node.add_child("workbookPr"); auto workbook_pr_node = root_node.add_child("workbookPr");
workbook_pr_node.add_attribute("codeName", "ThisWorkbook"); workbook_pr_node.add_attribute("codeName", "ThisWorkbook");
workbook_pr_node.add_attribute("defaultThemeVersion", "124226"); workbook_pr_node.add_attribute("defaultThemeVersion", "124226");
workbook_pr_node.add_attribute("date1904", wb_.get_properties().excel_base_date == calendar::mac_1904 ? "1" : "0"); workbook_pr_node.add_attribute("date1904", workbook_.get_properties().excel_base_date == calendar::mac_1904 ? "1" : "0");
auto book_views_node = root_node.add_child("bookViews"); auto book_views_node = root_node.add_child("bookViews");
auto workbook_view_node = book_views_node.add_child("workbookView"); auto workbook_view_node = book_views_node.add_child("workbookView");
@ -298,10 +273,11 @@ xml_document workbook_serializer::write_workbook() const
auto sheets_node = root_node.add_child("sheets"); auto sheets_node = root_node.add_child("sheets");
auto defined_names_node = root_node.add_child("definedNames"); auto defined_names_node = root_node.add_child("definedNames");
for(auto relationship : wb_.get_relationships()) for(const auto &relationship : workbook_.get_relationships())
{ {
if(relationship.get_type() == relationship::type::worksheet) if(relationship.get_type() == relationship::type::worksheet)
{ {
//TODO: this is ugly
std::string sheet_index_string = relationship.get_target_uri(); std::string sheet_index_string = relationship.get_target_uri();
sheet_index_string = sheet_index_string.substr(0, sheet_index_string.find('.')); sheet_index_string = sheet_index_string.substr(0, sheet_index_string.find('.'));
sheet_index_string = sheet_index_string.substr(sheet_index_string.find_last_of('/')); sheet_index_string = sheet_index_string.substr(sheet_index_string.find_last_of('/'));
@ -312,16 +288,16 @@ xml_document workbook_serializer::write_workbook() const
sheet_index_string = sheet_index_string.substr(static_cast<std::string::size_type>(first_digit + 1)); sheet_index_string = sheet_index_string.substr(static_cast<std::string::size_type>(first_digit + 1));
std::size_t sheet_index = static_cast<std::size_t>(std::stoll(sheet_index_string) - 1); std::size_t sheet_index = static_cast<std::size_t>(std::stoll(sheet_index_string) - 1);
auto ws = wb_.get_sheet_by_index(sheet_index); auto ws = workbook_.get_sheet_by_index(sheet_index);
auto sheet_node = sheets_node.add_child("sheet"); auto sheet_node = sheets_node.add_child("sheet");
sheet_node.add_attribute("name", ws.get_title()); sheet_node.add_attribute("name", ws.get_title());
sheet_node.add_attribute("r:id", relationship.get_id());
sheet_node.add_attribute("sheetId", std::to_string(sheet_index + 1)); sheet_node.add_attribute("sheetId", std::to_string(sheet_index + 1));
sheet_node.add_attribute("r:id", relationship.get_id());
if(ws.has_auto_filter()) if(ws.has_auto_filter())
{ {
auto &defined_name_node = defined_names_node.add_child("definedName"); auto defined_name_node = defined_names_node.add_child("definedName");
defined_name_node.add_attribute("name", "_xlnm._FilterDatabase"); defined_name_node.add_attribute("name", "_xlnm._FilterDatabase");
defined_name_node.add_attribute("hidden", "1"); defined_name_node.add_attribute("hidden", "1");
defined_name_node.add_attribute("localSheetId", "0"); defined_name_node.add_attribute("localSheetId", "0");
@ -339,14 +315,16 @@ xml_document workbook_serializer::write_workbook() const
return xml; return xml;
} }
bool workbook_serializer::write_named_ranges(xlnt::xml_node &named_ranges_node) xml_node workbook_serializer::write_named_ranges() const
{ {
for(auto &named_range : wb_.get_named_ranges()) xlnt::xml_node named_ranges_node;
for(auto &named_range : workbook_.get_named_ranges())
{ {
named_ranges_node.add_child(named_range.get_name()); named_ranges_node.add_child(named_range.get_name());
} }
return true; return named_ranges_node;
} }
} // namespace xlnt } // namespace xlnt

View File

@ -22,9 +22,13 @@ bool is_integral(long double d)
namespace xlnt { namespace xlnt {
bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::vector<std::string> &string_table, const relationship &rel) worksheet_serializer::worksheet_serializer(worksheet sheet) : sheet_(sheet)
{ {
auto &root_node = xml.root(); }
bool worksheet_serializer::read_worksheet(const xml_document &xml)
{
auto &root_node = xml.get_child("worksheet");
auto &dimension_node = root_node.get_child("dimension"); auto &dimension_node = root_node.get_child("dimension");
std::string dimension = dimension_node.get_attribute("ref"); std::string dimension = dimension_node.get_attribute("ref");
@ -53,6 +57,8 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve
} }
} }
auto &shared_strings = sheet_.get_parent().get_shared_strings();
for(auto row_node : sheet_data_node.get_children()) for(auto row_node : sheet_data_node.get_children())
{ {
if(row_node.get_name() != "row") if(row_node.get_name() != "row")
@ -62,7 +68,7 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve
auto row_index = static_cast<row_t>(std::stoull(row_node.get_attribute("r"))); auto row_index = static_cast<row_t>(std::stoull(row_node.get_attribute("r")));
if(row_node.get_attribute("ht") != nullptr) if(row_node.has_attribute("ht"))
{ {
sheet_.get_row_properties(row_index).height = std::stold(row_node.get_attribute("ht")); sheet_.get_row_properties(row_index).height = std::stold(row_node.get_attribute("ht"));
} }
@ -111,10 +117,10 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve
bool has_value = cell_node.has_child("v"); bool has_value = cell_node.has_child("v");
std::string value_string = has_value ? cell_node.get_child("v").get_text() : ""; std::string value_string = has_value ? cell_node.get_child("v").get_text() : "";
bool has_type = cell_node.get_attribute("t") != nullptr; bool has_type = cell_node.has_attribute("t");
std::string type = has_type ? cell_node.get_attribute("t") : ""; std::string type = has_type ? cell_node.get_attribute("t") : "";
bool has_style = cell_node.get_attribute("s") != nullptr; bool has_style = cell_node.has_attribute("s");
int style_id = has_style ? std::stoull(cell_node.get_attribute("s")) : 0; int style_id = has_style ? std::stoull(cell_node.get_attribute("s")) : 0;
bool has_formula = cell_node.has_child("f"); bool has_formula = cell_node.has_child("f");
@ -136,7 +142,7 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve
else if(has_type && type == "s" && !has_formula) // shared string else if(has_type && type == "s" && !has_formula) // shared string
{ {
auto shared_string_index = std::stoull(value_string); auto shared_string_index = std::stoull(value_string);
auto shared_string = string_table.at(shared_string_index); auto shared_string = shared_strings.at(shared_string_index);
cell.set_value(shared_string); cell.set_value(shared_string);
} }
else if(has_type && type == "b") // boolean else if(has_type && type == "b") // boolean
@ -205,34 +211,35 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve
return true; return true;
} }
bool worksheet_serializer::write_worksheet(const std::vector<std::string> &string_table, xml_document &xml) xml_document worksheet_serializer::write_worksheet() const
{ {
sheet_.get_cell("A1"); sheet_.get_cell("A1");
xml_document xml;
auto root_node = xml.add_child("worksheet");
xml.add_namespace("", constants::Namespaces.at("spreadsheetml")); xml.add_namespace("", constants::Namespaces.at("spreadsheetml"));
xml.add_namespace("r", constants::Namespaces.at("r")); xml.add_namespace("r", constants::Namespaces.at("r"));
auto &root_node = xml.root(); auto sheet_pr_node = root_node.add_child("sheetPr");
root_node.set_name("worksheet");
auto &sheet_pr_node = root_node.add_child("sheetPr");
if(!sheet_.get_page_setup().is_default()) if(!sheet_.get_page_setup().is_default())
{ {
auto &page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr"); auto page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr");
page_set_up_pr_node.add_attribute("fitToPage", sheet_.get_page_setup().fit_to_page() ? "1" : "0"); page_set_up_pr_node.add_attribute("fitToPage", sheet_.get_page_setup().fit_to_page() ? "1" : "0");
} }
auto &outline_pr_node = sheet_pr_node.add_child("outlinePr"); auto outline_pr_node = sheet_pr_node.add_child("outlinePr");
outline_pr_node.add_attribute("summaryBelow", "1"); outline_pr_node.add_attribute("summaryBelow", "1");
outline_pr_node.add_attribute("summaryRight", "1"); outline_pr_node.add_attribute("summaryRight", "1");
auto &dimension_node = root_node.add_child("dimension"); auto dimension_node = root_node.add_child("dimension");
dimension_node.add_attribute("ref", sheet_.calculate_dimension().to_string()); dimension_node.add_attribute("ref", sheet_.calculate_dimension().to_string());
auto &sheet_views_node = root_node.add_child("sheetViews"); auto sheet_views_node = root_node.add_child("sheetViews");
auto &sheet_view_node = sheet_views_node.add_child("sheetView"); auto sheet_view_node = sheet_views_node.add_child("sheetView");
sheet_view_node.add_attribute("workbookViewId", "0"); sheet_view_node.add_attribute("workbookViewId", "0");
std::string active_pane = "bottomRight"; std::string active_pane = "bottomRight";
@ -325,6 +332,7 @@ bool worksheet_serializer::write_worksheet(const std::vector<std::string> &strin
std::unordered_map<std::string, std::string> hyperlink_references; std::unordered_map<std::string, std::string> hyperlink_references;
auto sheet_data_node = root_node.add_child("sheetData"); auto sheet_data_node = root_node.add_child("sheetData");
const auto &shared_strings = sheet_.get_parent().get_shared_strings();
for(auto row : sheet_.rows()) for(auto row : sheet_.rows())
{ {
@ -395,9 +403,9 @@ bool worksheet_serializer::write_worksheet(const std::vector<std::string> &strin
int match_index = -1; int match_index = -1;
for(std::size_t i = 0; i < string_table.size(); i++) for(std::size_t i = 0; i < shared_strings.size(); i++)
{ {
if(string_table[i] == cell.get_value<std::string>()) if(shared_strings[i] == cell.get_value<std::string>())
{ {
match_index = static_cast<int>(i); match_index = static_cast<int>(i);
break; break;
@ -545,7 +553,7 @@ bool worksheet_serializer::write_worksheet(const std::vector<std::string> &strin
odd_footer_node.set_text(footer_text); odd_footer_node.set_text(footer_text);
} }
return true; return xml;
} }
} // namespace xlnt } // namespace xlnt

View File

@ -1,25 +1,78 @@
#include <xlnt/s11n/xml_document.hpp> #include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include <xlnt/s11n/xml_serializer.hpp>
#include <detail/xml_document_impl.hpp>
#include <detail/xml_node_impl.hpp>
namespace xlnt { namespace xlnt {
xml_document::xml_document() : d_(new detail::xml_document_impl())
{
}
xml_document::xml_document(const xml_document &other) : xml_document()
{
d_->doc.append_copy(other.d_->doc.root());
}
xml_document::~xml_document()
{
}
void xml_document::set_encoding(const std::string &encoding) void xml_document::set_encoding(const std::string &encoding)
{ {
d_->encoding = encoding;
} }
void xml_document::add_namespace(const std::string &id, const std::string &uri) void xml_document::add_namespace(const std::string &id, const std::string &uri)
{ {
d_->doc.first_child().append_attribute((id.empty() ? "xmlns" : "xmlns:" + id).c_str()).set_value(uri.c_str());
} }
xml_node &xml_document::root() xml_node xml_document::add_child(const xml_node &child)
{ {
return root_; auto child_node = d_->doc.root().append_copy(child.d_->node);
return xml_node(detail::xml_node_impl(child_node));
} }
const xml_node &xml_document::root() const xml_node xml_document::add_child(const std::string &child_name)
{ {
return root_; auto child = d_->doc.root().append_child(child_name.c_str());
return xml_node(detail::xml_node_impl(child));
}
xml_node xml_document::get_root()
{
return xml_node(detail::xml_node_impl(d_->doc.root()));
}
const xml_node xml_document::get_root() const
{
return xml_node(detail::xml_node_impl(d_->doc.root()));
}
std::string xml_document::to_string() const
{
return xml_serializer::serialize(*this);
}
xml_document &xml_document::from_string(const std::string &xml_string)
{
auto doc = xml_serializer::deserialize(xml_string);
std::swap(doc.d_, d_);
return *this;
}
xml_node xml_document::get_child(const std::string &child_name)
{
return xml_node(detail::xml_node_impl(d_->doc.child(child_name.c_str())));
}
const xml_node xml_document::get_child(const std::string &child_name) const
{
return xml_node(detail::xml_node_impl(d_->doc.child(child_name.c_str())));
} }
} // namespace xlnt } // namespace xlnt

View File

@ -1,120 +1,139 @@
#include <xlnt/s11n/xml_node.hpp> #include <xlnt/s11n/xml_node.hpp>
#include <xlnt/s11n/xml_serializer.hpp>
#include <detail/xml_node_impl.hpp>
namespace xlnt { namespace xlnt {
xml_node::xml_node() xml_node::xml_node() : d_(new detail::xml_node_impl)
{ {
} }
xml_node::xml_node(const std::string &name) xml_node::xml_node(const detail::xml_node_impl &d) : xml_node()
{ {
set_name(name); d_->node = d.node;
}
xml_node::~xml_node()
{
}
xml_node::xml_node(const xml_node &other) : xml_node()
{
d_->node = other.d_->node;
}
xml_node &xml_node::operator=(const xlnt::xml_node &other)
{
d_->node = other.d_->node;
return *this;
} }
std::string xml_node::get_name() const std::string xml_node::get_name() const
{ {
return name_; return d_->node.name();
} }
void xml_node::set_name(const std::string &name) void xml_node::set_name(const std::string &name)
{ {
name_ = name; d_->node.set_name(name.c_str());
} }
bool xml_node::has_text() const bool xml_node::has_text() const
{ {
return has_text_; return d_->node.text() != nullptr;
} }
std::string xml_node::get_text() const std::string xml_node::get_text() const
{ {
return text_; return d_->node.text().as_string();
} }
void xml_node::set_text(const std::string &text) void xml_node::set_text(const std::string &text)
{ {
text_ = text; d_->node.text().set(text.c_str());
has_text_ = true;
} }
const std::vector<xml_node> &xml_node::get_children() const const std::vector<xml_node> xml_node::get_children() const
{ {
return children_; std::vector<xml_node> children;
for(auto child : d_->node.children())
{
children.push_back(xml_node(detail::xml_node_impl(child)));
}
return children;
} }
xml_node &xml_node::add_child(const xml_node &child) xml_node xml_node::add_child(const xml_node &child)
{ {
has_text_ = false; auto child_node = xml_node(detail::xml_node_impl(d_->node.append_child(child.get_name().c_str())));
text_.clear();
children_.push_back(child); for(auto attr : child.get_attributes())
return children_.back(); {
child_node.add_attribute(attr.first, attr.second);
}
for(auto child_child : child.get_children())
{
child_node.add_child(child_child);
}
return child_node;
} }
xml_node &xml_node::add_child(const std::string &child_name) xml_node xml_node::add_child(const std::string &child_name)
{ {
return add_child(xml_node(child_name)); return xml_node(detail::xml_node_impl(d_->node.append_child(child_name.c_str())));
} }
const std::vector<string_pair> &xml_node::get_attributes() const const std::vector<xml_node::string_pair> xml_node::get_attributes() const
{ {
return attributes_; std::vector<string_pair> attributes;
for(auto attr : d_->node.attributes())
{
attributes.push_back(std::make_pair<std::string, std::string>(attr.name(), attr.value()));
}
return attributes;
} }
void xml_node::add_attribute(const std::string &name, const std::string &value) void xml_node::add_attribute(const std::string &name, const std::string &value)
{ {
attributes_.push_back(std::make_pair(name, value)); d_->node.append_attribute(name.c_str()).set_value(value.c_str());
} }
bool xml_node::has_attribute(const std::string &attribute_name) const bool xml_node::has_attribute(const std::string &attribute_name) const
{ {
return std::find_if(attributes_.begin(), attributes_.end(), return d_->node.attribute(attribute_name.c_str()) != nullptr;
[&](const string_pair &p) { return p.first == attribute_name; }) != attributes_.end();
} }
std::string xml_node::get_attribute(const std::string &attribute_name) const std::string xml_node::get_attribute(const std::string &attribute_name) const
{ {
auto match = std::find_if(attributes_.begin(), attributes_.end(), return d_->node.attribute(attribute_name.c_str()).value();
[&](const string_pair &p) { return p.first == attribute_name; });
if(match == attributes_.end())
{
throw std::runtime_error("attribute doesn't exist: " + attribute_name);
}
return match->second;
} }
bool xml_node::has_child(const std::string &child_name) const bool xml_node::has_child(const std::string &child_name) const
{ {
return std::find_if(children_.begin(), children_.end(), return d_->node.child(child_name.c_str()) != nullptr;
[&](const xml_node &n) { return n.get_name() == child_name; }) != children_.end();
} }
xml_node &xml_node::get_child(const std::string &child_name) xml_node xml_node::get_child(const std::string &child_name)
{ {
auto match = std::find_if(children_.begin(), children_.end(), return xml_node(detail::xml_node_impl(d_->node.child(child_name.c_str())));
[&](const xml_node &n) { return n.get_name() == child_name; });
if(match == children_.end())
{
throw std::runtime_error("child doesn't exist: " + child_name);
}
return *match;
} }
const xml_node &xml_node::get_child(const std::string &child_name) const const xml_node xml_node::get_child(const std::string &child_name) const
{ {
auto match = std::find_if(children_.begin(), children_.end(), return xml_node(detail::xml_node_impl(d_->node.child(child_name.c_str())));
[&](const xml_node &n) { return n.get_name() == child_name; }); }
if(match == children_.end()) std::string xml_node::to_string() const
{ {
throw std::runtime_error("child doesn't exist: " + child_name); return xml_serializer::serialize_node(*this);
}
return *match;
} }
} // namespace xlnt } // namespace xlnt

View File

@ -5,65 +5,31 @@
#include <xlnt/s11n/xml_node.hpp> #include <xlnt/s11n/xml_node.hpp>
#include "detail/include_pugixml.hpp" #include "detail/include_pugixml.hpp"
#include "detail/xml_document_impl.hpp"
namespace { #include "detail/xml_node_impl.hpp"
void serialize_node(const xlnt::xml_node &source, pugi::xml_node node)
{
for(const auto &attribute : source.get_attributes())
{
node.append_attribute(attribute.first.c_str()).set_value(attribute.second.c_str());
}
if(source.has_text())
{
node.text().set(source.get_text().c_str());
}
else
{
for(const auto &child : source.get_children())
{
pugi::xml_node child_node = node.append_child(child.get_name().c_str());
serialize_node(child, child_node);
}
}
}
xlnt::xml_node deserialize_node(const pugi::xml_node source)
{
xlnt::xml_node node(source.name());
for(const auto attribute : source.attributes())
{
node.add_attribute(attribute.name(), attribute.as_string());
}
if(source.text() != nullptr)
{
node.set_text(source.text().as_string());
}
else
{
for(const auto child : source.children())
{
node.add_child(deserialize_node(child));
}
}
return node;
}
} // namespace
namespace xlnt { namespace xlnt {
std::string xml_serializer::serialize(const xml_document &xml) std::string xml_serializer::serialize(const xml_document &xml)
{ {
pugi::xml_document doc; std::ostringstream ss;
xml.d_->doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8);
auto root = doc.root(); return ss.str();
root.set_name(xml.root().get_name().c_str()); }
serialize_node(xml.root(), root);
xml_document xml_serializer::deserialize(const std::string &xml_string)
{
xml_document doc;
doc.d_->doc.load(xml_string.c_str());
return doc;
}
std::string xml_serializer::serialize_node(const xml_node &xml)
{
pugi::xml_document doc;
doc.append_copy(xml.d_->node);
std::ostringstream ss; std::ostringstream ss;
doc.save(ss); doc.save(ss);
@ -71,15 +37,4 @@ std::string xml_serializer::serialize(const xml_document &xml)
return ss.str(); return ss.str();
} }
xml_document xml_serializer::deserialize(const std::string &xml_string)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
xml_document result;
result.root() = deserialize_node(doc.root());
return result;
}
} // namespace xlnt } // namespace xlnt

View File

@ -1,7 +1,78 @@
#include <xlnt/workbook/manifest.hpp> #include <xlnt/workbook/manifest.hpp>
namespace {
bool match_path(const std::string &path, const std::string &comparand)
{
if(path == comparand)
{
return true;
}
if(path[0] != '/' && path[0] != '.' && comparand[0] == '/')
{
return match_path("/" + path, comparand);
}
else if(comparand[0] != '/' && comparand[0] != '.' && path[0] == '/')
{
return match_path(path, "/" + comparand);
}
return false;
}
} // namespace
namespace xlnt { namespace xlnt {
default_type::default_type()
{
}
default_type::default_type(const std::string &extension, const std::string &content_type) :
extension_(extension),
content_type_(content_type)
{
}
default_type::default_type(const default_type &other) :
extension_(other.extension_),
content_type_(other.content_type_)
{
}
default_type &default_type::operator=(const default_type &other)
{
extension_ = other.extension_;
content_type_ = other.content_type_;
return *this;
}
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 override_type &other) :
part_name_(other.part_name_),
content_type_(other.content_type_)
{
}
override_type &override_type::operator=(const override_type &other)
{
part_name_ = other.part_name_;
content_type_ = other.content_type_;
return *this;
}
bool manifest::has_default_type(const std::string &extension) const bool manifest::has_default_type(const std::string &extension) const
{ {
return std::find_if(default_types_.begin(), default_types_.end(), return std::find_if(default_types_.begin(), default_types_.end(),
@ -11,7 +82,17 @@ bool manifest::has_default_type(const std::string &extension) const
bool manifest::has_override_type(const std::string &part_name) const bool manifest::has_override_type(const std::string &part_name) const
{ {
return std::find_if(override_types_.begin(), override_types_.end(), return std::find_if(override_types_.begin(), override_types_.end(),
[&](const override_type &d) { return d.get_part_name() == part_name; }) != override_types_.end(); [&](const override_type &d) { return match_path(d.get_part_name(), part_name); }) != override_types_.end();
}
void manifest::add_default_type(const std::string &extension, const std::string &content_type)
{
default_types_.push_back(default_type(extension, content_type));
}
void manifest::add_override_type(const std::string &part_name, const std::string &content_type)
{
override_types_.push_back(override_type(part_name, content_type));
} }
std::string manifest::get_default_type(const std::string &extension) const std::string manifest::get_default_type(const std::string &extension) const
@ -30,7 +111,7 @@ std::string manifest::get_default_type(const std::string &extension) const
std::string manifest::get_override_type(const std::string &part_name) const std::string manifest::get_override_type(const std::string &part_name) const
{ {
auto match = std::find_if(override_types_.begin(), override_types_.end(), auto match = std::find_if(override_types_.begin(), override_types_.end(),
[&](const override_type &d) { return d.get_part_name() == part_name; }); [&](const override_type &d) { return match_path(d.get_part_name(), part_name); });
if(match == override_types_.end()) if(match == override_types_.end())
{ {

View File

@ -619,7 +619,7 @@ const number_format &workbook::get_number_format(std::size_t style_id) const
for(const auto &number_format_ : d_->number_formats_) for(const auto &number_format_ : d_->number_formats_)
{ {
if(number_format_.get_id() == number_format_id) if(static_cast<std::size_t>(number_format_.get_id()) == number_format_id)
{ {
return number_format_; return number_format_;
} }
@ -631,15 +631,15 @@ const number_format &workbook::get_number_format(std::size_t style_id) const
return d_->number_formats_.back(); return d_->number_formats_.back();
} }
const font &workbook::get_font(std::size_t style_id) const const font &workbook::get_font(std::size_t font_id) const
{ {
return d_->fonts_[d_->styles_[style_id].font_id_]; return d_->fonts_[font_id];
} }
std::size_t workbook::set_font(const font &font_, std::size_t style_id) std::size_t workbook::set_font(const font &font_, std::size_t style_id)
{ {
auto match = std::find(d_->fonts_.begin(), d_->fonts_.end(), font_); auto match = std::find(d_->fonts_.begin(), d_->fonts_.end(), font_);
auto font_index = 0; std::size_t font_index = 0;
if(match == d_->fonts_.end()) if(match == d_->fonts_.end())
{ {
@ -674,22 +674,22 @@ std::size_t workbook::set_font(const font &font_, std::size_t style_id)
return d_->styles_.size() - 1; return d_->styles_.size() - 1;
} }
const fill &workbook::get_fill(std::size_t style_id) const const fill &workbook::get_fill(std::size_t fill_id) const
{ {
return d_->fills_[d_->styles_[style_id].fill_id_]; return d_->fills_[fill_id];
} }
std::size_t workbook::set_fill(const fill &fill_, std::size_t style_id) std::size_t workbook::set_fill(const fill &/*fill_*/, std::size_t style_id)
{ {
return style_id; return style_id;
} }
const border &workbook::get_border(std::size_t style_id) const const border &workbook::get_border(std::size_t border_id) const
{ {
return d_->borders_[d_->styles_[style_id].border_id_]; return d_->borders_[border_id];
} }
std::size_t workbook::set_border(const border &border_, std::size_t style_id) std::size_t workbook::set_border(const border &/*border_*/, std::size_t style_id)
{ {
return style_id; return style_id;
} }
@ -699,7 +699,7 @@ const alignment &workbook::get_alignment(std::size_t style_id) const
return d_->styles_[style_id].alignment_; return d_->styles_[style_id].alignment_;
} }
std::size_t workbook::set_alignment(const alignment &alignment_, std::size_t style_id) std::size_t workbook::set_alignment(const alignment &/*alignment_*/, std::size_t style_id)
{ {
return style_id; return style_id;
} }
@ -709,7 +709,7 @@ const protection &workbook::get_protection(std::size_t style_id) const
return d_->styles_[style_id].protection_; return d_->styles_[style_id].protection_;
} }
std::size_t workbook::set_protection(const protection &protection_, std::size_t style_id) std::size_t workbook::set_protection(const protection &/*protection_*/, std::size_t style_id)
{ {
return style_id; return style_id;
} }
@ -861,12 +861,27 @@ const std::vector<relationship> &workbook::get_root_relationships() const
{ {
if(d_->root_relationships_.empty()) if(d_->root_relationships_.empty())
{ {
d_->root_relationships_.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml")); d_->root_relationships_.push_back(relationship(relationship::type::core_properties, "rId1", "docProps/core.xml"));
d_->root_relationships_.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml")); d_->root_relationships_.push_back(relationship(relationship::type::extended_properties, "rId2", "docProps/app.xml"));
d_->root_relationships_.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml")); d_->root_relationships_.push_back(relationship(relationship::type::office_document, "rId3", "xl/workbook.xml"));
} }
return d_->root_relationships_; return d_->root_relationships_;
} }
std::vector<std::string> &workbook::get_shared_strings()
{
return d_->shared_strings_;
}
const std::vector<std::string> &workbook::get_shared_strings() const
{
return d_->shared_strings_;
}
void workbook::add_shared_string(const std::string &shared)
{
d_->shared_strings_.push_back(shared);
}
} // namespace xlnt } // namespace xlnt

View File

@ -83,4 +83,10 @@ bool range_reference::operator==(const range_reference &comparand) const
&& comparand.bottom_right_ == bottom_right_; && comparand.bottom_right_ == bottom_right_;
} }
bool range_reference::operator!=(const range_reference &comparand) const
{
return comparand.top_left_ != top_left_
|| comparand.bottom_right_ != bottom_right_;
}
} }

View File

@ -73,6 +73,11 @@ std::vector<range_reference> worksheet::get_merged_ranges() const
return d_->merged_cells_; return d_->merged_cells_;
} }
margins &worksheet::get_page_margins()
{
return d_->page_margins_;
}
const margins &worksheet::get_page_margins() const const margins &worksheet::get_page_margins() const
{ {
return d_->page_margins_; return d_->page_margins_;
@ -103,6 +108,11 @@ void worksheet::unset_auto_filter()
d_->auto_filter_ = range_reference(1, 1, 1, 1); d_->auto_filter_ = range_reference(1, 1, 1, 1);
} }
page_setup &worksheet::get_page_setup()
{
return d_->page_setup_;
}
const page_setup &worksheet::get_page_setup() const const page_setup &worksheet::get_page_setup() const
{ {
return d_->page_setup_; return d_->page_setup_;

View File

@ -1,16 +0,0 @@
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include "detail/include_pugixml.hpp"
namespace {
} // namespace
namespace xlnt {
} // namespace xlnt

View File

@ -31,15 +31,27 @@ public:
operator bool() const { return difference == difference_type::equivalent; } operator bool() const { return difference == difference_type::equivalent; }
}; };
static comparison_result compare_xml(const xlnt::xml_document &expected, const xlnt::xml_document &observed)
{
return compare_xml(expected.get_root(), observed.get_root());
}
static comparison_result compare_xml(const std::string &expected, const xlnt::xml_document &observed) static comparison_result compare_xml(const std::string &expected, const xlnt::xml_document &observed)
{ {
std::ifstream f(expected); std::string expected_contents = expected;
std::ostringstream s;
f >> s.rdbuf();
auto expected_xml = xlnt::xml_serializer::deserialize(s.str()); if(PathHelper::FileExists(expected))
{
std::ifstream f(expected);
std::ostringstream s;
f >> s.rdbuf();
return compare_xml(expected_xml.root(), observed.root()); expected_contents = s.str();
}
auto expected_xml = xlnt::xml_serializer::deserialize(expected_contents);
return compare_xml(expected_xml.get_root(), observed.get_root());
} }
static comparison_result compare_xml(const std::string &left_contents, const std::string &right_contents) static comparison_result compare_xml(const std::string &left_contents, const std::string &right_contents)
@ -47,7 +59,7 @@ public:
auto left_doc = xlnt::xml_serializer::deserialize(left_contents); auto left_doc = xlnt::xml_serializer::deserialize(left_contents);
auto right_doc = xlnt::xml_serializer::deserialize(right_contents); auto right_doc = xlnt::xml_serializer::deserialize(right_contents);
return compare_xml(left_doc.root(), right_doc.root()); return compare_xml(left_doc.get_root(), right_doc.get_root());
} }
static comparison_result compare_xml(const xlnt::xml_node &left, const xlnt::xml_node &right) static comparison_result compare_xml(const xlnt::xml_node &left, const xlnt::xml_node &right)
@ -100,13 +112,14 @@ public:
return {difference_type::text_values_differ, "((empty))", right_temp}; return {difference_type::text_values_differ, "((empty))", right_temp};
} }
auto right_child_iter = right.get_children().begin(); auto right_children = right.get_children();
auto right_child_iter = right_children.begin();
for(auto left_child : left.get_children()) for(auto left_child : left.get_children())
{ {
left_temp = left_child.get_name(); left_temp = left_child.get_name();
if(right_child_iter == right.get_children().end()) if(right_child_iter == right_children.end())
{ {
return {difference_type::child_order_differs, left_temp, "((end))"}; return {difference_type::child_order_differs, left_temp, "((end))"};
} }
@ -122,7 +135,7 @@ public:
} }
} }
if(right_child_iter != right.get_children().end()) if(right_child_iter != right_children.end())
{ {
right_temp = right_child_iter->get_name(); right_temp = right_child_iter->get_name();
return {difference_type::child_order_differs, "((end))", right_temp}; return {difference_type::child_order_differs, "((end))", right_temp};

View File

@ -83,6 +83,8 @@ public:
prop.last_modified_by = "SOMEBODY"; prop.last_modified_by = "SOMEBODY";
prop.created = xlnt::datetime(2010, 4, 1, 20, 30, 00); prop.created = xlnt::datetime(2010, 4, 1, 20, 30, 00);
prop.modified = xlnt::datetime(2010, 4, 5, 14, 5, 30); prop.modified = xlnt::datetime(2010, 4, 5, 14, 5, 30);
TS_FAIL("");
return;
xlnt::workbook_serializer serializer(wb); xlnt::workbook_serializer serializer(wb);
xlnt::xml_document xml = serializer.write_properties_core(); xlnt::xml_document xml = serializer.write_properties_core();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/core.xml", xml)); TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/core.xml", xml));
@ -95,6 +97,8 @@ public:
wb.create_sheet(); wb.create_sheet();
xlnt::workbook_serializer serializer(wb); xlnt::workbook_serializer serializer(wb);
xlnt::xml_document xml = serializer.write_properties_app(); xlnt::xml_document xml = serializer.write_properties_app();
TS_FAIL("");
return;
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/app.xml", xml)); TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/app.xml", xml));
} }
}; };

View File

@ -7,6 +7,7 @@
#include <xlnt/s11n/excel_serializer.hpp> #include <xlnt/s11n/excel_serializer.hpp>
#include <xlnt/s11n/manifest_serializer.hpp> #include <xlnt/s11n/manifest_serializer.hpp>
#include <xlnt/s11n/relationship_serializer.hpp>
#include <xlnt/s11n/workbook_serializer.hpp> #include <xlnt/s11n/workbook_serializer.hpp>
#include <xlnt/s11n/xml_serializer.hpp> #include <xlnt/s11n/xml_serializer.hpp>
#include <xlnt/workbook/manifest.hpp> #include <xlnt/workbook/manifest.hpp>
@ -331,11 +332,15 @@ public:
{ {
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx"); auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file archive(path); xlnt::zip_file archive(path);
std::vector<std::pair<std::string, std::string>> expected = std::vector<std::pair<std::string, std::string>> expected =
{ {
{"xl/worksheets/sheet1.xml", "Sheet1"} {"xl/worksheets/sheet1.xml", "Sheet1"}
}; };
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected);
xlnt::workbook wb;
xlnt::workbook_serializer serializer(wb);
TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected);
} }
{ {
@ -348,7 +353,9 @@ public:
{"xl/worksheets/sheet2.xml", "moredata"} {"xl/worksheets/sheet2.xml", "moredata"}
}; };
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected); xlnt::workbook wb;
xlnt::workbook_serializer serializer(wb);
TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected);
} }
{ {
@ -362,7 +369,9 @@ public:
{"xl/worksheets/sheet.xml", "Sheet3"} {"xl/worksheets/sheet.xml", "Sheet3"}
}; };
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected); xlnt::workbook wb;
xlnt::workbook_serializer serializer(wb);
TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected);
} }
} }
@ -381,7 +390,7 @@ public:
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx"); auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file archive(path); xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected); TS_ASSERT_EQUALS(xlnt::relationship_serializer::read_relationships(archive, "xl/workbook.xml"), expected);
} }
{ {
@ -400,7 +409,7 @@ public:
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx"); auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file archive(path); xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected); TS_ASSERT_EQUALS(xlnt::relationship_serializer::read_relationships(archive, "xl/workbook.xml"), expected);
} }
} }

View File

@ -13,14 +13,15 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
wb.add_number_format(xlnt::number_format("YYYY")); wb.add_number_format(xlnt::number_format("YYYY"));
xlnt::style_serializer writer(wb); xlnt::style_serializer writer(wb);
auto xml = writer.write_number_formats(); xlnt::xml_document observed;
writer.write_number_formats(observed.add_child("numFmts"));
std::string expected = std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">" "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"
" <numFmts count=\"1\">" " <numFmts count=\"1\">"
" <numFmt formatCode=\"YYYY\" numFmtId=\"164\"></numFmt>" " <numFmt formatCode=\"YYYY\" numFmtId=\"164\"></numFmt>"
" </numFmts>" " </numFmts>"
"</styleSheet>"; "</styleSheet>";
auto diff = Helper::compare_xml(xml, expected); auto diff = Helper::compare_xml(expected, observed);
TS_ASSERT(diff); TS_ASSERT(diff);
} }
/* /*

View File

@ -14,9 +14,9 @@ class test_theme : public CxxTest::TestSuite
public: public:
void test_write_theme() void test_write_theme()
{ {
xlnt::theme_serializer serializer;
xlnt::workbook wb; xlnt::workbook wb;
auto content = serializer.write_theme(wb.get_loaded_theme()); xlnt::theme_serializer serializer;
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/theme1.xml", content)); auto xml = serializer.write_theme(wb.get_loaded_theme());
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/theme1.xml", xml));
} }
}; };

View File

@ -3,6 +3,7 @@
#include <iostream> #include <iostream>
#include <cxxtest/TestSuite.h> #include <cxxtest/TestSuite.h>
#include <xlnt/s11n/worksheet_serializer.hpp>
#include <xlnt/worksheet/worksheet.hpp> #include <xlnt/worksheet/worksheet.hpp>
class test_worksheet : public CxxTest::TestSuite class test_worksheet : public CxxTest::TestSuite
@ -639,10 +640,8 @@ public:
{ {
xlnt::worksheet ws(wb_); xlnt::worksheet ws(wb_);
auto xml_string = xlnt::write_worksheet(ws, {}); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto expected_string = auto expected_string =
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
@ -660,10 +659,10 @@ public:
" <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>" " <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>"
"</worksheet>"; "</worksheet>";
pugi::xml_document expected_doc; xlnt::xml_document expected;
expected_doc.load(expected_string); expected.from_string(expected_string);
TS_ASSERT(Helper::compare_xml(expected_doc, doc)); TS_ASSERT(Helper::compare_xml(expected, observed));
} }
void test_page_margins() void test_page_margins()
@ -677,10 +676,8 @@ public:
ws.get_page_margins().set_header(1.5); ws.get_page_margins().set_header(1.5);
ws.get_page_margins().set_footer(1.5); ws.get_page_margins().set_footer(1.5);
auto xml_string = xlnt::write_worksheet(ws, {}); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto expected_string = auto expected_string =
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
@ -698,10 +695,10 @@ public:
" <pageMargins left=\"2\" right=\"2\" top=\"2\" bottom=\"2\" header=\"1.5\" footer=\"1.5\"/>" " <pageMargins left=\"2\" right=\"2\" top=\"2\" bottom=\"2\" header=\"1.5\" footer=\"1.5\"/>"
"</worksheet>"; "</worksheet>";
pugi::xml_document expected_doc; xlnt::xml_document expected;
expected_doc.load(expected_string); expected.from_string(expected_string);
TS_ASSERT(Helper::compare_xml(expected_doc, doc)); TS_ASSERT(Helper::compare_xml(expected, observed));
} }
void test_merge() void test_merge()
@ -737,18 +734,17 @@ public:
ws.get_cell("A1").set_value("Cell A1"); ws.get_cell("A1").set_value("Cell A1");
ws.get_cell("B1").set_value("Cell B1"); ws.get_cell("B1").set_value("Cell B1");
auto xml_string = xlnt::write_worksheet(ws, string_table); {
xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
pugi::xml_document doc; xlnt::xml_document expected;
doc.load(xml_string.c_str()); expected.from_string(expected_string1);
pugi::xml_document expected_doc; TS_ASSERT(Helper::compare_xml(expected, observed));
expected_doc.load(expected_string1); }
TS_ASSERT(Helper::compare_xml(expected_doc, doc));
ws.merge_cells("A1:B1"); ws.merge_cells("A1:B1");
xml_string = xlnt::write_worksheet(ws, string_table);
auto expected_string2 = auto expected_string2 =
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
@ -776,13 +772,17 @@ public:
" <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>" " <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>"
"</worksheet>"; "</worksheet>";
doc.load(xml_string.c_str()); {
expected_doc.load(expected_string2); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(expected_doc, doc)); xlnt::xml_document expected;
expected.from_string(expected_string2);
TS_ASSERT(Helper::compare_xml(expected, observed));
}
ws.unmerge_cells("A1:B1"); ws.unmerge_cells("A1:B1");
xml_string = xlnt::write_worksheet(ws, string_table);
auto expected_string3 = auto expected_string3 =
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
@ -807,10 +807,15 @@ public:
" <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>" " <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>"
"</worksheet>"; "</worksheet>";
doc.load(xml_string.c_str()); {
expected_doc.load(expected_string3); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(expected_doc, doc)); xlnt::xml_document expected;
expected.from_string(expected_string3);
TS_ASSERT(Helper::compare_xml(expected, observed));
}
} }
void test_printer_settings() void test_printer_settings()
@ -825,10 +830,8 @@ public:
ws.get_page_setup().set_horizontal_centered(true); ws.get_page_setup().set_horizontal_centered(true);
ws.get_page_setup().set_vertical_centered(true); ws.get_page_setup().set_vertical_centered(true);
auto xml_string = xlnt::write_worksheet(ws, {}); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto expected_string = auto expected_string =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
@ -850,10 +853,10 @@ public:
" <pageSetup orientation=\"landscape\" paperSize=\"3\" fitToHeight=\"0\" fitToWidth=\"1\" />" " <pageSetup orientation=\"landscape\" paperSize=\"3\" fitToHeight=\"0\" fitToWidth=\"1\" />"
"</worksheet>"; "</worksheet>";
pugi::xml_document expected_doc; xlnt::xml_document expected;
expected_doc.load(expected_string); expected.from_string(expected_string);
TS_ASSERT(Helper::compare_xml(expected_doc, doc)); TS_ASSERT(Helper::compare_xml(expected, observed));
} }
void test_header_footer() void test_header_footer()
@ -901,13 +904,15 @@ public:
" </headerFooter>" " </headerFooter>"
"</worksheet>"; "</worksheet>";
pugi::xml_document expected_doc; xlnt::xml_document expected;
pugi::xml_document observed_doc; expected.from_string(expected_xml_string);
expected_doc.load(expected_xml_string.c_str()); {
observed_doc.load(xlnt::write_worksheet(ws, {}).c_str()); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(expected_doc, observed_doc)); TS_ASSERT(Helper::compare_xml(expected, observed));
}
ws = wb_.create_sheet(); ws = wb_.create_sheet();
@ -927,10 +932,12 @@ public:
" <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>" " <pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>"
"</worksheet>"; "</worksheet>";
expected_doc.load(expected_xml_string.c_str()); {
observed_doc.load(xlnt::write_worksheet(ws, {}).c_str()); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(expected_doc, observed_doc)); TS_ASSERT(Helper::compare_xml(expected, observed));
}
} }
void test_page_setup() void test_page_setup()

View File

@ -3,7 +3,12 @@
#include <iostream> #include <iostream>
#include <cxxtest/TestSuite.h> #include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp> #include <xlnt/s11n/relationship_serializer.hpp>
#include <xlnt/s11n/shared_strings_serializer.hpp>
#include <xlnt/s11n/workbook_serializer.hpp>
#include <xlnt/s11n/worksheet_serializer.hpp>
#include <xlnt/workbook/workbook.hpp>
#include "helpers/temporary_file.hpp" #include "helpers/temporary_file.hpp"
#include "helpers/path_helper.hpp" #include "helpers/path_helper.hpp"
#include "helpers/helper.hpp" #include "helpers/helper.hpp"
@ -41,32 +46,45 @@ public:
void test_write_workbook_rels() void test_write_workbook_rels()
{ {
xlnt::workbook wb; xlnt::workbook wb;
auto content = xlnt::write_workbook_rels(wb); xlnt::zip_file archive;
xlnt::relationship_serializer::write_relationships(wb.get_relationships(), "xl/workbook.xml", archive);
auto content = xlnt::xml_serializer::deserialize(archive.read("xl/_rels/workbook.xml.rels"));
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml.rels", content)); TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml.rels", content));
} }
void test_write_workbook() void test_write_workbook()
{ {
xlnt::workbook wb; xlnt::workbook wb;
auto content = xlnt::write_workbook(wb); xlnt::workbook_serializer serializer(wb);
auto content = serializer.write_workbook();
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml", content)); TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml", content));
} }
void test_write_string_table() void test_write_string_table()
{ {
std::vector<std::string> table = {"hello", "world", "nice"}; xlnt::workbook wb;
auto content = xlnt::write_shared_strings(table); auto ws = wb.get_active_sheet();
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sharedStrings.xml", content));
ws.get_cell("A1").set_value("hello");
ws.get_cell("A2").set_value("world");
ws.get_cell("A3").set_value("nice");
auto content = xlnt::shared_strings_serializer::write_shared_strings(wb.get_shared_strings());
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sharedStrings.xml", content));
} }
void test_write_worksheet() void test_write_worksheet()
{ {
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", observed));
} }
void test_write_hidden_worksheet() void test_write_hidden_worksheet()
@ -74,8 +92,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_page_setup().set_sheet_state(xlnt::page_setup::sheet_state::hidden); ws.get_page_setup().set_sheet_state(xlnt::page_setup::sheet_state::hidden);
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", observed));
} }
void test_write_bool() void test_write_bool()
@ -83,8 +104,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F42").set_value(false); ws.get_cell("F42").set_value(false);
ws.get_cell("F43").set_value(true); ws.get_cell("F43").set_value(true);
auto content = xlnt::write_worksheet(ws, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_bool.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_bool.xml", observed));
} }
void test_write_formula() void test_write_formula()
@ -93,8 +117,11 @@ public:
ws.get_cell("F1").set_value(10); ws.get_cell("F1").set_value(10);
ws.get_cell("F2").set_value(32); ws.get_cell("F2").set_value(32);
ws.get_cell("F3").set_formula("F1+F2"); ws.get_cell("F3").set_formula("F1+F2");
auto content = xlnt::write_worksheet(ws, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_formula.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_formula.xml", observed));
} }
void test_write_height() void test_write_height()
@ -102,8 +129,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F1").set_value(10); ws.get_cell("F1").set_value(10);
ws.get_row_properties(ws.get_cell("F1").get_row()).height = 30; ws.get_row_properties(ws.get_cell("F1").get_row()).height = 30;
auto content = xlnt::write_worksheet(ws, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_height.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_height.xml", observed));
} }
void test_write_hyperlink() void test_write_hyperlink()
@ -111,8 +141,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("A1").set_value("test"); ws.get_cell("A1").set_value("test");
ws.get_cell("A1").set_hyperlink("http://test.com"); ws.get_cell("A1").set_hyperlink("http://test.com");
auto content = xlnt::write_worksheet(ws, {"test"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml", observed));
} }
void test_write_hyperlink_rels() void test_write_hyperlink_rels()
@ -125,8 +158,12 @@ public:
ws.get_cell("A2").set_value("test"); ws.get_cell("A2").set_value("test");
ws.get_cell("A2").set_hyperlink("http://test2.com/"); ws.get_cell("A2").set_hyperlink("http://test2.com/");
TS_ASSERT_EQUALS(2, ws.get_relationships().size()); TS_ASSERT_EQUALS(2, ws.get_relationships().size());
auto content = xlnt::write_worksheet_rels(ws);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml.rels", content)); xlnt::zip_file archive;
xlnt::relationship_serializer::write_relationships(ws.get_relationships(), "xl/worksheets/sheet1.xml", archive);
auto content = xlnt::xml_serializer::deserialize(archive.read("xl/worksheets/_rels/sheet1.xml.rels"));
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml.rels", content));
} }
void _test_write_hyperlink_image_rels() void _test_write_hyperlink_image_rels()
@ -149,11 +186,16 @@ public:
auto ws = wb.get_sheet_by_index(0); auto ws = wb.get_sheet_by_index(0);
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
ws.auto_filter("A1:F1"); ws.auto_filter("A1:F1");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_auto_filter.xml", content));
content = xlnt::write_workbook(wb); xlnt::worksheet_serializer ws_serializer(ws);
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook_auto_filter.xml", content)); auto observed = ws_serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_auto_filter.xml", observed));
xlnt::workbook_serializer wb_serializer(wb);
auto observed2 = wb_serializer.write_workbook();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook_auto_filter.xml", observed2));
} }
void test_write_auto_filter_filter_column() void test_write_auto_filter_filter_column()
@ -171,8 +213,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
ws.freeze_panes("A4"); ws.freeze_panes("A4");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_horiz.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_horiz.xml", observed));
} }
void test_freeze_panes_vert() void test_freeze_panes_vert()
@ -180,8 +225,11 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
ws.freeze_panes("D1"); ws.freeze_panes("D1");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_vert.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_vert.xml", observed));
} }
void test_freeze_panes_both() void test_freeze_panes_both()
@ -189,24 +237,33 @@ public:
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
ws.freeze_panes("D4"); ws.freeze_panes("D4");
auto content = xlnt::write_worksheet(ws, {"hello"});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_both.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_both.xml", observed));
} }
void test_long_number() void test_long_number()
{ {
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("A1").set_value(9781231231230LL); ws.get_cell("A1").set_value(9781231231230LL);
auto content = xlnt::write_worksheet(ws, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/long_number.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/long_number.xml", observed));
} }
void test_short_number() void test_short_number()
{ {
auto ws = wb_.create_sheet(); auto ws = wb_.create_sheet();
ws.get_cell("A1").set_value(1234567890); ws.get_cell("A1").set_value(1234567890);
auto content = xlnt::write_worksheet(ws, {});
TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/short_number.xml", content)); xlnt::worksheet_serializer serializer(ws);
auto observed = serializer.write_worksheet();
TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/short_number.xml", observed));
} }
void _test_write_images() void _test_write_images()

View File

@ -4,8 +4,7 @@
#include <cxxtest/TestSuite.h> #include <cxxtest/TestSuite.h>
#include <xlnt/common/exceptions.hpp> #include <xlnt/common/exceptions.hpp>
#include <xlnt/reader/workbook_reader.hpp> #include <xlnt/s11n/workbook_serializer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "helpers/path_helper.hpp" #include "helpers/path_helper.hpp"
@ -19,8 +18,10 @@ public:
ws.get_cell("F42").set_value("hello"); ws.get_cell("F42").set_value("hello");
ws.get_auto_filter() = "A1:F1"; ws.get_auto_filter() = "A1:F1";
auto content = xlnt::write_workbook(wb); xlnt::workbook_serializer serializer(wb);
auto diff = Helper::compare_xml(PathHelper::read_file("workbook_auto_filter.xml"), content); auto observed = serializer.write_workbook();
auto diff = Helper::compare_xml(PathHelper::read_file("workbook_auto_filter.xml"), observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
@ -30,8 +31,11 @@ public:
auto ws = wb.create_sheet(); auto ws = wb.create_sheet();
ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden); ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden);
wb.create_sheet(); wb.create_sheet();
auto xml = xlnt::write_workbook(wb);
std::string expected = xlnt::workbook_serializer serializer(wb);
auto observed = serializer.write_workbook();
std::string expected_string =
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
" <workbookPr/>" " <workbookPr/>"
" <bookViews>" " <bookViews>"
@ -44,7 +48,11 @@ public:
" <definedNames/>" " <definedNames/>"
" <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>" " <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>"
"</workbook>"; "</workbook>";
auto diff = Helper::compare_xml(xml, expected);
xlnt::xml_document expected;
expected.from_string(expected_string);
auto diff = Helper::compare_xml(expected, observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
@ -53,40 +61,56 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
auto ws = wb.get_active_sheet(); auto ws = wb.get_active_sheet();
ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden); ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden);
TS_ASSERT_THROWS(xlnt::write_workbook(wb), xlnt::value_error);
xlnt::workbook_serializer serializer(wb);
TS_ASSERT_THROWS(serializer.write_workbook(), xlnt::value_error);
} }
void test_write_empty_workbook() void test_write_empty_workbook()
{ {
xlnt::workbook wb; xlnt::workbook wb;
TemporaryFile file; TemporaryFile file;
xlnt::save_workbook(wb, file.GetFilename());
xlnt::excel_serializer serializer(wb);
serializer.save_workbook(file.GetFilename());
TS_ASSERT(PathHelper::FileExists(file.GetFilename())); TS_ASSERT(PathHelper::FileExists(file.GetFilename()));
} }
void test_write_virtual_workbook() void test_write_virtual_workbook()
{ {
xlnt::workbook old_wb; xlnt::workbook old_wb, new_wb;
auto saved_wb = xlnt::save_virtual_workbook(old_wb);
auto new_wb = xlnt::excel_reader::load_workbook(saved_wb); xlnt::excel_serializer serializer(old_wb);
std::vector<std::uint8_t> wb_bytes;
serializer.save_virtual_workbook(wb_bytes);
xlnt::excel_serializer deserializer(new_wb);
deserializer.load_virtual_workbook(wb_bytes);
TS_ASSERT(new_wb != nullptr); TS_ASSERT(new_wb != nullptr);
} }
void test_write_workbook_rels() void test_write_workbook_rels()
{ {
xlnt::workbook wb; xlnt::workbook wb;
auto content = xlnt::write_workbook_rels(wb); xlnt::zip_file archive;
xlnt::relationship_serializer::write_relationships(wb.get_relationships(), "xl/workbook.xml", archive);
xlnt::xml_document observed;
observed.from_string(archive.read("xl/workbook/_rels/workbook.xml.rels"));
auto filename = "workbook.xml.rels"; auto filename = "workbook.xml.rels";
auto diff = Helper::compare_xml(PathHelper::read_file(filename), content); auto diff = Helper::compare_xml(PathHelper::read_file(filename), observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
void test_write_workbook_() void test_write_workbook_()
{ {
xlnt::workbook wb; xlnt::workbook wb;
auto content = xlnt::write_workbook(wb); xlnt::workbook_serializer serializer(wb);
auto observed = serializer.write_workbook();
auto filename = PathHelper::GetDataDirectory("/workbook.xml"); auto filename = PathHelper::GetDataDirectory("/workbook.xml");
auto diff = Helper::compare_xml(PathHelper::read_file(filename), content); auto diff = Helper::compare_xml(PathHelper::read_file(filename), observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
@ -95,12 +119,15 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
auto ws = wb.create_sheet(); auto ws = wb.create_sheet();
wb.create_named_range("test_range", ws, "A1:B5"); wb.create_named_range("test_range", ws, "A1:B5");
auto xml = xlnt::write_defined_names(wb); xlnt::workbook_serializer serializer(wb);
auto observed_node = serializer.write_named_ranges();
xlnt::xml_document observed;
observed.add_child(observed_node);
std::string expected = std::string expected =
"<root>" "<root>"
"<s:definedName xmlns:s=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" name=\"test_range\">'Sheet'!$A$1:$B$5</s:definedName>" "<s:definedName xmlns:s=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" name=\"test_range\">'Sheet'!$A$1:$B$5</s:definedName>"
"</root>"; "</root>";
auto diff = Helper::compare_xml(xml, expected); auto diff = Helper::compare_xml(expected, observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
@ -115,7 +142,9 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
wb.set_code_name("MyWB"); wb.set_code_name("MyWB");
auto content = xlnt::write_workbook(wb); xlnt::workbook_serializer serializer(wb);
auto observed = serializer.write_workbook();
std::string expected = std::string expected =
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" "<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
" <workbookPr codeName=\"MyWB\"/>" " <workbookPr codeName=\"MyWB\"/>"
@ -128,21 +157,26 @@ public:
" <definedNames/>" " <definedNames/>"
" <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>" " <calcPr calcId=\"124519\" fullCalcOnLoad=\"1\"/>"
"</workbook>"; "</workbook>";
auto diff = Helper::compare_xml(content, expected); auto diff = Helper::compare_xml(expected, observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }
void test_write_root_rels() void test_write_root_rels()
{ {
xlnt::workbook wb; xlnt::workbook wb;
auto xml = xlnt::write_root_rels(wb); xlnt::zip_file archive;
xlnt::relationship_serializer::write_relationships(wb.get_root_relationships(), "", archive);
xlnt::xml_document observed;
observed.from_string(archive.read("_rels/.rels"));
std::string expected = std::string expected =
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
" <Relationship Id=\"rId1\" Target=\"xl/workbook.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"/>" " <Relationship Id=\"rId1\" Target=\"xl/workbook.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"/>"
" <Relationship Id=\"rId2\" Target=\"docProps/core.xml\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\"/>" " <Relationship Id=\"rId2\" Target=\"docProps/core.xml\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\"/>"
" <Relationship Id=\"rId3\" Target=\"docProps/app.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"/>" " <Relationship Id=\"rId3\" Target=\"docProps/app.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"/>"
"</Relationships>"; "</Relationships>";
auto diff = Helper::compare_xml(xml, expected);
auto diff = Helper::compare_xml(expected, observed);
TS_ASSERT(!diff); TS_ASSERT(!diff);
} }