start refactoring serialization code

This commit is contained in:
Thomas Fussell 2015-10-29 13:46:56 -04:00
parent 3176ee828c
commit c884ad7f82
57 changed files with 2639 additions and 2305 deletions

View File

@ -27,15 +27,20 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "../styles/style.hpp" #include <xlnt/common/types.hpp>
#include "../common/types.hpp"
namespace xlnt { namespace xlnt {
enum class calendar; enum class calendar;
class alignment;
class border;
class cell_reference; class cell_reference;
class comment; class comment;
class fill;
class font;
class number_format;
class protection;
class relationship; class relationship;
class worksheet; class worksheet;
@ -62,6 +67,9 @@ struct cell_impl;
class cell class cell
{ {
public: public:
/// <summary>
/// Enumerates the possible types a cell can be determined by it's current value.
/// </summary>
enum class type enum class type
{ {
null, null,
@ -72,14 +80,38 @@ public:
boolean boolean
}; };
static const std::unordered_map<std::string, int> ErrorCodes; /// <summary>
/// Return a map of error strings such as #DIV/0! and their associated indices.
/// </summary>
static const std::unordered_map<std::string, int> error_codes();
//TODO: Should it be possible to construct and use a cell without a parent worksheet?
//(cont'd) If so, it would need to be responsible for allocating and deleting its PIMPL.
/// <summary>
/// Construct a null cell without a parent.
/// Most methods will throw an exception if this cell is not further initialized.
/// </summary>
cell(); cell();
cell(worksheet worksheet, const cell_reference &reference);
/// <summary>
/// Construct a cell in worksheet, sheet, at the given reference location (e.g. A1).
/// </summary>
cell(worksheet sheet, const cell_reference &reference);
/// <summary>
/// This constructor, provided for convenience, is equivalent to calling:
/// cell c(sheet, reference);
/// c.set_value(initial_value);
/// </summary>
template<typename T> template<typename T>
cell(worksheet worksheet, const cell_reference &reference, const T &initial_value); cell(worksheet sheet, const cell_reference &reference, const T &initial_value);
// value // value
/// <summary>
/// Return true if value has been set and has not been cleared using cell::clear_value().
/// </summary>
bool has_value() const; bool has_value() const;
template<typename T> template<typename T>
@ -93,10 +125,18 @@ public:
type get_data_type() const; type get_data_type() const;
void set_data_type(type t); void set_data_type(type t);
// characteristics // properties
/// <summary>
/// There's no reason to keep a cell which has no value and is not a placeholder.
/// Return true if this cell has no value, style, isn't merged, etc.
/// </summary>
bool garbage_collectible() const; bool garbage_collectible() const;
/// <summary>
/// Return true iff this cell's number format matches a date format.
/// </summary>
bool is_date() const; bool is_date() const;
std::size_t get_xf_index() const;
// position // position
cell_reference get_reference() const; cell_reference get_reference() const;
@ -176,6 +216,7 @@ public:
bool operator==(const cell &comparand) const; bool operator==(const cell &comparand) const;
bool operator==(std::nullptr_t) const; bool operator==(std::nullptr_t) const;
// friend operators, so we can put cell on either side of comparisons with other types
friend bool operator==(std::nullptr_t, const cell &cell); friend bool operator==(std::nullptr_t, const cell &cell);
friend bool operator<(cell left, cell right); friend bool operator<(cell left, cell right);
@ -188,6 +229,10 @@ private:
detail::cell_impl *d_; detail::cell_impl *d_;
}; };
/// <summary>
/// Convenience function for writing cell to an ostream.
/// Uses cell::to_string() internally.
/// </summary>
inline std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell) inline std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)
{ {
return stream << cell.to_string(); return stream << cell.to_string();

View File

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

View File

@ -1,88 +0,0 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include "xlnt/workbook/workbook.hpp"
namespace pugi {
class xml_node;
} // namespace pugi
namespace xlnt {
class border;
class fill;
class font;
class named_style;
class number_format;
class style;
class zip_file;
class style_reader
{
public:
style_reader(workbook &wb);
void read_styles(zip_file &archive);
const std::vector<style> &get_styles() const { return styles_; }
const std::vector<border> &get_borders() const { return borders_; }
const std::vector<fill> &get_fills() const { return fills_; }
const std::vector<font> &get_fonts() const { return fonts_; }
const std::vector<number_format> &get_number_formats() const { return number_formats_; }
const std::vector<std::string> &get_colors() const { return colors_; }
private:
style read_style(pugi::xml_node stylesheet_node, pugi::xml_node xf_node);
void read_borders(pugi::xml_node borders_node);
void read_fills(pugi::xml_node fills_node);
void read_fonts(pugi::xml_node fonts_node);
void read_number_formats(pugi::xml_node num_fmt_node);
void read_colors(pugi::xml_node colors_node);
void read_dxfs();
void read_named_styles(pugi::xml_node named_styles_node);
void read_style_names();
void read_cell_styles();
void read_xfs();
void read_style_table();
workbook wb_;
std::vector<style> styles_;
std::vector<std::string> colors_;
std::vector<border> borders_;
std::vector<fill> fills_;
std::vector<font> fonts_;
std::vector<number_format> number_formats_;
};
} // namespace xlnt

View File

@ -23,43 +23,28 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <string>
#include <unordered_map>
#include <vector> #include <vector>
#include "charts/chart.h" #include <xlnt/cell/comment.hpp>
#include <xlnt/worksheet/worksheet.hpp>
namespace xlnt { namespace xlnt {
class chart_writer class xml_node;
class comment_serializer
{ {
comment_serializer(worksheet sheet);
}; void read_comments(const xml_node &xml);
void read_comments_vml(const xml_node &xml);
class pie_chart_writer : public chart_writer xml_node write_comments();
{ xml_node write_comments_vml();
}; private:
worksheet sheet_;
class pie_chart_writer : public chart_writer std::vector<comment> comments_;
{
};
class pie_chart_writer : public chart_writer
{
};
class pie_chart_writer : public chart_writer
{
};
class chart_writer_factory
{
public:
static std::unique_ptr<chart_writer> create_writer(chart c);
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -23,23 +23,53 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <iostream> #include <cstdint>
#include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <xlnt/common/zip_file.hpp>
namespace xlnt { namespace xlnt {
class workbook; class workbook;
class excel_reader class excel_serializer
{ {
public: public:
static std::string CentralDirectorySignature(); static std::string central_directory_signature();
static std::string repair_central_directory(const std::string &original); static std::string repair_central_directory(const std::string &original);
static workbook load_workbook(std::istream &stream, bool guess_types = false, bool data_only = false); excel_serializer(workbook &wb);
static workbook load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
static workbook load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types = false, bool data_only = false); bool load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
bool load_stream_workbook(std::istream &stream, 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);
bool save_workbook(workbook &wb, const std::string &filename, bool as_template = false);
bool save_virtual_workbook(xlnt::workbook &wb, std::vector<std::uint8_t> &bytes, bool as_template = false);
bool save_stream_workbook(xlnt::workbook &wb, std::ostream &stream, bool as_template = false);
private:
void read_data(bool guess_types, bool data_only);
void read_shared_strings();
void read_images();
void read_charts();
void read_chartsheets();
void read_worksheets();
void read_external_links();
void write_data(bool as_template);
void write_shared_strings();
void write_images();
void write_charts();
void write_chartsheets();
void write_worksheets();
void write_external_links();
workbook &wb_;
std::vector<std::string> shared_strings_;
zip_file archive_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace xlnt {
class manifest;
class workbook;
class xml_document;
class zip_file;
class manifest_serializer
{
public:
manifest_serializer(manifest &m);
bool read_mainfest(const xml_document &xml);
bool write_manifest(xml_document &xml);
private:
manifest &manifest_;
};
}; // namespace xlnt

View File

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

View File

@ -28,15 +28,13 @@
namespace xlnt { namespace xlnt {
class zip_file; class xml_document;
class shared_strings_reader class shared_strings_serializer
{ {
public: public:
std::vector<std::string> read_strings(zip_file &archive); bool read_strings(const xml_document &xml, std::vector<std::string> &strings);
void read_string_table(const std::string &xml_source); bool write_strings(const std::vector<std::string> &strings, xml_document &xml);
std::string get_string();
std::string get_text();
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -0,0 +1,255 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <xlnt/workbook/workbook.hpp>
namespace xlnt {
class alignment;
class border;
class color;
class conditional_format;
class fill;
class font;
class named_style;
class number_format;
class protection;
class side;
class style;
class xml_document;
class xml_node;
/// <summary>
/// Reads and writes xl/styles.xml from/to an associated workbook.
/// </summary>
class style_serializer
{
public:
/// <summary>
/// Construct a style_serializer which can write styles.xml based on wb or populate wb
/// with styles from an existing styles.xml.
style_serializer(workbook &wb);
/// <summary>
/// Load all styles from xml_document into workbook given in constructor.
/// </summary>
bool read_stylesheet(const xml_document &xml);
/// <summary>
/// Populate parameter xml with an XML tree representing the styles contained in the workbook
/// given in the constructor.
/// </summary>
bool write_stylesheet(xml_document &xml) const;
private:
/// <summary>
/// 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>
style read_style(const xml_node &style_node) const;
/// <summary>
/// Read and return an alignment from alignment_node.
/// </summary>
alignment read_alignment(const xml_node &alignment_node) const;
/// <summary>
/// Read and return a border side from side_node.
/// </summary>
side read_side(const xml_node &side_node) const;
/// <summary>
/// Read all borders from borders_node and add them to workbook.
/// Return true on success.
/// </summary>
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>
/// Read all fills from fills_node and add them to workbook.
/// Return true on success.
/// </summary>
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>
/// Read all fonts from fonts_node and add them to workbook.
/// Return true on success.
/// </summary>
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>
/// Read all colors from colors_node and add them to workbook.
/// Return true on success.
/// </summary>
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>
/// Read all named styles from named_style_node and cell_styles_node and add them to workbook.
/// Return true on success.
/// </summary>
bool read_named_styles(const xml_node &named_styles_node);
/// <summary>
/// Read and return a pair containing a name and corresponding style from named_style_node.
/// </summary>
std::pair<std::string, style> read_named_style(const xml_node &named_style_node) const;
/// <summary>
/// Build and return an xml tree representing alignment_.
/// </summary>
xml_node write_alignment(const alignment &alignment_) const;
/// <summary>
/// Build and return an xml tree representing border_.
/// </summary>
xml_node write_border(const border &border_) const;
/// <summary>
/// Build an xml tree representing all borders in workbook into borders_node.
/// Returns true on success.
/// </summary>
bool write_borders(xml_node &borders_node) const;
/// <summary>
/// Build and return an xml tree representing fill_.
/// </summary>
xml_node write_fill(const fill &fill_) const;
/// <summary>
/// Build an xml tree representing all fills in workbook into fills_node.
/// Returns true on success.
/// </summary>
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>
/// Build an xml tree representing all fonts in workbook into fonts_node.
/// Returns true on success.
/// </summary>
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>
/// Build an xml tree representing all number formats in workbook into number_formats_node.
/// Returns true on success.
/// </summary>
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>
/// Build an xml tree representing the given style into style_node.
/// Returns true on success.
/// </summary>
bool write_style(const style &style_, xml_node &style_node) const;
/// <summary>
/// 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>
/// Set in the constructor, this workbook is used as the source or target for all writing or reading, respectively.
/// </summary>
workbook &wb_;
};
} // namespace xlnt

View File

@ -27,7 +27,11 @@
namespace xlnt { namespace xlnt {
std::string write_theme(); class theme_serializer
//void write_theme(const theme &t); {
public:
theme read_theme(const xml_document &xml);
xml_document write_theme(const theme &theme_);
};
} // namespace xlnt } // namespace xlnt

View File

@ -23,23 +23,44 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
namespace xlnt { namespace xlnt {
class document_properties; class document_properties;
class manifest;
class relationship; class relationship;
class worksheet;
class workbook; class workbook;
class zip_file; class zip_file;
class xml_document;
std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive); class workbook_serializer
std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types); {
document_properties read_properties_core(const std::string &xml_string); public:
workbook_serializer(workbook &wb);
std::vector<std::pair<std::string,std::string>> read_sheets(zip_file &archive); void read_workbook(const xml_document &xml);
std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive); void read_properties_app(const xml_document &xml);
std::vector<relationship> read_relationships(zip_file &content, const std::string &filename); void read_properties_core(const xml_document &xml);
xml_document write_workbook() const;
xml_document write_properties_app() const;
xml_document write_properties_core() const;
private:
//workbook_view, sheets, sheet, defined_names
std::string determine_document_type(const manifest &manifest_);
using string_pair = std::pair<std::string, std::string>;
std::vector<string_pair> read_sheets(zip_file &archive);
std::vector<string_pair> detect_worksheets(zip_file &archive);
std::string write_defined_names(const workbook &wb);
workbook &wb_;
};
} // namespace xlnt } // namespace xlnt

View File

@ -26,13 +26,27 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <xlnt/worksheet/worksheet.hpp>
namespace xlnt { namespace xlnt {
class relationship; class relationship;
class workbook; class workbook;
class zip_file; class worksheet;
class xml_document;
class xml_node;
void read_worksheet(worksheet ws, zip_file &archive, const relationship &rel, const std::vector<std::string> &shared_strings); class worksheet_serializer
std::string read_dimension(const std::string &xml_string); {
public:
worksheet_serializer(worksheet sheet);
bool read_worksheet(const xml_document &xml, const std::vector<std::string> &shared_strings, const relationship &rel, worksheet ws);
std::string read_dimension(const xml_node &dimension_node);
bool write_worksheet(const worksheet ws, const std::vector<std::string> &string_table, xml_document &xml);
private:
worksheet sheet_;
};
} // namespace xlnt } // namespace xlnt

View File

@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <vector>
#include <xlnt/s11n/xml_node.hpp>
namespace xlnt {
class xml_document
{
public:
void set_encoding(const std::string &encoding);
void add_namespace(const std::string &id, const std::string &uri);
xml_node &root();
const xml_node &root() const;
private:
xml_node root_;
std::string encoding_;
std::vector<string_pair> namespaces_;
};
} // namespace xlnt

View File

@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <vector>
namespace xlnt {
using string_pair = std::pair<std::string, std::string>;
class xml_node
{
public:
xml_node();
explicit xml_node(const std::string &name);
std::string get_name() const;
void set_name(const std::string &name);
bool has_text() const;
std::string get_text() const;
void set_text(const std::string &text);
const std::vector<xml_node> &get_children() const;
bool has_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 std::string &child_name);
const std::vector<string_pair> &get_attributes() const;
bool has_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);
private:
std::string name_;
bool has_text_ = false;
std::string text_;
std::vector<string_pair> attributes_;
std::vector<xml_node> children_;
};
} // namespace xlnt

View File

@ -0,0 +1,16 @@
#pragma once
#include <string>
namespace xlnt {
class xml_document;
class xml_serializer
{
public:
static std::string serialize(const xml_document &xml);
static xml_document deserialize(const std::string &xml_string);
};
} // namespace xlnt

View File

@ -23,6 +23,10 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#pragma once #pragma once
#include <string>
#include <xlnt/common/hash_combine.hpp>
namespace xlnt { namespace xlnt {
class color class color
@ -57,12 +61,6 @@ public:
{ {
} }
bool operator==(const color &other) const
{
if(type_ != other.type_) return false;
return index_ == other.index_;
}
void set_auto(int auto_index) void set_auto(int auto_index)
{ {
type_ = type::auto_; type_ = type::auto_;
@ -126,6 +124,27 @@ public:
return rgb_string_; return rgb_string_;
} }
std::size_t hash() const
{
auto seed = static_cast<std::size_t>(type_);
if(type_ != type::rgb)
{
hash_combine(seed, index_);
}
else
{
hash_combine(seed, rgb_string_);
}
return seed;
}
bool operator==(const color &other) const
{
return hash() == other.hash();
}
private: private:
type type_ = type::indexed; type type_ = type::indexed;
int index_ = 0; int index_ = 0;

View File

@ -25,7 +25,7 @@
#include <cstddef> #include <cstddef>
#include "../common/hash_combine.hpp" #include <xlnt/styles/color.hpp>
namespace xlnt { namespace xlnt {
@ -73,8 +73,7 @@ public:
if(color_assigned_) if(color_assigned_)
{ {
hash_combine(seed, static_cast<std::size_t>(color_type_)); hash_combine(seed, color_.hash());
hash_combine(seed, color_);
} }
return seed; return seed;
@ -85,12 +84,7 @@ public:
return style_; return style_;
} }
color_type get_color_type() const color get_color() const
{
return color_type_;
}
std::size_t get_color() const
{ {
return color_; return color_;
} }
@ -107,7 +101,7 @@ public:
bool operator==(const side &other) const bool operator==(const side &other) const
{ {
return other.style_ == style_ && color_type_ == other.color_type_ && color_ == other.color_; return other.style_ == style_ && color_ == other.color_;
} }
void set_border_style(border_style bs) void set_border_style(border_style bs)
@ -116,19 +110,17 @@ public:
style_ = bs; style_ = bs;
} }
void set_color(color_type type, std::size_t color) void set_color(color new_color)
{ {
color_assigned_ = true; color_assigned_ = true;
color_type_ = type; color_ = new_color;
color_ = color;
} }
private: private:
bool style_assigned_ = false; bool style_assigned_ = false;
border_style style_ = border_style::none; border_style style_ = border_style::none;
bool color_assigned_ = false; bool color_assigned_ = false;
color_type color_type_ = color_type::indexed; color color_ = color::black;
std::size_t color_ = 0;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -72,8 +72,7 @@ public:
} }
private: private:
friend class style_reader; friend class style_serializer;
friend class style_writer;
friend class workbook; friend class workbook;
std::size_t id_; std::size_t id_;

View File

@ -0,0 +1,60 @@
#pragma once
#include <string>
#include <vector>
namespace xlnt {
class default_type
{
public:
default_type();
default_type(const std::string &extension, const std::string &content_type);
default_type(const default_type &other);
default_type &operator=(const default_type &other);
std::string get_extension() const { return extension_; }
std::string get_content_type() const { return content_type_; }
private:
std::string extension_;
std::string content_type_;
};
class override_type
{
public:
override_type();
override_type(const std::string &extension, const std::string &content_type);
override_type(const override_type &other);
override_type &operator=(const override_type &other);
std::string get_part_name() const { return part_name_; }
std::string get_content_type() const { return content_type_; }
private:
std::string part_name_;
std::string content_type_;
};
class manifest
{
public:
void add_default_type(const std::string &extension, const std::string &content_type);
void add_override_type(const std::string &part_name, const std::string &content_type);
const std::vector<default_type> &get_default_types() const { return default_types_; }
const std::vector<override_type> &get_override_types() const { return override_types_; }
bool has_default_type(const std::string &extension) const;
bool has_override_type(const std::string &part_name) const;
std::string get_default_type(const std::string &extension) const;
std::string get_override_type(const std::string &part_name) const;
private:
std::vector<default_type> default_types_;
std::vector<override_type> override_types_;
};
} // namespace xlnt

View File

@ -0,0 +1,10 @@
#pragma once
namespace xlnt {
class theme
{
};
} // namespace xlnt

View File

@ -29,7 +29,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "../common/relationship.hpp" #include <xlnt/common/relationship.hpp>
namespace xlnt { namespace xlnt {
@ -40,6 +40,7 @@ class document_properties;
class drawing; class drawing;
class fill; class fill;
class font; class font;
class manifest;
class named_range; class named_range;
class number_format; class number_format;
class pattern_fill; class pattern_fill;
@ -242,6 +243,9 @@ public:
const style &get_style(std::size_t style_id) const; const style &get_style(std::size_t style_id) const;
std::size_t add_style(const style &style_); std::size_t add_style(const style &style_);
manifest &get_manifest();
const manifest &get_manifest() 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

@ -262,8 +262,8 @@ public:
const range operator()(const cell_reference &top_left, const cell_reference &bottom_right) const; const range operator()(const cell_reference &top_left, const cell_reference &bottom_right) const;
// page // page
page_setup &get_page_setup(); const page_setup &get_page_setup() const;
margins &get_page_margins(); const margins &get_page_margins() const;
// auto filter // auto filter
range_reference get_auto_filter() const; range_reference get_auto_filter() const;

View File

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

View File

@ -1,56 +0,0 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <vector>
namespace xlnt {
class workbook;
class zip_file;
class excel_writer
{
public:
excel_writer(workbook &wb);
void save(const std::string &filename, bool as_template);
void write_data(zip_file &archive, bool as_template);
void write_shared_strings(zip_file &archive, const std::vector<std::string> &shared_strings);
void write_images(zip_file &archive);
void write_charts(zip_file &archive);
void write_chartsheets(zip_file &archive);
void write_worksheets(zip_file &archive, const std::vector<std::string> &shared_strings);
void write_external_links(zip_file &archive);
private:
workbook wb_;
};
bool save_workbook(workbook &wb, const std::string &filename, bool as_template = false);
std::vector<std::uint8_t> save_virtual_workbook(xlnt::workbook &wb, bool as_template = false);
} // namespace xlnt

View File

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

View File

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

View File

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

View File

@ -1,61 +0,0 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <array>
#include <string>
#include <unordered_map>
#include <vector>
#include "../styles/style.hpp"
namespace xlnt {
class workbook;
class style_writer
{
public:
style_writer(workbook &wb);
style_writer(const style_writer &);
style_writer &operator=(const style_writer &);
std::string write_table() const;
std::string write_number_formats();
private:
std::vector<style> get_style_list(const workbook &wb) const;
std::unordered_map<int, std::string> write_fonts() const;
std::unordered_map<int, std::string> write_fills() const;
std::unordered_map<int, std::string> write_borders() const;
void write_cell_style_xfs();
void write_cell_xfs();
void write_cell_style();
void write_dxfs();
void write_table_styles();
workbook &wb_;
};
} // namespace xlnt

View File

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

View File

@ -1,13 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
namespace xlnt {
class worksheet;
std::string write_worksheet(worksheet ws, const std::vector<std::string> &string_table);
} // namespace xlnt

View File

@ -799,7 +799,9 @@ std::string format_text(const std::string &text, const std::string &format)
namespace xlnt { namespace xlnt {
const std::unordered_map<std::string, int> cell::ErrorCodes = const std::unordered_map<std::string, int> cell::error_codes()
{
static const std::unordered_map<std::string, int> codes =
{ {
{"#NULL!", 0}, {"#NULL!", 0},
{"#DIV/0!", 1}, {"#DIV/0!", 1},
@ -810,6 +812,9 @@ const std::unordered_map<std::string, int> cell::ErrorCodes =
{"#N/A!", 6} {"#N/A!", 6}
}; };
return codes;
};
cell::cell() : d_(nullptr) cell::cell() : d_(nullptr)
{ {
} }
@ -1271,11 +1276,6 @@ void cell::set_data_type(type t)
d_->type_ = t; d_->type_ = t;
} }
std::size_t cell::get_xf_index() const
{
return d_->xf_index_;
}
const number_format &cell::get_number_format() const const number_format &cell::get_number_format() const
{ {
static const number_format default_format; static const number_format default_format;

View File

@ -22,7 +22,6 @@ cell_impl::cell_impl(worksheet_impl *parent, column_t column, row_t row)
value_numeric_(0), value_numeric_(0),
has_hyperlink_(false), has_hyperlink_(false),
is_merged_(false), is_merged_(false),
xf_index_(0),
has_style_(false), has_style_(false),
style_id_(0), style_id_(0),
comment_(nullptr) comment_(nullptr)

View File

@ -140,7 +140,7 @@ struct cell_impl
type_ = cell::type::formula; type_ = cell::type::formula;
value_string_.clear(); value_string_.clear();
} }
else if(cell::ErrorCodes.find(s) != cell::ErrorCodes.end()) else if(cell::error_codes().find(s) != cell::error_codes().end())
{ {
type_ = cell::type::error; type_ = cell::type::error;
} }
@ -195,8 +195,6 @@ struct cell_impl
bool is_merged_; bool is_merged_;
std::size_t xf_index_;
bool has_style_; bool has_style_;
std::size_t style_id_; std::size_t style_id_;

View File

@ -23,7 +23,8 @@ struct workbook_impl
borders_(other.borders_), borders_(other.borders_),
fills_(other.fills_), fills_(other.fills_),
fonts_(other.fonts_), fonts_(other.fonts_),
number_formats_(other.number_formats_) number_formats_(other.number_formats_),
manifest_(other.manifest_)
{ {
} }
@ -45,6 +46,7 @@ struct workbook_impl
fonts_ = other.fonts_; fonts_ = other.fonts_;
number_formats_ = other.number_formats_; number_formats_ = other.number_formats_;
colors_ = other.colors_; colors_ = other.colors_;
manifest_ = other.manifest_;
return *this; return *this;
} }
@ -69,6 +71,7 @@ struct workbook_impl
std::vector<font> fonts_; std::vector<font> fonts_;
std::vector<number_format> number_formats_; std::vector<number_format> number_formats_;
manifest manifest_;
}; };
} // namespace detail } // namespace detail

View File

@ -1,38 +0,0 @@
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/shared_strings_reader.hpp>
#include "detail/include_pugixml.hpp"
namespace xlnt {
std::vector<std::string> shared_strings_reader::read_strings(zip_file &archive)
{
static const std::string shared_strings_filename = "xl/sharedStrings.xml";
if(!archive.has_file(shared_strings_filename))
{
return { };
}
pugi::xml_document doc;
doc.load(archive.read(shared_strings_filename).c_str());
auto root_node = doc.child("sst");
int unique_count = root_node.attribute("uniqueCount").as_int();
std::vector<std::string> shared_strings;
for(auto si_node : root_node)
{
shared_strings.push_back(si_node.child("t").text().as_string());
}
if(unique_count != (int)shared_strings.size())
{
throw std::runtime_error("counts don't match");
}
return shared_strings;
}
} // namespace xlnt

View File

@ -1,484 +0,0 @@
#include <xlnt/common/zip_file.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/named_style.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/style.hpp>
#include <xlnt/reader/style_reader.hpp>
#include "detail/include_pugixml.hpp"
namespace xlnt {
protection read_protection(pugi::xml_node node)
{
auto prot_type_from_string = [](const std::string &type_string)
{
if(type_string == "true") return protection::type::protected_;
else if(type_string == "inherit") return protection::type::inherit;
return protection::type::unprotected;
};
protection prot;
prot.set_locked(prot_type_from_string(node.attribute("locked").as_string()));
prot.set_hidden(prot_type_from_string(node.attribute("hidden").as_string()));
return prot;
}
alignment read_alignment(pugi::xml_node node)
{
alignment align;
align.set_wrap_text(node.attribute("wrapText").as_bool());
bool has_vertical = node.attribute("vertical") != nullptr;
std::string vertical = has_vertical ? node.attribute("vertical").as_string() : "";
if(has_vertical)
{
if(vertical == "bottom")
{
align.set_vertical(alignment::vertical_alignment::bottom);
}
else if(vertical =="center")
{
align.set_vertical(alignment::vertical_alignment::center);
}
else if(vertical =="justify")
{
align.set_vertical(alignment::vertical_alignment::justify);
}
else if(vertical =="top")
{
align.set_vertical(alignment::vertical_alignment::top);
}
else
{
throw "unknown alignment";
}
}
bool has_horizontal = node.attribute("horizontal") != nullptr;
std::string horizontal = has_horizontal ? node.attribute("horizontal").as_string() : "";
if(has_horizontal)
{
if(horizontal == "left")
{
align.set_horizontal(alignment::horizontal_alignment::left);
}
else if(horizontal == "center")
{
align.set_horizontal(alignment::horizontal_alignment::center);
}
else if(horizontal == "center-continuous")
{
align.set_horizontal(alignment::horizontal_alignment::center_continuous);
}
else if(horizontal == "right")
{
align.set_horizontal(alignment::horizontal_alignment::right);
}
else if(horizontal == "justify")
{
align.set_horizontal(alignment::horizontal_alignment::justify);
}
else if(horizontal == "general")
{
align.set_horizontal(alignment::horizontal_alignment::general);
}
else
{
throw "unknown alignment";
}
}
return align;
}
style style_reader::read_style(pugi::xml_node stylesheet_node, pugi::xml_node xf_node)
{
style s;
s.apply_number_format(xf_node.attribute("applyNumberFormat").as_bool());
s.number_format_id_ = xf_node.attribute("numFmtId").as_int();
bool builtin_format = true;
for(auto num_fmt : number_formats_)
{
if(num_fmt.get_id() == s.get_number_format_id())
{
s.number_format_ = num_fmt;
builtin_format = false;
break;
}
}
if(builtin_format)
{
s.number_format_ = number_format::from_builtin_id(s.get_number_format_id());
}
s.apply_font(xf_node.attribute("applyFont").as_bool());
s.font_id_ = xf_node.attribute("fontId") != nullptr ? xf_node.attribute("fontId").as_int() : 0;
s.font_ = fonts_[s.font_id_];
s.apply_fill(xf_node.attribute("applyFill").as_bool());
s.fill_id_ = xf_node.attribute("fillId").as_int();
s.fill_ = fills_[s.fill_id_];
s.apply_border(xf_node.attribute("applyBorder").as_bool());
s.border_id_ = xf_node.attribute("borderId").as_int();
s.border_ = borders_[s.border_id_];
s.apply_protection(xf_node.attribute("protection") != nullptr);
if(s.protection_apply_)
{
auto inline_protection = read_protection(xf_node.child("protection"));
s.protection_ = inline_protection;
}
s.apply_alignment(xf_node.child("alignment") != nullptr);
if(s.alignment_apply_)
{
auto inline_alignment = read_alignment(xf_node.child("alignment"));
s.alignment_ = inline_alignment;
}
return s;
}
style_reader::style_reader(workbook &wb)
{
}
void style_reader::read_styles(zip_file &archive)
{
auto xml = archive.read("xl/styles.xml");
pugi::xml_document doc;
doc.load_string(xml.c_str());
auto root = doc.root();
auto stylesheet_node = root.child("styleSheet");
read_borders(stylesheet_node.child("borders"));
read_fills(stylesheet_node.child("fills"));
read_fonts(stylesheet_node.child("fonts"));
read_number_formats(stylesheet_node.child("numFmts"));
auto cell_xfs_node = stylesheet_node.child("cellXfs");
for(auto xf_node : cell_xfs_node.children("xf"))
{
styles_.push_back(read_style(stylesheet_node, xf_node));
styles_.back().id_ = styles_.size() - 1;
}
read_colors(stylesheet_node.child("colors"));
read_dxfs();
read_cell_styles();
read_named_styles(root.child("namedStyles"));
}
void style_reader::read_number_formats(pugi::xml_node num_fmts_node)
{
for(auto num_fmt_node : num_fmts_node.children("numFmt"))
{
number_format nf;
nf.set_format_string(num_fmt_node.attribute("formatCode").as_string());
if(nf.get_format_string() == "GENERAL")
{
nf.set_format_string("General");
}
nf.set_id(num_fmt_node.attribute("numFmtId").as_int());
number_formats_.push_back(nf);
}
}
void style_reader::read_dxfs()
{
}
void style_reader::read_fonts(pugi::xml_node fonts_node)
{
for(auto font_node : fonts_node)
{
font new_font;
new_font.set_size(font_node.child("sz").attribute("val").as_int());
new_font.set_name(font_node.child("name").attribute("val").as_string());
if(font_node.child("color").attribute("theme") != nullptr)
{
new_font.set_color(color(color::type::theme, font_node.child("color").attribute("theme").as_ullong()));
}
else if(font_node.child("color").attribute("indexed") != nullptr)
{
new_font.set_color(color(color::type::indexed, font_node.child("color").attribute("indexed").as_ullong()));
}
if(font_node.child("family") != nullptr)
{
new_font.set_family(font_node.child("family").attribute("val").as_int());
}
if(font_node.child("scheme") != nullptr)
{
new_font.set_scheme(font_node.child("scheme").attribute("val").as_string());
}
if(font_node.child("b") != nullptr)
{
new_font.set_bold(font_node.child("b").attribute("val").as_bool());
}
if(font_node.child("strike") != nullptr)
{
new_font.set_strikethrough(font_node.child("strike").attribute("val").as_bool());
}
if(font_node.child("i") != nullptr)
{
new_font.set_italic(font_node.child("i").attribute("val").as_bool());
}
if(font_node.child("u") != nullptr)
{
std::string underline_string = font_node.child("u").attribute("val").as_string();
if(underline_string == "single")
{
new_font.set_underline(font::underline_style::single);
}
}
fonts_.push_back(new_font);
}
}
void style_reader::read_colors(pugi::xml_node colors_node)
{
auto indexed_colors_node = colors_node.child("indexedColors");
for(auto rgb_color_node : indexed_colors_node.children("rgbColor"))
{
colors_.push_back(rgb_color_node.attribute("rgb").as_string());
}
}
void style_reader::read_fills(pugi::xml_node fills_node)
{
auto pattern_fill_type_from_string = [](const std::string &fill_type)
{
if(fill_type == "none") return fill::pattern_type::none;
if(fill_type == "solid") return fill::pattern_type::solid;
if(fill_type == "gray125") return fill::pattern_type::gray125;
throw std::runtime_error("invalid fill type");
};
for(auto fill_node : fills_node)
{
fill new_fill;
if(fill_node.child("patternFill") != nullptr)
{
auto pattern_fill_node = fill_node.child("patternFill");
new_fill.set_type(fill::type::pattern);
std::string pattern_type_string = pattern_fill_node.attribute("patternType").as_string();
auto pattern_type = pattern_fill_type_from_string(pattern_type_string);
new_fill.set_pattern_type(pattern_type);
auto bg_color_node = pattern_fill_node.child("bgColor");
if(bg_color_node != nullptr)
{
if(bg_color_node.attribute("indexed") != nullptr)
{
new_fill.set_background_color(color(color::type::indexed, bg_color_node.attribute("indexed").as_ullong()));
}
else if(bg_color_node.attribute("auto") != nullptr)
{
new_fill.set_background_color(color(color::type::auto_, bg_color_node.attribute("auto").as_ullong()));
}
else if(bg_color_node.attribute("theme") != nullptr)
{
new_fill.set_background_color(color(color::type::theme, bg_color_node.attribute("theme").as_ullong()));
}
}
auto fg_color_node = pattern_fill_node.child("fgColor");
if(fg_color_node != nullptr)
{
if(fg_color_node.attribute("indexed") != nullptr)
{
new_fill.set_foreground_color(color(color::type::indexed, fg_color_node.attribute("indexed").as_ullong()));
}
else if(fg_color_node.attribute("auto") != nullptr)
{
new_fill.set_foreground_color(color(color::type::auto_, fg_color_node.attribute("auto").as_ullong()));
}
else if(fg_color_node.attribute("theme") != nullptr)
{
new_fill.set_foreground_color(color(color::type::theme, fg_color_node.attribute("theme").as_ullong()));
}
}
}
fills_.push_back(new_fill);
}
}
void read_side(pugi::xml_node side_node, side &side, bool &assigned)
{
if(side_node != nullptr)
{
assigned = true;
if(side_node.attribute("style") != nullptr)
{
std::string border_style_string = side_node.attribute("style").as_string();
if(border_style_string == "none")
{
side.set_border_style(border_style::none);
}
else if(border_style_string == "dashdot")
{
side.set_border_style(border_style::dashdot);
}
else if(border_style_string == "dashdotdot")
{
side.set_border_style(border_style::dashdotdot);
}
else if(border_style_string == "dashed")
{
side.set_border_style(border_style::dashed);
}
else if(border_style_string == "dotted")
{
side.set_border_style(border_style::dotted);
}
else if(border_style_string == "double")
{
side.set_border_style(border_style::double_);
}
else if(border_style_string == "hair")
{
side.set_border_style(border_style::hair);
}
else if(border_style_string == "medium")
{
side.set_border_style(border_style::medium);
}
else if(border_style_string == "mediumdashdot")
{
side.set_border_style(border_style::mediumdashdot);
}
else if(border_style_string == "mediumdashdotdot")
{
side.set_border_style(border_style::mediumdashdotdot);
}
else if(border_style_string == "mediumdashed")
{
side.set_border_style(border_style::mediumdashed);
}
else if(border_style_string == "slantdashdot")
{
side.set_border_style(border_style::slantdashdot);
}
else if(border_style_string == "thick")
{
side.set_border_style(border_style::thick);
}
else if(border_style_string == "thin")
{
side.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = side_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
side.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
side.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
}
void style_reader::read_borders(pugi::xml_node borders_node)
{
for(auto border_node : borders_node)
{
border new_border;
std::vector<std::tuple<std::string, side *, bool *>> sides;
sides.push_back(std::make_tuple("start", &new_border.start, &new_border.start_assigned));
sides.push_back(std::make_tuple("end", &new_border.end, &new_border.end_assigned));
sides.push_back(std::make_tuple("left", &new_border.left, &new_border.left_assigned));
sides.push_back(std::make_tuple("right", &new_border.right, &new_border.right_assigned));
sides.push_back(std::make_tuple("top", &new_border.top, &new_border.top_assigned));
sides.push_back(std::make_tuple("bottom", &new_border.bottom, &new_border.bottom_assigned));
sides.push_back(std::make_tuple("diagonal", &new_border.diagonal, &new_border.diagonal_assigned));
sides.push_back(std::make_tuple("vertical", &new_border.vertical, &new_border.vertical_assigned));
sides.push_back(std::make_tuple("horizontal", &new_border.horizontal, &new_border.horizontal_assigned));
for(const auto &side : sides)
{
read_side(border_node.child(std::get<0>(side).c_str()), *std::get<1>(side), *std::get<2>(side));
}
borders_.push_back(new_border);
}
}
void style_reader::read_named_styles(pugi::xml_node named_styles_node)
{
}
void style_reader::read_style_names()
{
}
void style_reader::read_cell_styles()
{
}
void style_reader::read_xfs()
{
}
void style_reader::read_style_table()
{
}
} // namespace xlnt

View File

@ -1,240 +0,0 @@
#include <algorithm>
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include "detail/include_pugixml.hpp"
namespace {
xlnt::datetime w3cdtf_to_datetime(const std::string &string)
{
xlnt::datetime result(1900, 1, 1);
auto separator_index = string.find('-');
result.year = std::stoi(string.substr(0, separator_index));
result.month = std::stoi(string.substr(separator_index + 1, string.find('-', separator_index + 1)));
separator_index = string.find('-', separator_index + 1);
result.day = std::stoi(string.substr(separator_index + 1, string.find('T', separator_index + 1)));
separator_index = string.find('T', separator_index + 1);
result.hour = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.minute = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.second = std::stoi(string.substr(separator_index + 1, string.find('Z', separator_index + 1)));
return result;
}
}
namespace xlnt {
std::vector<std::pair<std::string, std::string>> read_sheets(zip_file &archive)
{
auto xml_source = archive.read("xl/workbook.xml");
pugi::xml_document doc;
doc.load(xml_source.c_str());
std::string ns;
for(auto child : doc.children())
{
std::string name = child.name();
if(name.find(':') != std::string::npos)
{
auto colon_index = name.find(':');
ns = name.substr(0, colon_index);
break;
}
}
auto with_ns = [&](const std::string &base) { return ns.empty() ? base : ns + ":" + base; };
auto root_node = doc.child(with_ns("workbook").c_str());
auto sheets_node = root_node.child(with_ns("sheets").c_str());
std::vector<std::pair<std::string, std::string>> sheets;
// store temp because pugixml iteration uses the internal char array multiple times
auto sheet_element_name = with_ns("sheet");
for(auto sheet_node : sheets_node.children(sheet_element_name.c_str()))
{
std::string id = sheet_node.attribute("r:id").as_string();
std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name));
}
return sheets;
}
document_properties read_properties_core(const std::string &xml_string)
{
document_properties props;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("cp:coreProperties");
props.excel_base_date = calendar::windows_1900;
if(root_node.child("dc:creator") != nullptr)
{
props.creator = root_node.child("dc:creator").text().as_string();
}
if(root_node.child("cp:lastModifiedBy") != nullptr)
{
props.last_modified_by = root_node.child("cp:lastModifiedBy").text().as_string();
}
if(root_node.child("dcterms:created") != nullptr)
{
std::string created_string = root_node.child("dcterms:created").text().as_string();
props.created = w3cdtf_to_datetime(created_string);
}
if(root_node.child("dcterms:modified") != nullptr)
{
std::string modified_string = root_node.child("dcterms:modified").text().as_string();
props.modified = w3cdtf_to_datetime(modified_string);
}
return props;
}
std::string read_dimension(const std::string &xml_string)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
return dimension;
}
std::vector<relationship> read_relationships(zip_file &archive, const std::string &filename)
{
auto filename_separator_index = filename.find_last_of('/');
auto basename = filename.substr(filename_separator_index + 1);
auto dirname = filename.substr(0, filename_separator_index);
auto rels_filename = dirname + "/_rels/" + basename + ".rels";
pugi::xml_document doc;
auto content = archive.read(rels_filename);
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
std::vector<relationship> relationships;
for(auto relationship : root_node.children("Relationship"))
{
std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").as_string();
if(target[0] != '/' && target.substr(0, 2) != "..")
{
target = dirname + "/" + target;
}
if(target[0] == '/')
{
target = target.substr(1);
}
relationships.push_back(xlnt::relationship(type, id, target));
}
return relationships;
}
std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive)
{
pugi::xml_document doc;
try
{
auto content_types_string = archive.read("[Content_Types].xml");
doc.load(content_types_string.c_str());
}
catch(std::exception e)
{
throw invalid_file_exception(archive.get_filename());
}
auto root_node = doc.child("Types");
std::vector<std::pair<std::string, std::string>> override_types;
for(auto child : root_node.children("Override"))
{
std::string part_name = child.attribute("PartName").as_string();
std::string content_type = child.attribute("ContentType").as_string();
override_types.push_back({part_name, content_type});
}
return override_types;
}
std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types)
{
auto match = std::find_if(override_types.begin(), override_types.end(), [](const std::pair<std::string, std::string> &p) { return p.first == "/xl/workbook.xml"; });
if(match == override_types.end())
{
return "unsupported";
}
std::string type = match->second;
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";
}
std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive)
{
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
auto content_types = read_content_types(archive);
std::vector<std::string> valid_sheets;
for(const auto &content_type : content_types)
{
if(content_type.second == ValidWorksheet)
{
valid_sheets.push_back(content_type.first);
}
}
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
std::vector<std::pair<std::string, std::string>> result;
for(const auto &ws : read_sheets(archive))
{
auto rel = *std::find_if(workbook_relationships.begin(), workbook_relationships.end(), [&](const relationship &r) { return r.get_id() == ws.first; });
auto target = rel.get_target_uri();
if(std::find(valid_sheets.begin(), valid_sheets.end(), "/" + target) != valid_sheets.end())
{
result.push_back({target, ws.second});
}
}
return result;
}
}

View File

@ -0,0 +1,5 @@
#include <xlnt/s11n/comment_serializer.hpp>
namespace {
} // namespace xlnt

View File

@ -1,17 +1,7 @@
#include <xlnt/common/datetime.hpp> #include <xlnt/s11n/excel_serializer.hpp>
#include <xlnt/common/exceptions.hpp> #include <xlnt/s11n/manifest_serializer.hpp>
#include <xlnt/common/zip_file.hpp> #include <xlnt/workbook/manifest.hpp>
#include <xlnt/styles/style.hpp>
#include <xlnt/reader/excel_reader.hpp>
#include <xlnt/reader/shared_strings_reader.hpp>
#include <xlnt/reader/style_reader.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp> #include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include "detail/include_pugixml.hpp"
namespace { namespace {
@ -166,4 +156,112 @@ xlnt::workbook excel_reader::load_workbook(const std::vector<std::uint8_t> &byte
return ::load_workbook(archive, guess_types, data_only); return ::load_workbook(archive, guess_types, data_only);
} }
excel_writer::excel_writer(workbook &wb) : wb_(wb)
{
}
void excel_writer::save(const std::string &filename, bool as_template)
{
zip_file archive;
write_data(archive, as_template);
archive.save(filename);
}
void excel_writer::write_data(zip_file &archive, bool as_template)
{
archive.writestr(constants::ArcRootRels, write_root_rels(wb_));
archive.writestr(constants::ArcWorkbookRels, write_workbook_rels(wb_));
archive.writestr(constants::ArcApp, write_properties_app(wb_));
archive.writestr(constants::ArcCore, write_properties_core(wb_.get_properties()));
if(wb_.has_loaded_theme())
{
archive.writestr(constants::ArcTheme, wb_.get_loaded_theme());
}
else
{
archive.writestr(constants::ArcTheme, write_theme());
}
archive.writestr(constants::ArcWorkbook, write_workbook(wb_));
auto shared_strings = extract_all_strings(wb_);
write_charts(archive);
write_images(archive);
write_shared_strings(archive, shared_strings);
write_worksheets(archive, shared_strings);
write_chartsheets(archive);
write_external_links(archive);
style_writer style_writer_(wb_);
archive.writestr(constants::ArcStyles, style_writer_.write_table());
auto manifest = write_content_types(wb_, as_template);
archive.writestr(constants::ArcContentTypes, manifest);
}
void excel_writer::write_shared_strings(xlnt::zip_file &archive, const std::vector<std::string> &shared_strings)
{
archive.writestr(constants::ArcSharedString, ::xlnt::write_shared_strings(shared_strings));
}
void excel_writer::write_images(zip_file &/*archive*/)
{
}
void excel_writer::write_charts(zip_file &/*archive*/)
{
}
void excel_writer::write_chartsheets(zip_file &/*archive*/)
{
}
void excel_writer::write_worksheets(zip_file &archive, const std::vector<std::string> &shared_strings)
{
std::size_t index = 0;
for(auto ws : wb_)
{
for(auto relationship : wb_.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet &&
workbook::index_from_ws_filename(relationship.get_target_uri()) == index)
{
archive.writestr(relationship.get_target_uri(), write_worksheet(ws, shared_strings));
break;
}
}
index++;
}
}
void excel_writer::write_external_links(zip_file &/*archive*/)
{
}
bool save_workbook(workbook &wb, const std::string &filename, bool as_template)
{
excel_writer writer(wb);
writer.save(filename, as_template);
return true;
}
std::vector<std::uint8_t> save_virtual_workbook(xlnt::workbook &wb, bool as_template)
{
zip_file archive;
excel_writer writer(wb);
writer.write_data(archive, as_template);
std::vector<std::uint8_t> buffer;
archive.save(buffer);
return buffer;
}
} // namespace xlnt } // namespace xlnt

View File

@ -0,0 +1,33 @@
#include <xlnt/s11n/manifest_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/workbook/manifest.hpp>
#include "detail/constants.hpp"
namespace xlnt {
bool manifest_serializer::write_manifest(xml_document &xml)
{
xml.add_namespace("", constants::Namespaces.at("content-types"));
auto root_node = xml.root();
root_node.set_name("Types");
for(const auto &default_type : manifest_.get_default_types())
{
auto type_node = root_node.add_child("Default");
type_node.add_attribute("Extension", default_type.get_extension());
type_node.add_attribute("ContentType", default_type.get_extension());
}
for(const auto &override_type : manifest_.get_override_types())
{
auto type_node = root_node.add_child("Override");
type_node.add_attribute("PartName", override_type.get_part_name());
type_node.add_attribute("ContentType", override_type.get_content_type());
}
return true;
}
} // namespace xlnt

View File

@ -0,0 +1,47 @@
#include <xlnt/s11n/relationship_serializer.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include "detail/constants.hpp"
namespace xlnt {
bool relationship_serializer::read_relationships(const xml_document &xml, const std::string &dir, std::vector<relationship> &relationships)
{
return false;
}
bool relationship_serializer::write_relationships(const std::vector<relationship> &relationships, const std::string &dir, xml_document &xml)
{
xml.add_namespace("", constants::Namespaces.at("relationships"));
auto root_node = xml.root();
root_node.set_name("Relationships");
for(const auto &relationship : relationships)
{
auto target = relationship.get_target_uri();
if (dir != "" && target.substr(0, dir.size()) == dir)
{
target = target.substr(dir.size());
}
auto app_props_node = root_node.add_child("Relationship");
app_props_node.add_attribute("Id", relationship.get_id());
app_props_node.add_attribute("Target", target);
app_props_node.add_attribute("Type", relationship.get_type_string());
if(relationship.get_target_mode() == target_mode::external)
{
app_props_node.add_attribute("TargetMode", "External");
}
}
return true;
}
}

View File

@ -0,0 +1,29 @@
#include <xlnt/s11n/shared_strings_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
namespace xlnt {
bool shared_strings_serializer::read_strings(const xml_document &xml, std::vector<std::string> &strings)
{
strings.clear();
auto root_node = xml.root();
root_node.set_name("sst");
auto unique_count = std::stoull(root_node.get_attribute("uniqueCount"));
for(const auto &si_node : root_node.get_children())
{
strings.push_back(si_node.get_child("t").get_text());
}
if(unique_count != strings.size())
{
throw std::runtime_error("counts don't match");
}
return true;
}
} // namespace xlnt

View File

@ -0,0 +1,900 @@
#include <xlnt/s11n/style_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/protection.hpp>
#include <xlnt/styles/style.hpp>
namespace {
bool is_true(const std::string &bool_string)
{
return bool_string == "1";
}
xlnt::protection::type protection_type_from_string(const std::string &type_string)
{
if(type_string == "true")
{
return xlnt::protection::type::protected_;
}
else if(type_string == "inherit")
{
return xlnt::protection::type::inherit;
}
return xlnt::protection::type::unprotected;
};
xlnt::font::underline_style underline_style_from_string(const std::string &underline_string)
{
if(underline_string == "none")
{
return xlnt::font::underline_style::none;
}
else if(underline_string == "single")
{
return xlnt::font::underline_style::single;
}
else if(underline_string == "single-accounting")
{
return xlnt::font::underline_style::single_accounting;
}
else if(underline_string == "double")
{
return xlnt::font::underline_style::double_;
}
else if(underline_string == "double-accounting")
{
return xlnt::font::underline_style::double_accounting;
}
return xlnt::font::underline_style::none;
}
xlnt::fill::pattern_type pattern_fill_type_from_string(const std::string &fill_type)
{
if(fill_type == "none") return xlnt::fill::pattern_type::none;
if(fill_type == "solid") return xlnt::fill::pattern_type::solid;
if(fill_type == "gray125") return xlnt::fill::pattern_type::gray125;
return xlnt::fill::pattern_type::none;
};
xlnt::border_style border_style_from_string(const std::string &border_style_string)
{
if(border_style_string == "none")
{
return xlnt::border_style::none;
}
else if(border_style_string == "dashdot")
{
return xlnt::border_style::dashdot;
}
else if(border_style_string == "dashdotdot")
{
return xlnt::border_style::dashdotdot;
}
else if(border_style_string == "dashed")
{
return xlnt::border_style::dashed;
}
else if(border_style_string == "dotted")
{
return xlnt::border_style::dotted;
}
else if(border_style_string == "double")
{
return xlnt::border_style::double_;
}
else if(border_style_string == "hair")
{
return xlnt::border_style::hair;
}
else if(border_style_string == "medium")
{
return xlnt::border_style::medium;
}
else if(border_style_string == "mediumdashdot")
{
return xlnt::border_style::mediumdashdot;
}
else if(border_style_string == "mediumdashdotdot")
{
return xlnt::border_style::mediumdashdotdot;
}
else if(border_style_string == "mediumdashed")
{
return xlnt::border_style::mediumdashed;
}
else if(border_style_string == "slantdashdot")
{
return xlnt::border_style::slantdashdot;
}
else if(border_style_string == "thick")
{
return xlnt::border_style::thick;
}
else if(border_style_string == "thin")
{
return xlnt::border_style::thin;
}
else
{
throw std::runtime_error("unknown border style");
}
}
} // namespace
namespace xlnt {
style_serializer::style_serializer(workbook &wb) : wb_(wb)
{
}
protection style_serializer::read_protection(const xml_node &protection_node) const
{
protection prot;
prot.set_locked(protection_type_from_string(protection_node.get_attribute("locked")));
prot.set_hidden(protection_type_from_string(protection_node.get_attribute("hidden")));
return prot;
}
alignment style_serializer::read_alignment(const xml_node &alignment_node) const
{
alignment align;
align.set_wrap_text(is_true(alignment_node.get_attribute("wrapText")));
bool has_vertical = alignment_node.has_attribute("vertical");
if(has_vertical)
{
std::string vertical = alignment_node.get_attribute("vertical");
if(vertical == "bottom")
{
align.set_vertical(alignment::vertical_alignment::bottom);
}
else if(vertical =="center")
{
align.set_vertical(alignment::vertical_alignment::center);
}
else if(vertical =="justify")
{
align.set_vertical(alignment::vertical_alignment::justify);
}
else if(vertical =="top")
{
align.set_vertical(alignment::vertical_alignment::top);
}
else
{
throw "unknown alignment";
}
}
bool has_horizontal = alignment_node.has_attribute("horizontal");
if(has_horizontal)
{
std::string horizontal = alignment_node.get_attribute("horizontal");
if(horizontal == "left")
{
align.set_horizontal(alignment::horizontal_alignment::left);
}
else if(horizontal == "center")
{
align.set_horizontal(alignment::horizontal_alignment::center);
}
else if(horizontal == "center-continuous")
{
align.set_horizontal(alignment::horizontal_alignment::center_continuous);
}
else if(horizontal == "right")
{
align.set_horizontal(alignment::horizontal_alignment::right);
}
else if(horizontal == "justify")
{
align.set_horizontal(alignment::horizontal_alignment::justify);
}
else if(horizontal == "general")
{
align.set_horizontal(alignment::horizontal_alignment::general);
}
else
{
throw "unknown alignment";
}
}
return align;
}
style style_serializer::read_style(const xml_node &style_node) const
{
style s;
s.apply_number_format(is_true(style_node.get_attribute("applyNumberFormat")));
s.number_format_id_ = std::stoull(style_node.get_attribute("numFmtId"));
bool builtin_format = true;
for(auto num_fmt : wb_.get_number_formats())
{
if(num_fmt.get_id() == s.get_number_format_id())
{
s.number_format_ = num_fmt;
builtin_format = false;
break;
}
}
if(builtin_format)
{
s.number_format_ = number_format::from_builtin_id(s.get_number_format_id());
}
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_ = wb_.get_font(s.font_id_);
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_ = wb_.get_fill(s.fill_id_);
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_ = wb_.get_border(s.border_id_);
s.apply_protection(style_node.has_attribute("protection"));
if(s.protection_apply_)
{
auto inline_protection = read_protection(style_node.get_child("protection"));
s.protection_ = inline_protection;
}
s.apply_alignment(style_node.has_child("alignment"));
if(s.alignment_apply_)
{
auto inline_alignment = read_alignment(style_node.get_child("alignment"));
s.alignment_ = inline_alignment;
}
return s;
}
bool style_serializer::read_stylesheet(const xml_document &xml)
{
auto stylesheet_node = xml.root();
read_borders(stylesheet_node.get_child("borders"));
read_fills(stylesheet_node.get_child("fills"));
read_fonts(stylesheet_node.get_child("fonts"));
read_number_formats(stylesheet_node.get_child("numFmts"));
read_colors(stylesheet_node.get_child("colors"));
auto cell_xfs_node = stylesheet_node.get_child("cellXfs");
for(auto xf_node : cell_xfs_node.get_children())
{
if(xf_node.get_name() != "xf")
{
continue;
}
wb_.add_style(read_style(xf_node));
}
return true;
}
bool style_serializer::read_number_formats(const xml_node &number_formats_node)
{
for(auto num_fmt_node : number_formats_node.get_children())
{
if(num_fmt_node.get_name() != "numFmt")
{
continue;
}
auto format_string = num_fmt_node.get_attribute("formatCode");
if(format_string == "GENERAL")
{
format_string = "General";
}
number_format nf;
nf.set_format_string(format_string);
nf.set_id(std::stoull(num_fmt_node.get_attribute("numFmtId")));
wb_.add_number_format(nf);
}
return true;
}
bool style_serializer::read_fonts(const xml_node &fonts_node)
{
for(auto font_node : fonts_node.get_children())
{
wb_.add_font(read_font(font_node));
}
return true;
}
font style_serializer::read_font(const xlnt::xml_node &font_node) const
{
font new_font;
new_font.set_size(std::stoi(font_node.get_child("sz").get_attribute("val")));
new_font.set_name(font_node.get_child("name").get_attribute("val"));
if(font_node.has_child("color"))
{
new_font.set_color(read_color(font_node.get_child("color")));
}
if(font_node.has_child("family"))
{
new_font.set_family(std::stoull(font_node.get_child("family").get_attribute("val")));
}
if(font_node.has_child("scheme"))
{
new_font.set_scheme(font_node.get_child("scheme").get_attribute("val"));
}
if(font_node.has_child("b"))
{
new_font.set_bold(is_true(font_node.get_child("b").get_attribute("val")));
}
if(font_node.has_child("strike"))
{
new_font.set_strikethrough(is_true(font_node.get_child("strike").get_attribute("val")));
}
if(font_node.has_child("i"))
{
new_font.set_italic(is_true(font_node.get_child("i").get_attribute("val")));
}
if(font_node.has_child("u"))
{
std::string underline_string = font_node.get_child("u").get_attribute("val");
new_font.set_underline(underline_style_from_string(underline_string));
}
return new_font;
}
bool style_serializer::read_colors(const xml_node &colors_node)
{
if(colors_node.has_child("indexedColors"))
{
auto indexed_colors = read_indexed_colors(colors_node.get_child("indexedColors"));
for(const auto &indexed_color : indexed_colors)
{
wb_.add_color(indexed_color);
}
}
return true;
}
bool style_serializer::read_fills(const xml_node &fills_node)
{
for(auto fill_node : fills_node.get_children())
{
wb_.add_fill(read_fill(fill_node));
}
return true;
}
fill style_serializer::read_fill(const xml_node &fill_node) const
{
fill new_fill;
if(fill_node.has_child("patternFill"))
{
new_fill.set_type(fill::type::pattern);
auto pattern_fill_node = fill_node.get_child("patternFill");
new_fill.set_pattern_type(pattern_fill_type_from_string(pattern_fill_node.get_attribute("patternType")));
if(pattern_fill_node.has_child("bgColor"))
{
new_fill.set_background_color(read_color(pattern_fill_node.get_child("bgColor")));
}
if(pattern_fill_node.has_child("fgColor"))
{
new_fill.set_foreground_color(read_color(pattern_fill_node.get_child("fgColor")));
}
}
return new_fill;
}
side style_serializer::read_side(const xml_node &side_node) const
{
side new_side;
if(side_node.has_attribute("style"))
{
new_side.set_border_style(border_style_from_string(side_node.get_attribute("style")));
}
if(side_node.has_child("color"))
{
new_side.set_color(read_color(side_node.get_child("color")));
}
return new_side;
}
bool style_serializer::read_borders(const xml_node &borders_node)
{
for(auto border_node : borders_node.get_children())
{
wb_.add_border(read_border(border_node));
}
return true;
}
border style_serializer::read_border(const xml_node &border_node) const
{
border new_border;
if(border_node.has_child("start"))
{
new_border.start_assigned = true;
new_border.start = read_side(border_node.get_child("start"));
}
if(border_node.has_child("end"))
{
new_border.end_assigned = true;
new_border.end = read_side(border_node.get_child("end"));
}
if(border_node.has_child("left"))
{
new_border.left_assigned = true;
new_border.left = read_side(border_node.get_child("left"));
}
if(border_node.has_child("right"))
{
new_border.right_assigned = true;
new_border.right = read_side(border_node.get_child("right"));
}
if(border_node.has_child("top"))
{
new_border.top_assigned = true;
new_border.top = read_side(border_node.get_child("top"));
}
if(border_node.has_child("bottom"))
{
new_border.bottom_assigned = true;
new_border.bottom = read_side(border_node.get_child("bottom"));
}
if(border_node.has_child("diagonal"))
{
new_border.diagonal_assigned = true;
new_border.diagonal = read_side(border_node.get_child("diagonal"));
}
if(border_node.has_child("vertical"))
{
new_border.vertical_assigned = true;
new_border.vertical = read_side(border_node.get_child("vertical"));
}
if(border_node.has_child("horizontal"))
{
new_border.horizontal_assigned = true;
new_border.horizontal = read_side(border_node.get_child("horizontal"));
}
return new_border;
}
bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const
{
xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
xml.add_namespace("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006");
xml.add_namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
auto &style_sheet_node = doc.add_child("styleSheet");
xml.add_attribute("mc:Ignorable", "x14ac");
auto num_fmts_node = style_sheet_node.add_child("numFmts");
auto num_fmts = wb_.get_number_formats();
num_fmts_node.add_attribute("count", static_cast<int>(num_fmts.size()));
for(auto &num_fmt : num_fmts)
{
auto num_fmt_node = num_fmts_node.add_child("numFmt");
num_fmt_node.add_attribute("numFmtId", num_fmt.get_id());
num_fmt_node.add_attribute("formatCode", num_fmt.get_format_string());
}
auto fonts_node = style_sheet_node.add_child("fonts");
auto fonts = wb_.get_fonts();
if(fonts.empty())
{
fonts.push_back(font());
}
fonts_node.add_attribute("count", static_cast<int>(fonts.size()));
fonts_node.add_attribute("x14ac:knownFonts", 1);
for(auto &f : fonts)
{
auto font_node = fonts_node.add_child("font");
if(f.is_bold())
{
auto bold_node = font_node.add_child("b");
bold_node.add_attribute("val", 1);
}
if(f.is_italic())
{
auto bold_node = font_node.add_child("i");
bold_node.add_attribute("val", 1);
}
if(f.is_underline())
{
auto bold_node = font_node.add_child("u");
switch(f.get_underline())
{
case font::underline_style::single:
bold_node.add_attribute("val", "single");
break;
default:
break;
}
}
if(f.is_strikethrough())
{
auto bold_node = font_node.add_child("strike");
bold_node.add_attribute("val", 1);
}
auto size_node = font_node.add_child("sz");
size_node.add_attribute("val", f.get_size());
auto color_node = font_node.add_child("color");
if(f.get_color().get_type() == color::type::indexed)
{
color_node.add_attribute("indexed", static_cast<unsigned int>(f.get_color().get_index()));
}
else if(f.get_color().get_type() == color::type::theme)
{
color_node.add_attribute("theme", static_cast<unsigned int>(f.get_color().get_theme()));
}
auto name_node = font_node.add_child("name");
name_node.add_attribute("val", f.get_name());
if(f.has_family())
{
auto family_node = font_node.add_child("family");
family_node.add_attribute("val", 2);
}
if(f.has_scheme())
{
auto scheme_node = font_node.add_child("scheme");
scheme_node.add_attribute("val", "minor");
}
}
auto fills_node = style_sheet_node.add_child("fills");
const auto &fills = wb_.get_fills();
fills_node.add_attribute("count", static_cast<unsigned int>(fills.size()));
for(auto &fill_ : fills)
{
auto fill_node = fills_node.add_child("fill");
if(fill_.get_type() == fill::type::pattern)
{
auto pattern_fill_node = fill_node.add_child("patternFill");
auto type_string = fill_.get_pattern_type_string();
pattern_fill_node.add_attribute("patternType", type_string);
if(fill_.has_foreground_color())
{
auto fg_color_node = pattern_fill_node.add_child("fgColor");
switch(fill_.get_foreground_color().get_type())
{
case color::type::auto_: fg_color_node.add_attribute("auto", fill_.get_foreground_color().get_auto()); break;
case color::type::theme: fg_color_node.add_attribute("theme", fill_.get_foreground_color().get_theme()); break;
case color::type::indexed: fg_color_node.add_attribute("indexed", fill_.get_foreground_color().get_index()); break;
default: throw std::runtime_error("bad type");
}
}
if(fill_.has_background_color())
{
auto bg_color_node = pattern_fill_node.add_child("bgColor");
switch(fill_.get_background_color().get_type())
{
case color::type::auto_: bg_color_node.add_attribute("auto", fill_.get_background_color().get_auto()); break;
case color::type::theme: bg_color_node.add_attribute("theme", fill_.get_background_color().get_theme()); break;
case color::type::indexed: bg_color_node.add_attribute("indexed", fill_.get_background_color().get_index()); break;
default: throw std::runtime_error("bad type");
}
}
}
else if(fill_.get_type() == fill::type::solid)
{
auto solid_fill_node = fill_node.add_child("solidFill");
solid_fill_node.add_child("color");
}
else if(fill_.get_type() == fill::type::gradient)
{
auto gradient_fill_node = fill_node.add_child("gradientFill");
if(fill_.get_gradient_type_string() == "linear")
{
gradient_fill_node.add_attribute("degree", fill_.get_rotation());
}
else if(fill_.get_gradient_type_string() == "path")
{
gradient_fill_node.add_attribute("left", fill_.get_gradient_left());
gradient_fill_node.add_attribute("right", fill_.get_gradient_right());
gradient_fill_node.add_attribute("top", fill_.get_gradient_top());
gradient_fill_node.add_attribute("bottom", fill_.get_gradient_bottom());
auto start_node = gradient_fill_node.add_child("stop");
start_node.add_attribute("position", 0);
auto end_node = gradient_fill_node.add_child("stop");
end_node.add_attribute("position", 1);
}
}
}
auto borders_node = style_sheet_node.add_child("borders");
const auto &borders = wb_.get_borders();
borders_node.add_attribute("count", static_cast<unsigned int>(borders.size()));
for(const auto &border_ : borders)
{
auto border_node = borders_node.add_child("border");
std::vector<std::tuple<std::string, const side, bool>> sides;
sides.push_back(std::make_tuple("start", border_.start, border_.start_assigned));
sides.push_back(std::make_tuple("end", border_.end, border_.end_assigned));
sides.push_back(std::make_tuple("left", border_.left, border_.left_assigned));
sides.push_back(std::make_tuple("right", border_.right, border_.right_assigned));
sides.push_back(std::make_tuple("top", border_.top, border_.top_assigned));
sides.push_back(std::make_tuple("bottom", border_.bottom, border_.bottom_assigned));
sides.push_back(std::make_tuple("diagonal", border_.diagonal, border_.diagonal_assigned));
sides.push_back(std::make_tuple("vertical", border_.vertical, border_.vertical_assigned));
sides.push_back(std::make_tuple("horizontal", border_.horizontal, border_.horizontal_assigned));
for(const auto &side_tuple : sides)
{
std::string name = std::get<0>(side_tuple);
const side side_ = std::get<1>(side_tuple);
bool assigned = std::get<2>(side_tuple);
if(assigned)
{
auto side_node = border_node.add_child(name);
if(side_.is_style_assigned())
{
auto style_attribute = side_node.add_attribute("style");
switch(side_.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::dashdot : style_attribute.set_value("dashdot"); break;
case border_style::dashdotdot : style_attribute.set_value("dashdotdot"); break;
case border_style::dashed : style_attribute.set_value("dashed"); break;
case border_style::dotted : style_attribute.set_value("dotted"); break;
case border_style::double_ : style_attribute.set_value("double"); break;
case border_style::hair : style_attribute.set_value("hair"); break;
case border_style::medium : style_attribute.set_value("thin"); break;
case border_style::mediumdashdot: style_attribute.set_value("mediumdashdot"); break;
case border_style::mediumdashdotdot: style_attribute.set_value("mediumdashdotdot"); break;
case border_style::mediumdashed: style_attribute.set_value("mediumdashed"); break;
case border_style::slantdashdot: style_attribute.set_value("slantdashdot"); break;
case border_style::thick: style_attribute.set_value("thick"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(side_.is_color_assigned())
{
auto color_node = side_node.add_child("color");
if(side_.get_color_type() == side::color_type::indexed)
{
color_node.add_attribute("indexed", (int)side_.get_color());
}
else if(side_.get_color_type() == side::color_type::theme)
{
color_node.add_attribute("indexed", (int)side_.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
}
}
auto cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs");
cell_style_xfs_node.add_attribute("count", 1);
auto style_xf_node = cell_style_xfs_node.add_child("xf");
style_xf_node.add_attribute("numFmtId", 0);
style_xf_node.add_attribute("fontId", 0);
style_xf_node.add_attribute("fillId", 0);
style_xf_node.add_attribute("borderId", 0);
auto cell_xfs_node = style_sheet_node.add_child("cellXfs");
const auto &styles = wb_.get_styles();
cell_xfs_node.add_attribute("count", static_cast<int>(styles.size()));
for(auto &style : styles)
{
auto xf_node = cell_xfs_node.add_child("xf");
xf_node.add_attribute("numFmtId", style.get_number_format().get_id());
xf_node.add_attribute("fontId", (int)style.get_font_id());
if(style.fill_apply_)
{
xf_node.add_attribute("fillId", (int)style.get_fill_id());
}
if(style.border_apply_)
{
xf_node.add_attribute("borderId", (int)style.get_border_id());
}
xf_node.add_attribute("applyNumberFormat", style.number_format_apply_ ? 1 : 0);
xf_node.add_attribute("applyFont", style.font_apply_ ? 1 : 0);
xf_node.add_attribute("applyFill", style.fill_apply_ ? 1 : 0);
xf_node.add_attribute("applyBorder", style.border_apply_ ? 1 : 0);
xf_node.add_attribute("applyAlignment", style.alignment_apply_ ? 1 : 0);
xf_node.add_attribute("applyProtection", style.protection_apply_ ? 1 : 0);
if(style.alignment_apply_)
{
auto alignment_node = xf_node.add_child("alignment");
if(style.alignment_.has_vertical())
{
switch(style.alignment_.get_vertical())
{
case alignment::vertical_alignment::bottom:
alignment_node.add_attribute("vertical", "bottom");
break;
case alignment::vertical_alignment::center:
alignment_node.add_attribute("vertical", "center");
break;
case alignment::vertical_alignment::justify:
alignment_node.add_attribute("vertical", "justify");
break;
case alignment::vertical_alignment::top:
alignment_node.add_attribute("vertical", "top");
break;
default:
throw std::runtime_error("invalid alignment");
}
}
if(style.alignment_.has_horizontal())
{
switch(style.alignment_.get_horizontal())
{
case alignment::horizontal_alignment::center:
alignment_node.add_attribute("horizontal", "center");
break;
case alignment::horizontal_alignment::center_continuous:
alignment_node.add_attribute("horizontal", "center_continuous");
break;
case alignment::horizontal_alignment::general:
alignment_node.add_attribute("horizontal", "general");
break;
case alignment::horizontal_alignment::justify:
alignment_node.add_attribute("horizontal", "justify");
break;
case alignment::horizontal_alignment::left:
alignment_node.add_attribute("horizontal", "left");
break;
case alignment::horizontal_alignment::right:
alignment_node.add_attribute("horizontal", "right");
break;
default:
throw std::runtime_error("invalid alignment");
}
}
if(style.alignment_.get_wrap_text())
{
alignment_node.add_attribute("wrapText", 1);
}
}
}
auto cell_styles_node = style_sheet_node.add_child("cellStyles");
cell_styles_node.add_attribute("count", 1);
auto cell_style_node = cell_styles_node.add_child("cellStyle");
cell_style_node.add_attribute("name", "Normal");
cell_style_node.add_attribute("xfId", 0);
cell_style_node.add_attribute("builtinId", 0);
style_sheet_node.add_child("dxfs").add_attribute("count", 0);
auto table_styles_node = style_sheet_node.add_child("tableStyles");
table_styles_node.add_attribute("count", 0);
table_styles_node.add_attribute("defaultTableStyle", "TableStyleMedium2");
table_styles_node.add_attribute("defaultPivotStyle", "PivotStyleMedium9");
auto colors_node = style_sheet_node.add_child("colors");
auto indexed_colors_node = colors_node.add_child("indexedColors");
for(auto &c : wb_.get_colors())
{
indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string());
}
auto ext_list_node = style_sheet_node.add_child("extLst");
auto ext_node = ext_list_node.add_child("ext");
ext_node.add_attribute("uri", "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}");
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");
return true;
}
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()));
for(const auto &num_fmt : wb_.get_number_formats())
{
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("formatCode", num_fmt.get_format_string());
}
return true;
}
} // namespace xlnt

View File

@ -1,27 +1,33 @@
#include <set> #include <xlnt/s11n/workbook_serializer.hpp>
#include <sstream> #include <xlnt/common/datetime.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/common/exceptions.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_node.hpp>
#include <xlnt/workbook/document_properties.hpp> #include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/named_range.hpp> #include <xlnt/workbook/manifest.hpp>
#include <xlnt/workbook/workbook.hpp> #include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/manifest_writer.hpp>
#include <xlnt/writer/relationship_writer.hpp>
#include <xlnt/writer/theme_writer.hpp>
#include <xlnt/writer/worksheet_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "detail/constants.hpp" #include "detail/constants.hpp"
#include "detail/include_pugixml.hpp"
namespace { namespace {
xlnt::datetime w3cdtf_to_datetime(const std::string &string)
{
xlnt::datetime result(1900, 1, 1);
auto separator_index = string.find('-');
result.year = std::stoi(string.substr(0, separator_index));
result.month = std::stoi(string.substr(separator_index + 1, string.find('-', separator_index + 1)));
separator_index = string.find('-', separator_index + 1);
result.day = std::stoi(string.substr(separator_index + 1, string.find('T', separator_index + 1)));
separator_index = string.find('T', separator_index + 1);
result.hour = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.minute = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.second = std::stoi(string.substr(separator_index + 1, string.find('Z', separator_index + 1)));
return result;
}
std::string fill(const std::string &string, std::size_t length = 2) std::string fill(const std::string &string, std::size_t length = 2)
{ {
if(string.size() >= length) if(string.size() >= length)
@ -41,7 +47,176 @@ std::string datetime_to_w3cdtf(const xlnt::datetime &dt)
namespace xlnt { namespace xlnt {
std::string write_shared_strings(const std::vector<std::string> &string_table) /*
std::vector<std::pair<std::string, std::string>> workbook_serializer::read_sheets(zip_file &archive)
{
std::string ns;
for(auto child : doc.children())
{
std::string name = child.name();
if(name.find(':') != std::string::npos)
{
auto colon_index = name.find(':');
ns = name.substr(0, colon_index);
break;
}
}
auto with_ns = [&](const std::string &base) { return ns.empty() ? base : ns + ":" + base; };
auto root_node = doc.child(with_ns("workbook").c_str());
auto sheets_node = root_node.child(with_ns("sheets").c_str());
std::vector<std::pair<std::string, std::string>> sheets;
// store temp because pugixml iteration uses the internal char array multiple times
auto sheet_element_name = with_ns("sheet");
for(auto sheet_node : sheets_node.children(sheet_element_name.c_str()))
{
std::string id = sheet_node.attribute("r:id").as_string();
std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name));
}
return sheets;
}
*/
void workbook_serializer::read_properties_core(const xml_document &xml)
{
auto &props = wb_.get_properties();
auto root_node = xml.root();
props.excel_base_date = calendar::windows_1900;
if(root_node.has_child("dc:creator"))
{
props.creator = root_node.get_child("dc:creator").get_text();
}
if(root_node.has_child("cp:lastModifiedBy"))
{
props.last_modified_by = root_node.get_child("cp:lastModifiedBy").get_text();
}
if(root_node.has_child("dcterms:created"))
{
std::string created_string = root_node.get_child("dcterms:created").get_text();
props.created = w3cdtf_to_datetime(created_string);
}
if(root_node.has_child("dcterms:modified"))
{
std::string modified_string = root_node.get_child("dcterms:modified").get_text();
props.modified = w3cdtf_to_datetime(modified_string);
}
}
std::string workbook_serializer::read_dimension(const std::string &xml_string)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
return dimension;
}
std::vector<relationship> workbook_serializer::read_relationships(zip_file &archive, const std::string &filename)
{
auto filename_separator_index = filename.find_last_of('/');
auto basename = filename.substr(filename_separator_index + 1);
auto dirname = filename.substr(0, filename_separator_index);
auto rels_filename = dirname + "/_rels/" + basename + ".rels";
pugi::xml_document doc;
auto content = archive.read(rels_filename);
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
std::vector<relationship> relationships;
for(auto relationship : root_node.children("Relationship"))
{
std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").as_string();
if(target[0] != '/' && target.substr(0, 2) != "..")
{
target = dirname + "/" + target;
}
if(target[0] == '/')
{
target = target.substr(1);
}
relationships.push_back(xlnt::relationship(type, id, target));
}
return relationships;
}
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";
}
std::vector<std::pair<std::string, std::string>> workbook_serializer::detect_worksheets(zip_file &archive)
{
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
auto content_types = read_content_types(archive);
std::vector<std::string> valid_sheets;
for(const auto &content_type : content_types)
{
if(content_type.second == ValidWorksheet)
{
valid_sheets.push_back(content_type.first);
}
}
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
std::vector<std::pair<std::string, std::string>> result;
for(const auto &ws : read_sheets(archive))
{
auto rel = *std::find_if(workbook_relationships.begin(), workbook_relationships.end(), [&](const relationship &r) { return r.get_id() == ws.first; });
auto target = rel.get_target_uri();
if(std::find(valid_sheets.begin(), valid_sheets.end(), "/" + target) != valid_sheets.end())
{
result.push_back({target, ws.second});
}
}
return result;
}
std::string workbook_serializer::write_shared_strings(const std::vector<std::string> &string_table)
{ {
pugi::xml_document doc; pugi::xml_document doc;
auto root_node = doc.append_child("sst"); auto root_node = doc.append_child("sst");
@ -59,7 +234,7 @@ std::string write_shared_strings(const std::vector<std::string> &string_table)
return ss.str(); return ss.str();
} }
std::string write_properties_core(const document_properties &prop) std::string workbook_serializer::write_properties_core(const document_properties &prop)
{ {
pugi::xml_document doc; pugi::xml_document doc;
auto root_node = doc.append_child("cp:coreProperties"); auto root_node = doc.append_child("cp:coreProperties");
@ -87,12 +262,12 @@ std::string write_properties_core(const document_properties &prop)
return ss.str(); return ss.str();
} }
std::string write_worksheet_rels(worksheet ws) std::string workbook_serializer::write_worksheet_rels(worksheet ws)
{ {
return write_relationships(ws.get_relationships(), ""); return write_relationships(ws.get_relationships(), "");
} }
std::string write_theme() std::string workbook_serializer::write_theme()
{ {
pugi::xml_document doc; pugi::xml_document doc;
auto theme_node = doc.append_child("a:theme"); auto theme_node = doc.append_child("a:theme");
@ -578,12 +753,12 @@ std::string write_workbook(const workbook &wb)
return ss.str(); return ss.str();
} }
std::string write_workbook_rels(const workbook &wb) std::string workbook_serializer::write_workbook_rels(const workbook &wb)
{ {
return write_relationships(wb.get_relationships(), "xl/"); return write_relationships(wb.get_relationships(), "xl/");
} }
std::string write_defined_names(const xlnt::workbook &wb) std::string workbook_serializer::write_defined_names(const xlnt::workbook &wb)
{ {
pugi::xml_document doc; pugi::xml_document doc;
auto names = doc.root().append_child("names"); auto names = doc.root().append_child("names");

View File

@ -0,0 +1,368 @@
#include <xlnt/s11n/worksheet_serializer.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include "detail/constants.hpp"
namespace {
bool is_integral(long double d)
{
return d == static_cast<long long int>(d);
}
} // namepsace
namespace xlnt {
bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector<std::string> &string_table, xml_document &xml)
{
ws.get_cell("A1");
xml.add_namespace("", constants::Namespaces.at("spreadsheetml"));
xml.add_namespace("r", constants::Namespaces.at("r"));
auto &root_node = xml.root();
root_node.set_name("worksheet");
auto &sheet_pr_node = root_node.add_child("sheetPr");
if(!ws.get_page_setup().is_default())
{
auto &page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr");
page_set_up_pr_node.add_attribute("fitToPage", ws.get_page_setup().fit_to_page() ? "1" : "0");
}
auto &outline_pr_node = sheet_pr_node.add_child("outlinePr");
outline_pr_node.add_attribute("summaryBelow", "1");
outline_pr_node.add_attribute("summaryRight", "1");
auto &dimension_node = root_node.add_child("dimension");
dimension_node.add_attribute("ref", ws.calculate_dimension().to_string());
auto &sheet_views_node = root_node.add_child("sheetViews");
auto &sheet_view_node = sheet_views_node.add_child("sheetView");
sheet_view_node.add_attribute("workbookViewId", "0");
std::string active_pane = "bottomRight";
if(ws.has_frozen_panes())
{
auto pane_node = sheet_view_node.add_child("pane");
if(ws.get_frozen_panes().get_column_index() > 1)
{
pane_node.add_attribute("xSplit", std::to_string(ws.get_frozen_panes().get_column_index() - 1));
active_pane = "topRight";
}
if(ws.get_frozen_panes().get_row() > 1)
{
pane_node.add_attribute("ySplit", std::to_string(ws.get_frozen_panes().get_row() - 1));
active_pane = "bottomLeft";
}
if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1)
{
auto top_right_node = sheet_view_node.add_child("selection");
top_right_node.add_attribute("pane", "topRight");
auto bottom_left_node = sheet_view_node.add_child("selection");
bottom_left_node.add_attribute("pane", "bottomLeft");
active_pane = "bottomRight";
}
pane_node.add_attribute("topLeftCell", ws.get_frozen_panes().to_string());
pane_node.add_attribute("activePane", active_pane);
pane_node.add_attribute("state", "frozen");
}
auto selection_node = sheet_view_node.add_child("selection");
if(ws.has_frozen_panes())
{
if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1)
{
selection_node.add_attribute("pane", "bottomRight");
}
else if(ws.get_frozen_panes().get_row() > 1)
{
selection_node.add_attribute("pane", "bottomLeft");
}
else if(ws.get_frozen_panes().get_column_index() > 1)
{
selection_node.add_attribute("pane", "topRight");
}
}
std::string active_cell = "A1";
selection_node.add_attribute("activeCell", active_cell);
selection_node.add_attribute("sqref", active_cell);
auto sheet_format_pr_node = root_node.add_child("sheetFormatPr");
sheet_format_pr_node.add_attribute("baseColWidth", "10");
sheet_format_pr_node.add_attribute("defaultRowHeight", "15");
bool has_column_properties = false;
for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++)
{
if(ws.has_column_properties(column))
{
has_column_properties = true;
break;
}
}
if(has_column_properties)
{
auto cols_node = root_node.add_child("cols");
for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++)
{
const auto &props = ws.get_column_properties(column);
auto col_node = cols_node.add_child("col");
col_node.add_attribute("min", std::to_string(column));
col_node.add_attribute("max", std::to_string(column));
col_node.add_attribute("width", std::to_string(props.width));
col_node.add_attribute("style", std::to_string(props.style));
col_node.add_attribute("customWidth", props.custom ? "1" : "0");
}
}
std::unordered_map<std::string, std::string> hyperlink_references;
auto sheet_data_node = root_node.add_child("sheetData");
for(auto row : ws.rows())
{
row_t min = static_cast<row_t>(row.num_cells());
row_t max = 0;
bool any_non_null = false;
for(auto cell : row)
{
min = std::min(min, cell_reference::column_index_from_string(cell.get_column()));
max = std::max(max, cell_reference::column_index_from_string(cell.get_column()));
if(!cell.garbage_collectible())
{
any_non_null = true;
}
}
if(!any_non_null)
{
continue;
}
auto row_node = sheet_data_node.add_child("row");
row_node.add_attribute("r", std::to_string(row.front().get_row()));
row_node.add_attribute("spans", (std::to_string(min) + ":" + std::to_string(max)));
if(ws.has_row_properties(row.front().get_row()))
{
row_node.add_attribute("customHeight", "1");
auto height = ws.get_row_properties(row.front().get_row()).height;
if(height == std::floor(height))
{
row_node.add_attribute("ht", std::to_string(static_cast<long long int>(height)) + ".0");
}
else
{
row_node.add_attribute("ht", std::to_string(height));
}
}
//row_node.add_attribute("x14ac:dyDescent", 0.25);
for(auto cell : row)
{
if(!cell.garbage_collectible())
{
if(cell.has_hyperlink())
{
hyperlink_references[cell.get_hyperlink().get_id()] = cell.get_reference().to_string();
}
auto cell_node = row_node.add_child("c");
cell_node.add_attribute("r", cell.get_reference().to_string());
if(cell.get_data_type() == cell::type::string)
{
if(cell.has_formula())
{
cell_node.add_attribute("t", "str");
cell_node.add_child("f").set_text(cell.get_formula());
cell_node.add_child("v").set_text(cell.to_string());
continue;
}
int match_index = -1;
for(std::size_t i = 0; i < string_table.size(); i++)
{
if(string_table[i] == cell.get_value<std::string>())
{
match_index = static_cast<int>(i);
break;
}
}
if(match_index == -1)
{
if(cell.get_value<std::string>().empty())
{
cell_node.add_attribute("t", "s");
}
else
{
cell_node.add_attribute("t", "inlineStr");
auto inline_string_node = cell_node.add_child("is");
inline_string_node.add_child("t").text().set(cell.get_value<std::string>());
}
}
else
{
cell_node.add_attribute("t", "s");
auto value_node = cell_node.add_child("v");
value_node.text().set(match_index);
}
}
else
{
if(cell.get_data_type() != cell::type::null)
{
if(cell.get_data_type() == cell::type::boolean)
{
cell_node.add_attribute("t", "b");
auto value_node = cell_node.add_child("v");
value_node.text().set(cell.get_value<bool>() ? 1 : 0);
}
else if(cell.get_data_type() == cell::type::numeric)
{
if(cell.has_formula())
{
cell_node.add_child("f").text().set(cell.get_formula());
cell_node.add_child("v").text().set(cell.to_string());
continue;
}
cell_node.add_attribute("t", "n");
auto value_node = cell_node.add_child("v");
if(is_integral(cell.get_value<long double>()))
{
value_node.text().set(cell.get_value<long long>());
}
else
{
std::stringstream ss;
ss.precision(20);
ss << cell.get_value<long double>();
ss.str();
value_node.text().set(ss.str());
}
}
}
else if(cell.has_formula())
{
cell_node.add_child("f").text().set(cell.get_formula());
cell_node.add_child("v");
continue;
}
}
//if(cell.has_style())
{
auto style_id = cell.get_style_id();
cell_node.add_attribute("s", (int)style_id);
}
}
}
}
if(ws.has_auto_filter())
{
auto auto_filter_node = root_node.add_child("autoFilter");
auto_filter_node.add_attribute("ref", ws.get_auto_filter().to_string());
}
if(!ws.get_merged_ranges().empty())
{
auto merge_cells_node = root_node.add_child("mergeCells");
merge_cells_node.add_attribute("count", (unsigned int)ws.get_merged_ranges().size());
for(auto merged_range : ws.get_merged_ranges())
{
auto merge_cell_node = merge_cells_node.add_child("mergeCell");
merge_cell_node.add_attribute("ref", merged_range.to_string());
}
}
if(!ws.get_relationships().empty())
{
auto hyperlinks_node = root_node.add_child("hyperlinks");
for(auto relationship : ws.get_relationships())
{
auto hyperlink_node = hyperlinks_node.add_child("hyperlink");
hyperlink_node.add_attribute("display", relationship.get_target_uri());
hyperlink_node.add_attribute("ref", hyperlink_references.at(relationship.get_id()));
hyperlink_node.add_attribute("r:id", relationship.get_id());
}
}
if(!ws.get_page_setup().is_default())
{
auto print_options_node = root_node.add_child("printOptions");
print_options_node.add_attribute("horizontalCentered", ws.get_page_setup().get_horizontal_centered() ? 1 : 0);
print_options_node.add_attribute("verticalCentered", ws.get_page_setup().get_vertical_centered() ? 1 : 0);
}
auto page_margins_node = root_node.add_child("pageMargins");
page_margins_node.add_attribute("left", ws.get_page_margins().get_left());
page_margins_node.add_attribute("right", ws.get_page_margins().get_right());
page_margins_node.add_attribute("top", ws.get_page_margins().get_top());
page_margins_node.add_attribute("bottom", ws.get_page_margins().get_bottom());
page_margins_node.add_attribute("header", ws.get_page_margins().get_header());
page_margins_node.add_attribute("footer", ws.get_page_margins().get_footer());
if(!ws.get_page_setup().is_default())
{
auto page_setup_node = root_node.add_child("pageSetup");
std::string orientation_string = ws.get_page_setup().get_orientation() == page_setup::orientation::landscape ? "landscape" : "portrait";
page_setup_node.add_attribute("orientation", orientation_string);
page_setup_node.add_attribute("paperSize", (int)ws.get_page_setup().get_paper_size());
page_setup_node.add_attribute("fitToHeight", ws.get_page_setup().fit_to_height() ? 1 : 0);
page_setup_node.add_attribute("fitToWidth", ws.get_page_setup().fit_to_width() ? 1 : 0);
}
if(!ws.get_header_footer().is_default())
{
auto header_footer_node = root_node.add_child("headerFooter");
auto odd_header_node = header_footer_node.add_child("oddHeader");
std::string header_text = "&L&\"Calibri,Regular\"&K000000Left Header Text&C&\"Arial,Regular\"&6&K445566Center Header Text&R&\"Arial,Bold\"&8&K112233Right Header Text";
odd_header_node.text().set(header_text);
auto odd_footer_node = header_footer_node.add_child("oddFooter");
std::string footer_text = "&L&\"Times New Roman,Regular\"&10&K445566Left Footer Text_x000D_And &D and &T&C&\"Times New Roman,Bold\"&12&K778899Center Footer Text &Z&F on &A&R&\"Times New Roman,Italic\"&14&KAABBCCRight Footer Text &P of &N";
odd_footer_node.text().set(footer_text);
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
} // namespace xlnt

View File

@ -0,0 +1,25 @@
#include <xlnt/s11n/xml_document.hpp>
namespace xlnt {
void xml_document::set_encoding(const std::string &encoding)
{
}
void xml_document::add_namespace(const std::string &id, const std::string &uri)
{
}
xml_node &xml_document::root()
{
return root_;
}
const xml_node &xml_document::root() const
{
return root_;
}
} // namespace xlnt

107
source/s11n/xml_node.cpp Normal file
View File

@ -0,0 +1,107 @@
#include <xlnt/s11n/xml_node.hpp>
namespace xlnt {
xml_node::xml_node()
{
}
xml_node::xml_node(const std::string &name)
{
set_name(name);
}
std::string xml_node::get_name() const
{
return name_;
}
void xml_node::set_name(const std::string &name)
{
name_ = name;
}
bool xml_node::has_text() const
{
return has_text_;
}
std::string xml_node::get_text() const
{
return text_;
}
void xml_node::set_text(const std::string &text)
{
text_ = text;
has_text_ = true;
}
const std::vector<xml_node> &xml_node::get_children() const
{
return children_;
}
xml_node &xml_node::add_child(const xml_node &child)
{
has_text_ = false;
text_.clear();
children_.push_back(child);
return children_.back();
}
xml_node &xml_node::add_child(const std::string &child_name)
{
return add_child(xml_node(child_name));
}
const std::vector<string_pair> &xml_node::get_attributes() const
{
return attributes_;
}
void xml_node::add_attribute(const std::string &name, const std::string &value)
{
attributes_.push_back(std::make_pair(name, value));
}
bool xml_node::has_attribute(const std::string &attribute_name) const
{
return std::find(attributes_.begin(), attributes_.end(),
[&](const string_pair &p) { return p.first == attribute_name; }) != attributes_.end();
}
std::string xml_node::get_attribute(const std::string &attribute_name) const
{
auto match = std::find(attributes_.begin(), attributes_.end(),
[&](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
{
return std::find(children_.begin(), children_.end(),
[&](const xml_node &n) { return n.get_name() == child_name; }) != children_.end();
}
const xml_node &xml_node::get_child(const std::string &child_name) const
{
auto match = std::find(children_.begin(), children_.end(),
[&](const xml_node &n) { return n.get_name() == child_name; });
if(match == attributes_.end())
{
throw std::runtime_error("child doesn't exist: " + child_name);
}
return *match;
}
} // namespace xlnt

View File

@ -0,0 +1,85 @@
#include <sstream>
#include <xlnt/s11n/xml_serializer.hpp>
#include <xlnt/s11n/xml_document.hpp>
#include <xlnt/s11n/xml_node.hpp>
#include "detail/include_pugixml.hpp"
namespace {
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 {
std::string xml_serializer::serialize(const xml_document &xml)
{
pugi::xml_document doc;
auto root = doc.root();
root.set_name(xml.root().get_name().c_str());
serialize_node(xml.root(), root);
std::ostringstream ss;
doc.save(ss);
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

View File

@ -0,0 +1,43 @@
#include <xlnt/workbook/manifest.hpp>
namespace xlnt {
bool manifest::has_default_type(const std::string &extension) const
{
return std::find_if(default_types_.begin(), default_types_.end(),
[&](const default_type &d) { return d.get_extension() == extension; }) != default_types_.end();
}
bool manifest::has_override_type(const std::string &part_name) const
{
return std::find_if(override_types_.begin(), override_types_.end(),
[&](const override_type &d) { return d.get_part_name() == part_name; }) != override_types_.end();
}
std::string manifest::get_default_type(const std::string &extension) const
{
auto match = std::find_if(default_types_.begin(), default_types_.end(),
[&](const default_type &d) { return d.get_extension() == extension; });
if(match == default_types_.end())
{
throw std::runtime_error("no default type found for extension: " + extension);
}
return match->get_content_type();
}
std::string manifest::get_override_type(const std::string &part_name) const
{
auto match = std::find_if(override_types_.begin(), override_types_.end(),
[&](const override_type &d) { return d.get_part_name() == part_name; });
if(match == override_types_.end())
{
throw std::runtime_error("no default type found for part name: " + part_name);
}
return match->get_content_type();
}
} // namespace xlnt

View File

@ -837,4 +837,14 @@ std::vector<color> workbook::get_colors() const
return d_->colors_; return d_->colors_;
} }
manifest &workbook::get_manifest()
{
return d_->manifest_;
}
const manifest &workbook::get_manifest() const
{
return d_->manifest_;
}
} // namespace xlnt } // namespace xlnt

View File

@ -73,7 +73,7 @@ std::vector<range_reference> worksheet::get_merged_ranges() const
return d_->merged_cells_; return d_->merged_cells_;
} }
margins &worksheet::get_page_margins() const margins &worksheet::get_page_margins() const
{ {
return d_->page_margins_; return d_->page_margins_;
} }
@ -103,7 +103,7 @@ 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() const page_setup &worksheet::get_page_setup() const
{ {
return d_->page_setup_; return d_->page_setup_;
} }

View File

@ -1,152 +0,0 @@
#include <unordered_set>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/excel_writer.hpp>
#include <xlnt/writer/manifest_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include <xlnt/writer/worksheet_writer.hpp>
#include "detail/constants.hpp"
namespace {
std::vector<std::string> extract_all_strings(xlnt::workbook &wb)
{
std::unordered_set<std::string> strings;
for(auto ws : wb)
{
for(auto row : ws.rows())
{
for(auto cell : row)
{
if(cell.get_data_type() == xlnt::cell::type::string)
{
strings.insert(cell.get_value<std::string>());
}
}
}
}
return std::vector<std::string>(strings.begin(), strings.end());
}
} // namespace
namespace xlnt {
excel_writer::excel_writer(workbook &wb) : wb_(wb)
{
}
void excel_writer::save(const std::string &filename, bool as_template)
{
zip_file archive;
write_data(archive, as_template);
archive.save(filename);
}
void excel_writer::write_data(zip_file &archive, bool as_template)
{
archive.writestr(constants::ArcRootRels, write_root_rels(wb_));
archive.writestr(constants::ArcWorkbookRels, write_workbook_rels(wb_));
archive.writestr(constants::ArcApp, write_properties_app(wb_));
archive.writestr(constants::ArcCore, write_properties_core(wb_.get_properties()));
if(wb_.has_loaded_theme())
{
archive.writestr(constants::ArcTheme, wb_.get_loaded_theme());
}
else
{
archive.writestr(constants::ArcTheme, write_theme());
}
archive.writestr(constants::ArcWorkbook, write_workbook(wb_));
auto shared_strings = extract_all_strings(wb_);
write_charts(archive);
write_images(archive);
write_shared_strings(archive, shared_strings);
write_worksheets(archive, shared_strings);
write_chartsheets(archive);
write_external_links(archive);
style_writer style_writer_(wb_);
archive.writestr(constants::ArcStyles, style_writer_.write_table());
auto manifest = write_content_types(wb_, as_template);
archive.writestr(constants::ArcContentTypes, manifest);
}
void excel_writer::write_shared_strings(xlnt::zip_file &archive, const std::vector<std::string> &shared_strings)
{
archive.writestr(constants::ArcSharedString, ::xlnt::write_shared_strings(shared_strings));
}
void excel_writer::write_images(zip_file &/*archive*/)
{
}
void excel_writer::write_charts(zip_file &/*archive*/)
{
}
void excel_writer::write_chartsheets(zip_file &/*archive*/)
{
}
void excel_writer::write_worksheets(zip_file &archive, const std::vector<std::string> &shared_strings)
{
std::size_t index = 0;
for(auto ws : wb_)
{
for(auto relationship : wb_.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet &&
workbook::index_from_ws_filename(relationship.get_target_uri()) == index)
{
archive.writestr(relationship.get_target_uri(), write_worksheet(ws, shared_strings));
break;
}
}
index++;
}
}
void excel_writer::write_external_links(zip_file &/*archive*/)
{
}
bool save_workbook(workbook &wb, const std::string &filename, bool as_template)
{
excel_writer writer(wb);
writer.save(filename, as_template);
return true;
}
std::vector<std::uint8_t> save_virtual_workbook(xlnt::workbook &wb, bool as_template)
{
zip_file archive;
excel_writer writer(wb);
writer.write_data(archive, as_template);
std::vector<std::uint8_t> buffer;
archive.save(buffer);
return buffer;
}
} // namespace xlnt

View File

@ -1,40 +0,0 @@
#include <sstream>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/writer/manifest_writer.hpp>
#include "detail/constants.hpp"
#include "detail/include_pugixml.hpp"
namespace xlnt {
std::string write_content_types(const workbook &wb, bool /*as_template*/)
{
pugi::xml_document doc;
auto root_node = doc.append_child("Types");
root_node.append_attribute("xmlns").set_value(constants::Namespaces.at("content-types").c_str());
for(auto type : wb.get_content_types())
{
pugi::xml_node type_node;
if (type.is_default)
{
type_node = root_node.append_child("Default");
type_node.append_attribute("Extension").set_value(type.extension.c_str());
}
else
{
type_node = root_node.append_child("Override");
type_node.append_attribute("PartName").set_value(type.part_name.c_str());
}
type_node.append_attribute("ContentType").set_value(type.type.c_str());
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
} // namespace xlnt

View File

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

View File

@ -1,409 +0,0 @@
#include <sstream>
#include <pugixml.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/protection.hpp>
#include <xlnt/writer/style_writer.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/cell/cell.hpp>
namespace xlnt {
style_writer::style_writer(xlnt::workbook &wb) : wb_(wb)
{
}
std::string style_writer::write_table() const
{
pugi::xml_document doc;
auto style_sheet_node = doc.append_child("styleSheet");
style_sheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
style_sheet_node.append_attribute("xmlns:mc").set_value("http://schemas.openxmlformats.org/markup-compatibility/2006");
style_sheet_node.append_attribute("mc:Ignorable").set_value("x14ac");
style_sheet_node.append_attribute("xmlns:x14ac").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
auto num_fmts_node = style_sheet_node.append_child("numFmts");
auto num_fmts = wb_.get_number_formats();
num_fmts_node.append_attribute("count").set_value(static_cast<int>(num_fmts.size()));
for(auto &num_fmt : num_fmts)
{
auto num_fmt_node = num_fmts_node.append_child("numFmt");
num_fmt_node.append_attribute("numFmtId").set_value(num_fmt.get_id());
num_fmt_node.append_attribute("formatCode").set_value(num_fmt.get_format_string().c_str());
}
auto fonts_node = style_sheet_node.append_child("fonts");
auto fonts = wb_.get_fonts();
if(fonts.empty())
{
fonts.push_back(font());
}
fonts_node.append_attribute("count").set_value(static_cast<int>(fonts.size()));
fonts_node.append_attribute("x14ac:knownFonts").set_value(1);
for(auto &f : fonts)
{
auto font_node = fonts_node.append_child("font");
if(f.is_bold())
{
auto bold_node = font_node.append_child("b");
bold_node.append_attribute("val").set_value(1);
}
if(f.is_italic())
{
auto bold_node = font_node.append_child("i");
bold_node.append_attribute("val").set_value(1);
}
if(f.is_underline())
{
auto bold_node = font_node.append_child("u");
switch(f.get_underline())
{
case font::underline_style::single:
bold_node.append_attribute("val").set_value("single");
break;
default:
break;
}
}
if(f.is_strikethrough())
{
auto bold_node = font_node.append_child("strike");
bold_node.append_attribute("val").set_value(1);
}
auto size_node = font_node.append_child("sz");
size_node.append_attribute("val").set_value(f.get_size());
auto color_node = font_node.append_child("color");
if(f.get_color().get_type() == color::type::indexed)
{
color_node.append_attribute("indexed").set_value(static_cast<unsigned int>(f.get_color().get_index()));
}
else if(f.get_color().get_type() == color::type::theme)
{
color_node.append_attribute("theme").set_value(static_cast<unsigned int>(f.get_color().get_theme()));
}
auto name_node = font_node.append_child("name");
name_node.append_attribute("val").set_value(f.get_name().c_str());
if(f.has_family())
{
auto family_node = font_node.append_child("family");
family_node.append_attribute("val").set_value(2);
}
if(f.has_scheme())
{
auto scheme_node = font_node.append_child("scheme");
scheme_node.append_attribute("val").set_value("minor");
}
}
auto fills_node = style_sheet_node.append_child("fills");
const auto &fills = wb_.get_fills();
fills_node.append_attribute("count").set_value(static_cast<unsigned int>(fills.size()));
for(auto &fill_ : fills)
{
auto fill_node = fills_node.append_child("fill");
if(fill_.get_type() == fill::type::pattern)
{
auto pattern_fill_node = fill_node.append_child("patternFill");
auto type_string = fill_.get_pattern_type_string();
pattern_fill_node.append_attribute("patternType").set_value(type_string.c_str());
if(fill_.has_foreground_color())
{
auto fg_color_node = pattern_fill_node.append_child("fgColor");
switch(fill_.get_foreground_color().get_type())
{
case color::type::auto_: fg_color_node.append_attribute("auto").set_value(fill_.get_foreground_color().get_auto()); break;
case color::type::theme: fg_color_node.append_attribute("theme").set_value(fill_.get_foreground_color().get_theme()); break;
case color::type::indexed: fg_color_node.append_attribute("indexed").set_value(fill_.get_foreground_color().get_index()); break;
default: throw std::runtime_error("bad type");
}
}
if(fill_.has_background_color())
{
auto bg_color_node = pattern_fill_node.append_child("bgColor");
switch(fill_.get_background_color().get_type())
{
case color::type::auto_: bg_color_node.append_attribute("auto").set_value(fill_.get_background_color().get_auto()); break;
case color::type::theme: bg_color_node.append_attribute("theme").set_value(fill_.get_background_color().get_theme()); break;
case color::type::indexed: bg_color_node.append_attribute("indexed").set_value(fill_.get_background_color().get_index()); break;
default: throw std::runtime_error("bad type");
}
}
}
else if(fill_.get_type() == fill::type::solid)
{
auto solid_fill_node = fill_node.append_child("solidFill");
solid_fill_node.append_child("color");
}
else if(fill_.get_type() == fill::type::gradient)
{
auto gradient_fill_node = fill_node.append_child("gradientFill");
if(fill_.get_gradient_type_string() == "linear")
{
gradient_fill_node.append_attribute("degree").set_value(fill_.get_rotation());
}
else if(fill_.get_gradient_type_string() == "path")
{
gradient_fill_node.append_attribute("left").set_value(fill_.get_gradient_left());
gradient_fill_node.append_attribute("right").set_value(fill_.get_gradient_right());
gradient_fill_node.append_attribute("top").set_value(fill_.get_gradient_top());
gradient_fill_node.append_attribute("bottom").set_value(fill_.get_gradient_bottom());
auto start_node = gradient_fill_node.append_child("stop");
start_node.append_attribute("position").set_value(0);
auto end_node = gradient_fill_node.append_child("stop");
end_node.append_attribute("position").set_value(1);
}
}
}
auto borders_node = style_sheet_node.append_child("borders");
const auto &borders = wb_.get_borders();
borders_node.append_attribute("count").set_value(static_cast<unsigned int>(borders.size()));
for(const auto &border_ : borders)
{
auto border_node = borders_node.append_child("border");
std::vector<std::tuple<std::string, const side, bool>> sides;
sides.push_back(std::make_tuple("start", border_.start, border_.start_assigned));
sides.push_back(std::make_tuple("end", border_.end, border_.end_assigned));
sides.push_back(std::make_tuple("left", border_.left, border_.left_assigned));
sides.push_back(std::make_tuple("right", border_.right, border_.right_assigned));
sides.push_back(std::make_tuple("top", border_.top, border_.top_assigned));
sides.push_back(std::make_tuple("bottom", border_.bottom, border_.bottom_assigned));
sides.push_back(std::make_tuple("diagonal", border_.diagonal, border_.diagonal_assigned));
sides.push_back(std::make_tuple("vertical", border_.vertical, border_.vertical_assigned));
sides.push_back(std::make_tuple("horizontal", border_.horizontal, border_.horizontal_assigned));
for(const auto &side_tuple : sides)
{
std::string name = std::get<0>(side_tuple);
const side side_ = std::get<1>(side_tuple);
bool assigned = std::get<2>(side_tuple);
if(assigned)
{
auto side_node = border_node.append_child(name.c_str());
if(side_.is_style_assigned())
{
auto style_attribute = side_node.append_attribute("style");
switch(side_.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::dashdot : style_attribute.set_value("dashdot"); break;
case border_style::dashdotdot : style_attribute.set_value("dashdotdot"); break;
case border_style::dashed : style_attribute.set_value("dashed"); break;
case border_style::dotted : style_attribute.set_value("dotted"); break;
case border_style::double_ : style_attribute.set_value("double"); break;
case border_style::hair : style_attribute.set_value("hair"); break;
case border_style::medium : style_attribute.set_value("thin"); break;
case border_style::mediumdashdot: style_attribute.set_value("mediumdashdot"); break;
case border_style::mediumdashdotdot: style_attribute.set_value("mediumdashdotdot"); break;
case border_style::mediumdashed: style_attribute.set_value("mediumdashed"); break;
case border_style::slantdashdot: style_attribute.set_value("slantdashdot"); break;
case border_style::thick: style_attribute.set_value("thick"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(side_.is_color_assigned())
{
auto color_node = side_node.append_child("color");
if(side_.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)side_.get_color());
}
else if(side_.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)side_.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
}
}
auto cell_style_xfs_node = style_sheet_node.append_child("cellStyleXfs");
cell_style_xfs_node.append_attribute("count").set_value(1);
auto style_xf_node = cell_style_xfs_node.append_child("xf");
style_xf_node.append_attribute("numFmtId").set_value(0);
style_xf_node.append_attribute("fontId").set_value(0);
style_xf_node.append_attribute("fillId").set_value(0);
style_xf_node.append_attribute("borderId").set_value(0);
auto cell_xfs_node = style_sheet_node.append_child("cellXfs");
const auto &styles = wb_.get_styles();
cell_xfs_node.append_attribute("count").set_value(static_cast<int>(styles.size()));
for(auto &style : styles)
{
auto xf_node = cell_xfs_node.append_child("xf");
xf_node.append_attribute("numFmtId").set_value(style.get_number_format().get_id());
xf_node.append_attribute("fontId").set_value((int)style.get_font_id());
if(style.fill_apply_)
{
xf_node.append_attribute("fillId").set_value((int)style.get_fill_id());
}
if(style.border_apply_)
{
xf_node.append_attribute("borderId").set_value((int)style.get_border_id());
}
xf_node.append_attribute("applyNumberFormat").set_value(style.number_format_apply_ ? 1 : 0);
xf_node.append_attribute("applyFont").set_value(style.font_apply_ ? 1 : 0);
xf_node.append_attribute("applyFill").set_value(style.fill_apply_ ? 1 : 0);
xf_node.append_attribute("applyBorder").set_value(style.border_apply_ ? 1 : 0);
xf_node.append_attribute("applyAlignment").set_value(style.alignment_apply_ ? 1 : 0);
xf_node.append_attribute("applyProtection").set_value(style.protection_apply_ ? 1 : 0);
if(style.alignment_apply_)
{
auto alignment_node = xf_node.append_child("alignment");
if(style.alignment_.has_vertical())
{
switch(style.alignment_.get_vertical())
{
case alignment::vertical_alignment::bottom:
alignment_node.append_attribute("vertical").set_value("bottom");
break;
case alignment::vertical_alignment::center:
alignment_node.append_attribute("vertical").set_value("center");
break;
case alignment::vertical_alignment::justify:
alignment_node.append_attribute("vertical").set_value("justify");
break;
case alignment::vertical_alignment::top:
alignment_node.append_attribute("vertical").set_value("top");
break;
default:
throw std::runtime_error("invalid alignment");
}
}
if(style.alignment_.has_horizontal())
{
switch(style.alignment_.get_horizontal())
{
case alignment::horizontal_alignment::center:
alignment_node.append_attribute("horizontal").set_value("center");
break;
case alignment::horizontal_alignment::center_continuous:
alignment_node.append_attribute("horizontal").set_value("center_continuous");
break;
case alignment::horizontal_alignment::general:
alignment_node.append_attribute("horizontal").set_value("general");
break;
case alignment::horizontal_alignment::justify:
alignment_node.append_attribute("horizontal").set_value("justify");
break;
case alignment::horizontal_alignment::left:
alignment_node.append_attribute("horizontal").set_value("left");
break;
case alignment::horizontal_alignment::right:
alignment_node.append_attribute("horizontal").set_value("right");
break;
default:
throw std::runtime_error("invalid alignment");
}
}
if(style.alignment_.get_wrap_text())
{
alignment_node.append_attribute("wrapText").set_value(1);
}
}
}
auto cell_styles_node = style_sheet_node.append_child("cellStyles");
cell_styles_node.append_attribute("count").set_value(1);
auto cell_style_node = cell_styles_node.append_child("cellStyle");
cell_style_node.append_attribute("name").set_value("Normal");
cell_style_node.append_attribute("xfId").set_value(0);
cell_style_node.append_attribute("builtinId").set_value(0);
style_sheet_node.append_child("dxfs").append_attribute("count").set_value(0);
auto table_styles_node = style_sheet_node.append_child("tableStyles");
table_styles_node.append_attribute("count").set_value(0);
table_styles_node.append_attribute("defaultTableStyle").set_value("TableStyleMedium2");
table_styles_node.append_attribute("defaultPivotStyle").set_value("PivotStyleMedium9");
auto colors_node = style_sheet_node.append_child("colors");
auto indexed_colors_node = colors_node.append_child("indexedColors");
for(auto &c : wb_.get_colors())
{
indexed_colors_node.append_child("rgbColor").append_attribute("rgb").set_value(c.get_rgb_string().c_str());
}
auto ext_list_node = style_sheet_node.append_child("extLst");
auto ext_node = ext_list_node.append_child("ext");
ext_node.append_attribute("uri").set_value("{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}");
ext_node.append_attribute("xmlns:x14").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
ext_node.append_child("x14:slicerStyles").append_attribute("defaultSlicerStyle").set_value("SlicerStyleLight1");
std::stringstream ss;
doc.save(ss);
return ss.str();
}
std::string style_writer::write_number_formats()
{
pugi::xml_document doc;
auto root = doc.append_child("styleSheet");
root.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
auto num_fmts_node = root.append_child("numFmts");
num_fmts_node.append_attribute("count").set_value(1);
auto num_fmt_node = num_fmts_node.append_child("numFmt");
num_fmt_node.append_attribute("formatCode").set_value("YYYY");
num_fmt_node.append_attribute("numFmtId").set_value(164);
std::stringstream ss;
doc.save(ss);
return ss.str();
}
} // namespace xlnt

View File

@ -1,369 +0,0 @@
#include <algorithm>
#include <array>
#include <sstream>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/worksheet_writer.hpp>
#include "detail/constants.hpp"
#include "detail/include_pugixml.hpp"
namespace {
bool is_integral(long double d)
{
return d == static_cast<long long int>(d);
}
} // namepsace
namespace xlnt {
std::string write_worksheet(worksheet ws, const std::vector<std::string> &string_table)
{
ws.get_cell("A1");
pugi::xml_document doc;
auto root_node = doc.append_child("worksheet");
root_node.append_attribute("xmlns").set_value(constants::Namespaces.at("spreadsheetml").c_str());
root_node.append_attribute("xmlns:r").set_value(constants::Namespaces.at("r").c_str());
auto sheet_pr_node = root_node.append_child("sheetPr");
auto outline_pr_node = sheet_pr_node.append_child("outlinePr");
if(!ws.get_page_setup().is_default())
{
auto page_set_up_pr_node = sheet_pr_node.append_child("pageSetUpPr");
page_set_up_pr_node.append_attribute("fitToPage").set_value(ws.get_page_setup().fit_to_page() ? 1 : 0);
}
outline_pr_node.append_attribute("summaryBelow").set_value(1);
outline_pr_node.append_attribute("summaryRight").set_value(1);
auto dimension_node = root_node.append_child("dimension");
dimension_node.append_attribute("ref").set_value(ws.calculate_dimension().to_string().c_str());
auto sheet_views_node = root_node.append_child("sheetViews");
auto sheet_view_node = sheet_views_node.append_child("sheetView");
sheet_view_node.append_attribute("workbookViewId").set_value(0);
std::string active_pane = "bottomRight";
if(ws.has_frozen_panes())
{
auto pane_node = sheet_view_node.append_child("pane");
if(ws.get_frozen_panes().get_column_index() > 1)
{
pane_node.append_attribute("xSplit").set_value(ws.get_frozen_panes().get_column_index() - 1);
active_pane = "topRight";
}
if(ws.get_frozen_panes().get_row() > 1)
{
pane_node.append_attribute("ySplit").set_value(ws.get_frozen_panes().get_row() - 1);
active_pane = "bottomLeft";
}
if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1)
{
auto top_right_node = sheet_view_node.append_child("selection");
top_right_node.append_attribute("pane").set_value("topRight");
auto bottom_left_node = sheet_view_node.append_child("selection");
bottom_left_node.append_attribute("pane").set_value("bottomLeft");
active_pane = "bottomRight";
}
pane_node.append_attribute("topLeftCell").set_value(ws.get_frozen_panes().to_string().c_str());
pane_node.append_attribute("activePane").set_value(active_pane.c_str());
pane_node.append_attribute("state").set_value("frozen");
}
auto selection_node = sheet_view_node.append_child("selection");
if(ws.has_frozen_panes())
{
if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1)
{
selection_node.append_attribute("pane").set_value("bottomRight");
}
else if(ws.get_frozen_panes().get_row() > 1)
{
selection_node.append_attribute("pane").set_value("bottomLeft");
}
else if(ws.get_frozen_panes().get_column_index() > 1)
{
selection_node.append_attribute("pane").set_value("topRight");
}
}
std::string active_cell = "A1";
selection_node.append_attribute("activeCell").set_value(active_cell.c_str());
selection_node.append_attribute("sqref").set_value(active_cell.c_str());
auto sheet_format_pr_node = root_node.append_child("sheetFormatPr");
sheet_format_pr_node.append_attribute("baseColWidth").set_value(10);
sheet_format_pr_node.append_attribute("defaultRowHeight").set_value(15);
bool has_column_properties = false;
for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++)
{
if(ws.has_column_properties(column))
{
has_column_properties = true;
break;
}
}
if(has_column_properties)
{
auto cols_node = root_node.append_child("cols");
for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++)
{
const auto &props = ws.get_column_properties(column);
auto col_node = cols_node.append_child("col");
col_node.append_attribute("min").set_value(column);
col_node.append_attribute("max").set_value(column);
col_node.append_attribute("width").set_value(props.width);
col_node.append_attribute("style").set_value(static_cast<unsigned long long>(props.style));
col_node.append_attribute("customWidth").set_value(props.custom ? 1 : 0);
}
}
std::unordered_map<std::string, std::string> hyperlink_references;
auto sheet_data_node = root_node.append_child("sheetData");
for(auto row : ws.rows())
{
row_t min = static_cast<row_t>(row.num_cells());
row_t max = 0;
bool any_non_null = false;
for(auto cell : row)
{
min = std::min(min, cell_reference::column_index_from_string(cell.get_column()));
max = std::max(max, cell_reference::column_index_from_string(cell.get_column()));
if(!cell.garbage_collectible())
{
any_non_null = true;
}
}
if(!any_non_null)
{
continue;
}
auto row_node = sheet_data_node.append_child("row");
row_node.append_attribute("r").set_value(row.front().get_row());
row_node.append_attribute("spans").set_value((std::to_string(min) + ":" + std::to_string(max)).c_str());
if(ws.has_row_properties(row.front().get_row()))
{
row_node.append_attribute("customHeight").set_value(1);
auto height = ws.get_row_properties(row.front().get_row()).height;
if(height == std::floor(height))
{
row_node.append_attribute("ht").set_value((std::to_string((int)height) + ".0").c_str());
}
else
{
row_node.append_attribute("ht").set_value(height);
}
}
//row_node.append_attribute("x14ac:dyDescent").set_value(0.25);
for(auto cell : row)
{
if(!cell.garbage_collectible())
{
if(cell.has_hyperlink())
{
hyperlink_references[cell.get_hyperlink().get_id()] = cell.get_reference().to_string();
}
auto cell_node = row_node.append_child("c");
cell_node.append_attribute("r").set_value(cell.get_reference().to_string().c_str());
if(cell.get_data_type() == cell::type::string)
{
if(cell.has_formula())
{
cell_node.append_attribute("t").set_value("str");
cell_node.append_child("f").text().set(cell.get_formula().c_str());
cell_node.append_child("v").text().set(cell.to_string().c_str());
continue;
}
int match_index = -1;
for(std::size_t i = 0; i < string_table.size(); i++)
{
if(string_table[i] == cell.get_value<std::string>())
{
match_index = static_cast<int>(i);
break;
}
}
if(match_index == -1)
{
if(cell.get_value<std::string>().empty())
{
cell_node.append_attribute("t").set_value("s");
}
else
{
cell_node.append_attribute("t").set_value("inlineStr");
auto inline_string_node = cell_node.append_child("is");
inline_string_node.append_child("t").text().set(cell.get_value<std::string>().c_str());
}
}
else
{
cell_node.append_attribute("t").set_value("s");
auto value_node = cell_node.append_child("v");
value_node.text().set(match_index);
}
}
else
{
if(cell.get_data_type() != cell::type::null)
{
if(cell.get_data_type() == cell::type::boolean)
{
cell_node.append_attribute("t").set_value("b");
auto value_node = cell_node.append_child("v");
value_node.text().set(cell.get_value<bool>() ? 1 : 0);
}
else if(cell.get_data_type() == cell::type::numeric)
{
if(cell.has_formula())
{
cell_node.append_child("f").text().set(cell.get_formula().c_str());
cell_node.append_child("v").text().set(cell.to_string().c_str());
continue;
}
cell_node.append_attribute("t").set_value("n");
auto value_node = cell_node.append_child("v");
if(is_integral(cell.get_value<long double>()))
{
value_node.text().set(cell.get_value<long long>());
}
else
{
std::stringstream ss;
ss.precision(20);
ss << cell.get_value<long double>();
ss.str();
value_node.text().set(ss.str().c_str());
}
}
}
else if(cell.has_formula())
{
cell_node.append_child("f").text().set(cell.get_formula().c_str());
cell_node.append_child("v");
continue;
}
}
//if(cell.has_style())
{
auto style_id = cell.get_style_id();
cell_node.append_attribute("s").set_value((int)style_id);
}
}
}
}
if(ws.has_auto_filter())
{
auto auto_filter_node = root_node.append_child("autoFilter");
auto_filter_node.append_attribute("ref").set_value(ws.get_auto_filter().to_string().c_str());
}
if(!ws.get_merged_ranges().empty())
{
auto merge_cells_node = root_node.append_child("mergeCells");
merge_cells_node.append_attribute("count").set_value((unsigned int)ws.get_merged_ranges().size());
for(auto merged_range : ws.get_merged_ranges())
{
auto merge_cell_node = merge_cells_node.append_child("mergeCell");
merge_cell_node.append_attribute("ref").set_value(merged_range.to_string().c_str());
}
}
if(!ws.get_relationships().empty())
{
auto hyperlinks_node = root_node.append_child("hyperlinks");
for(auto relationship : ws.get_relationships())
{
auto hyperlink_node = hyperlinks_node.append_child("hyperlink");
hyperlink_node.append_attribute("display").set_value(relationship.get_target_uri().c_str());
hyperlink_node.append_attribute("ref").set_value(hyperlink_references.at(relationship.get_id()).c_str());
hyperlink_node.append_attribute("r:id").set_value(relationship.get_id().c_str());
}
}
if(!ws.get_page_setup().is_default())
{
auto print_options_node = root_node.append_child("printOptions");
print_options_node.append_attribute("horizontalCentered").set_value(ws.get_page_setup().get_horizontal_centered() ? 1 : 0);
print_options_node.append_attribute("verticalCentered").set_value(ws.get_page_setup().get_vertical_centered() ? 1 : 0);
}
auto page_margins_node = root_node.append_child("pageMargins");
page_margins_node.append_attribute("left").set_value(ws.get_page_margins().get_left());
page_margins_node.append_attribute("right").set_value(ws.get_page_margins().get_right());
page_margins_node.append_attribute("top").set_value(ws.get_page_margins().get_top());
page_margins_node.append_attribute("bottom").set_value(ws.get_page_margins().get_bottom());
page_margins_node.append_attribute("header").set_value(ws.get_page_margins().get_header());
page_margins_node.append_attribute("footer").set_value(ws.get_page_margins().get_footer());
if(!ws.get_page_setup().is_default())
{
auto page_setup_node = root_node.append_child("pageSetup");
std::string orientation_string = ws.get_page_setup().get_orientation() == page_setup::orientation::landscape ? "landscape" : "portrait";
page_setup_node.append_attribute("orientation").set_value(orientation_string.c_str());
page_setup_node.append_attribute("paperSize").set_value((int)ws.get_page_setup().get_paper_size());
page_setup_node.append_attribute("fitToHeight").set_value(ws.get_page_setup().fit_to_height() ? 1 : 0);
page_setup_node.append_attribute("fitToWidth").set_value(ws.get_page_setup().fit_to_width() ? 1 : 0);
}
if(!ws.get_header_footer().is_default())
{
auto header_footer_node = root_node.append_child("headerFooter");
auto odd_header_node = header_footer_node.append_child("oddHeader");
std::string header_text = "&L&\"Calibri,Regular\"&K000000Left Header Text&C&\"Arial,Regular\"&6&K445566Center Header Text&R&\"Arial,Bold\"&8&K112233Right Header Text";
odd_header_node.text().set(header_text.c_str());
auto odd_footer_node = header_footer_node.append_child("oddFooter");
std::string footer_text = "&L&\"Times New Roman,Regular\"&10&K445566Left Footer Text_x000D_And &D and &T&C&\"Times New Roman,Bold\"&12&K778899Center Footer Text &Z&F on &A&R&\"Times New Roman,Italic\"&14&KAABBCCRight Footer Text &P of &N";
odd_footer_node.text().set(footer_text.c_str());
}
std::stringstream ss;
doc.save(ss);
return ss.str();
}
} // namespace xlnt

View File

@ -22,6 +22,8 @@
#include <xlnt/writer/excel_writer.hpp> #include <xlnt/writer/excel_writer.hpp>
#include <xlnt/reader/excel_reader.hpp> #include <xlnt/reader/excel_reader.hpp>
#include <xlnt/common/xml_tree.hpp>
class test_cell : public CxxTest::TestSuite class test_cell : public CxxTest::TestSuite
{ {
private: private:
@ -33,6 +35,13 @@ public:
wb_guess_types.set_guess_types(true); wb_guess_types.set_guess_types(true);
} }
void test_debug()
{
auto wb = xlnt::excel_reader::load_workbook("/Users/thomas/Development/xlnt/samples/formatting.xlsx");
wb.save("/Users/thomas/Development/xlnt/samples/formatting-rt.xlsx");
wb.save("/Users/thomas/Development/xlnt/samples/formatting-rt.zip");
}
void test_infer_numeric() void test_infer_numeric()
{ {
auto ws = wb_guess_types.create_sheet(); auto ws = wb_guess_types.create_sheet();