finished minimal styles implementation

This commit is contained in:
Thomas Fussell 2015-10-23 14:42:36 -04:00
parent 21b3d366d6
commit c802a1f591
42 changed files with 2283 additions and 1016 deletions

View File

@ -111,7 +111,9 @@ public:
bool has_hyperlink() const;
// style
bool has_style() const;
std::size_t get_style_id() const;
void set_style_id(std::size_t style_id);
const number_format &get_number_format() const;
void set_number_format(const number_format &format);
const font &get_font() const;

View File

@ -0,0 +1,15 @@
#pragma once
#include <functional>
namespace xlnt {
//TODO: Can this be in source->detail, or must it be in a header?
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
} // namespace xlnt

View File

@ -23,6 +23,7 @@
// @author: see AUTHORS file
#pragma once
#include <iostream>
#include <string>
#include <vector>
@ -30,9 +31,15 @@ namespace xlnt {
class workbook;
std::string CentralDirectorySignature();
std::string repair_central_directory(const std::string &original);
workbook load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
workbook load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types = false, bool data_only = false);
class excel_reader
{
public:
static std::string CentralDirectorySignature();
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);
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);
};
} // namespace xlnt

View File

@ -28,9 +28,15 @@
namespace xlnt {
std::vector<std::string> read_shared_strings(const std::string &xml_string);
void read_string_table(const std::string &xml_source);
std::string get_string();
std::string get_text();
class zip_file;
class shared_strings_reader
{
public:
std::vector<std::string> read_strings(zip_file &archive);
void read_string_table(const std::string &xml_source);
std::string get_string();
std::string get_text();
};
} // namespace xlnt

View File

@ -27,21 +27,60 @@
#include <unordered_map>
#include <vector>
#include "style.h"
#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;
void read_styles();
void read_custom_num_formats();
void read_color_index();
void read_dxfs();
void read_fonts();
void read_fills();
void read_borders();
void read_named_styles();
void read_style_names();
void read_cell_styles();
void read_xfs();
void read_style_table();
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_; }
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_color_index();
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<border> borders_;
std::vector<fill> fills_;
std::vector<font> fonts_;
std::vector<number_format> number_formats_;
};
} // namespace xlnt

View File

@ -23,19 +23,16 @@
// @author: see AUTHORS file
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
namespace xlnt {
class relationship;
class workbook;
class worksheet;
class zip_file;
worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats);
void read_worksheet(worksheet ws, std::istream &stream, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats);
void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats);
void read_worksheet(worksheet ws, zip_file &archive, const relationship &rel, const std::vector<std::string> &shared_strings);
std::string read_dimension(const std::string &xml_string);
} // namespace xlnt

View File

@ -23,6 +23,8 @@
// @author: see AUTHORS file
#pragma once
#include <xlnt/common/hash_combine.hpp>
namespace xlnt {
/// <summary>
@ -33,6 +35,7 @@ class alignment
public:
enum class horizontal_alignment
{
none,
general,
left,
right,
@ -43,27 +46,72 @@ public:
enum class vertical_alignment
{
none,
bottom,
top,
center,
justify
};
bool get_wrap_text() const
{
return wrap_text_;
}
void set_wrap_text(bool wrap_text)
{
wrap_text_ = wrap_text;
}
bool has_horizontal() const
{
return horizontal_ != horizontal_alignment::none;
}
horizontal_alignment get_horizontal() const
{
return horizontal_;
}
void set_horizontal(horizontal_alignment horizontal)
{
horizontal_ = horizontal;
}
bool has_vertical() const
{
return vertical_ != vertical_alignment::none;
}
vertical_alignment get_vertical() const
{
return vertical_;
}
void set_vertical(vertical_alignment vertical)
{
vertical_ = vertical;
}
bool operator==(const alignment &other) const
{
return hash() == other.hash();
}
std::size_t hash() const { return 0; }
std::size_t hash() const
{
std::size_t seed = wrap_text_;
seed = seed << 1 & shrink_to_fit_;
hash_combine(seed, static_cast<std::size_t>(horizontal_));
hash_combine(seed, static_cast<std::size_t>(vertical_));
hash_combine(seed, text_rotation_);
hash_combine(seed, indent_);
return seed;
}
private:
horizontal_alignment horizontal_ = horizontal_alignment::general;
vertical_alignment vertical_ = vertical_alignment::bottom;
horizontal_alignment horizontal_ = horizontal_alignment::none;
vertical_alignment vertical_ = vertical_alignment::none;
int text_rotation_ = 0;
bool wrap_text_ = false;
bool shrink_to_fit_ = false;

View File

@ -24,17 +24,12 @@
#pragma once
#include <cstddef>
#include <functional>
#include "side.hpp"
#include "../common/hash_combine.hpp"
namespace xlnt {
template<typename T>
struct optional
{
T value;
bool initialized;
};
enum class diagonal_direction
{
@ -49,28 +44,52 @@ class border
public:
static border default_border();
optional<side> start;
optional<side> end;
optional<side> left;
optional<side> right;
optional<side> top;
optional<side> bottom;
optional<side> diagonal;
optional<side> vertical;
optional<side> horizontal;
bool start_assigned;
side start;
bool end_assigned;
side end;
bool left_assigned;
side left;
bool right_assigned;
side right;
bool top_assigned;
side top;
bool bottom_assigned;
side bottom;
bool diagonal_assigned;
side diagonal;
bool vertical_assigned;
side vertical;
bool horizontal_assigned;
side horizontal;
bool outline;
bool diagonal_up;
bool diagonal_down;
bool outline = false;
bool diagonal_up = false;
bool diagonal_down = false;
diagonal_direction diagonal_direction_;
diagonal_direction diagonal_direction_ = diagonal_direction::none;
bool operator==(const border &other) const
{
return hash() == other.hash();
}
std::size_t hash() const { return 0; }
std::size_t hash() const
{
std::size_t seed = 0;
if(start_assigned) hash_combine(seed, start.hash());
if(end_assigned) hash_combine(seed, end.hash());
if(left_assigned) hash_combine(seed, left.hash());
if(right_assigned) hash_combine(seed, right.hash());
if(top_assigned) hash_combine(seed, top.hash());
if(bottom_assigned) hash_combine(seed, bottom.hash());
if(diagonal_assigned) hash_combine(seed, diagonal.hash());
if(vertical_assigned) hash_combine(seed, vertical.hash());
if(horizontal_assigned) hash_combine(seed, horizontal.hash());
return seed;
}
};
} // namespace xlnt

View File

@ -28,6 +28,13 @@ namespace xlnt {
class color
{
public:
enum class type
{
indexed,
theme,
auto_
};
static const color black;
static const color white;
static const color red;
@ -39,17 +46,74 @@ public:
static const color yellow;
static const color darkyellow;
color(int index) : index_(index)
color() {}
color(type t, std::size_t v) : type_(t), index_(v)
{
}
bool operator==(const color &other) const
{
if(type_ != other.type_) return false;
return index_ == other.index_;
}
void set_auto(int auto_index)
{
type_ = type::auto_;
index_ = auto_index;
}
void set_index(int index)
{
type_ = type::indexed;
index_ = index;
}
void set_theme(int theme)
{
type_ = type::theme;
index_ = theme;
}
type get_type() const
{
return type_;
}
int get_auto() const
{
if(type_ != type::auto_)
{
throw std::runtime_error("not auto color");
}
return index_;
}
int get_index() const
{
if(type_ != type::indexed)
{
throw std::runtime_error("not indexed color");
}
return index_;
}
int get_theme() const
{
if(type_ != type::theme)
{
throw std::runtime_error("not theme color");
}
return index_;
}
private:
int index_;
type type_ = type::indexed;
int index_ = 0;
};
} // namespace xlnt

View File

@ -23,7 +23,8 @@
// @author: see AUTHORS file
#pragma once
#include "color.hpp"
#include "../common/hash_combine.hpp"
#include "../styles/color.hpp"
namespace xlnt {
@ -34,38 +35,139 @@ public:
{
none,
solid,
gradient_linear,
gradient_path,
pattern_darkdown,
pattern_darkgray,
pattern_darkgrid,
pattern_darkhorizontal,
pattern_darktrellis,
pattern_darkup,
pattern_darkvertical,
pattern_gray0625,
pattern_gray125,
pattern_lightdown,
pattern_lightgray,
pattern_lightgrid,
pattern_lighthorizontal,
pattern_lighttrellis,
pattern_lightup,
pattern_lightvertical,
pattern_mediumgray,
gradient,
pattern
};
type type_ = type::none;
int rotation = 0;
color start_color = color::white;
color end_color = color::black;
enum class gradient_type
{
linear,
path
};
virtual bool operator==(const fill &other) const
enum class pattern_type
{
none,
solid,
darkdown,
darkgray,
darkgrid,
darkhorizontal,
darktrellis,
darkup,
darkvertical,
gray0625,
gray125,
lightdown,
lightgray,
lightgrid,
lighthorizontal,
lighttrellis,
lightup,
lightvertical,
mediumgray,
};
type get_type() const { return type_; }
void set_type(type t) { type_ = t; }
std::string get_pattern_type_string() const
{
if(type_ != type::pattern)
{
throw std::runtime_error("not pattern fill");
}
switch(pattern_type_)
{
case pattern_type::none: return "none";
case pattern_type::solid: return "solid";
case pattern_type::darkdown: return "darkdown";
case pattern_type::darkgray: return "darkgray";
case pattern_type::darkgrid: return "darkgrid";
case pattern_type::darkhorizontal: return "darkhorizontal";
case pattern_type::darktrellis: return "darktrellis";
case pattern_type::darkup: return "darkup";
case pattern_type::darkvertical: return "darkvertical";
case pattern_type::gray0625: return "gray0625";
case pattern_type::gray125: return "gray125";
case pattern_type::lightdown: return "lightdown";
case pattern_type::lightgray: return "lightgray";
case pattern_type::lightgrid: return "lightgrid";
case pattern_type::lighthorizontal: return "lighthorizontal";
case pattern_type::lighttrellis: return "lighttrellis";
case pattern_type::lightup: return "lightup";
case pattern_type::lightvertical: return "lightvertical";
case pattern_type::mediumgray: return "mediumgray";
default: throw std::runtime_error("invalid type");
}
}
pattern_type get_pattern_type() const
{
return pattern_type_;
}
void set_pattern_type(pattern_type t) { pattern_type_ = t; }
void set_gradient_type(gradient_type t) { gradient_type_ = t; }
void set_foreground_color(const color &c)
{
foreground_color_ = c;
foreground_color_assigned_ = true;
}
void set_background_color(const color &c)
{
background_color_ = c;
background_color_assigned_ = true;
}
color get_foreground_color() const
{
return foreground_color_;
}
color get_background_color() const
{
return background_color_;
}
bool has_foreground_color() const
{
return foreground_color_assigned_;
}
bool has_background_color() const
{
return background_color_assigned_;
}
bool operator==(const fill &other) const
{
return hash() == other.hash();
}
virtual std::size_t hash() const { return 0; }
std::size_t hash() const
{
auto seed = static_cast<std::size_t>(type_);
if(type_ == type::pattern)
{
hash_combine(seed, static_cast<std::size_t>(pattern_type_));
}
return seed;
}
private:
type type_ = type::none;
pattern_type pattern_type_;
gradient_type gradient_type_;
int rotation_ = 0;
bool foreground_color_assigned_ = false;
color foreground_color_ = color::black;
bool background_color_assigned_ = false;
color background_color_ = color::white;
color start_color_ = color::white;
color end_color_ = color::black;
};
} // namespace xlnt

View File

@ -25,6 +25,7 @@
#include <string>
#include <xlnt/common/hash_combine.hpp>
#include <xlnt/styles/color.hpp>
namespace xlnt {
@ -44,18 +45,51 @@ public:
};
void set_bold(bool bold) { bold_ = bold; }
bool is_bold() const { return bold_; }
void set_size(int size) { size_ = size; }
int get_size() const { return size_; }
void set_name(const std::string &name) { name_ = name; }
std::string get_name() const { return name_; }
void set_color(color c) { color_ = c; }
void set_family(int family) { has_family_ = true; family_ = family; }
void set_scheme(const std::string &scheme) { has_scheme_ = true; scheme_ = scheme; }
color get_color() const { return color_; }
bool has_family() const { return has_family_; }
bool has_scheme() const { return has_scheme_; }
std::size_t hash() const
{
std::size_t seed = bold_;
seed = seed << 1 & italic_;
seed = seed << 1 & superscript_;
seed = seed << 1 & subscript_;
seed = seed << 1 & strikethrough_;
hash_combine(seed, name_);
hash_combine(seed, size_);
hash_combine(seed, static_cast<std::size_t>(underline_));
hash_combine(seed, static_cast<std::size_t>(color_.get_type()));
switch(color_.get_type())
{
case color::type::indexed: hash_combine(seed, color_.get_index()); break;
case color::type::theme: hash_combine(seed, color_.get_theme()); break;
default: break;
}
hash_combine(seed, family_);
hash_combine(seed, scheme_);
return seed;
}
bool operator==(const font &other) const
{
return hash() == other.hash();
}
int get_size() const { return size_; }
std::string get_name() const { return name_; }
bool is_bold() const { return bold_; }
std::size_t hash() const { return 0; }
private:
friend class style;
@ -67,7 +101,11 @@ private:
bool subscript_ = false;
underline underline_ = underline::none;
bool strikethrough_ = false;
color color_ = color::black;
color color_;
bool has_family_ = false;
int family_;
bool has_scheme_ = false;
std::string scheme_;
};
} // namespace xlnt

View File

@ -1,39 +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 "fill.hpp"
namespace xlnt {
class gradient_fill : public fill
{
public:
std::size_t hash() const override { return 0; }
private:
};
} // namespace xlnt

View File

@ -23,68 +23,25 @@
// @author: see AUTHORS file
#pragma once
#include "alignment.hpp"
#include "borders.hpp"
#include "fill.hpp"
#include "font.hpp"
#include "number_format.hpp"
#include "protection.hpp"
#include "style.hpp"
namespace xlnt {
class workbook;
class style
class named_style
{
public:
style();
named_style();
std::size_t hash() const;
const alignment get_alignment() const;
const border get_border() const;
const fill get_fill() const;
const font get_font() const;
const number_format get_number_format() const;
const protection get_protection() const;
bool pivot_button() const;
bool quote_prefix() const;
std::size_t get_fill_index() const { return fill_index_; }
std::size_t get_font_index() const { return font_index_; }
std::size_t get_border_index() const { return border_index_; }
std::size_t get_number_format_index() const { return number_format_index_; }
bool operator==(const style &other) const
bool operator==(const named_style &other) const
{
return hash() == other.hash();
return style_.hash() == other.style_.hash();
}
private:
friend class workbook;
std::size_t style_index_;
std::size_t alignment_index_;
alignment alignment_;
std::size_t border_index_;
border border_;
std::size_t fill_index_;
fill fill_;
std::size_t font_index_;
font font_;
std::size_t number_format_index_;
number_format number_format_;
std::size_t protection_index_;
protection protection_;
bool pivot_button_;
bool quote_prefix_;
std::string name_;
style style_;
};
} // namespace xlnt

View File

@ -32,80 +32,52 @@ namespace xlnt {
class number_format
{
public:
enum class format
{
general,
text,
number,
number_00,
number_comma_separated1,
number_comma_separated2,
percentage,
percentage_00,
date_yyyymmdd2,
date_yyyymmdd,
date_ddmmyyyy,
date_dmyslash,
date_dmyminus,
date_dmminus,
date_myminus,
date_xlsx14,
date_xlsx15,
date_xlsx16,
date_xlsx17,
date_xlsx22,
date_datetime,
date_time1,
date_time2,
date_time3,
date_time4,
date_time5,
date_time6,
date_time7,
date_time8,
date_timedelta,
date_yyyymmddslash,
currency_usd_simple,
currency_usd,
currency_eur_simple,
unknown
};
static const number_format general();
static const number_format text();
static const number_format number();
static const number_format number_00();
static const number_format number_comma_separated1();
static const number_format number_comma_separated2();
static const number_format percentage();
static const number_format percentage_00();
static const number_format date_yyyymmdd2();
static const number_format date_yyyymmdd();
static const number_format date_ddmmyyyy();
static const number_format date_dmyslash();
static const number_format date_dmyminus();
static const number_format date_dmminus();
static const number_format date_myminus();
static const number_format date_xlsx14();
static const number_format date_xlsx15();
static const number_format date_xlsx16();
static const number_format date_xlsx17();
static const number_format date_xlsx22();
static const number_format date_datetime();
static const number_format date_time1();
static const number_format date_time2();
static const number_format date_time3();
static const number_format date_time4();
static const number_format date_time5();
static const number_format date_time6();
static const number_format date_time7();
static const number_format date_time8();
static const number_format date_timedelta();
static const number_format date_yyyymmddslash();
static const number_format currency_usd_simple();
static const number_format currency_usd();
static const number_format currency_eur_simple();
struct format_hash
{
std::size_t operator()(format f) const
{
return std::hash<int>()((int)f);
}
};
static const std::unordered_map<format, std::string, format_hash> &format_strings();
static const std::unordered_map<int, std::string> &builtin_formats();
static const std::unordered_map<std::string, int> &reversed_builtin_formats();
static std::string builtin_format_code(int index);
static format lookup_format(int code);
static bool is_builtin(const std::string &format);
static const number_format &default_number_format()
{
static number_format default_;
return default_;
}
static number_format from_builtin_id(int builtin_id);
number_format();
number_format(format code);
number_format(const std::string &code);
number_format(int builtin_id);
number_format(const std::string &code, int custom_id = -1);
format get_format_code() const;
void set_format_code(format format_code);
void set_format_string(const std::string &format_code);
void set_format_string(const std::string &format_code, int id = -1);
std::string get_format_string() const;
int get_format_index() const { return format_index_; }
void set_id(int id) { id_ = id; }
int get_id() const { return id_; }
std::size_t hash() const;
@ -115,8 +87,7 @@ public:
}
private:
format format_code_;
int format_index_;
int id_;
std::string format_string_;
};

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 "color.hpp"
namespace xlnt {
class pattern_fill : public fill
{
public:
void set_pattern_type(const std::string &type) { type_ = type; }
void set_foreground_color(const std::string &hex) { foreground_color_ = hex; }
std::size_t hash() const override { return 0; }
private:
std::string type_;
std::string foreground_color_;
};
} // namespace xlnt

View File

@ -25,6 +25,8 @@
#include <cstddef>
#include <xlnt/common/hash_combine.hpp>
namespace xlnt {
class protection
@ -40,9 +42,14 @@ public:
protection();
protection(type locked);
void set_locked(bool locked)
void set_locked(type locked_type)
{
locked_ = locked ? type::protected_ : type::unprotected;
locked_ = locked_type;
}
void set_hidden(type hidden_type)
{
hidden_ = hidden_type;
}
bool operator==(const protection &other) const
@ -50,7 +57,13 @@ public:
return hash() == other.hash();
}
std::size_t hash() const { return 0; }
std::size_t hash() const
{
std::size_t seed = static_cast<std::size_t>(locked_);
hash_combine(seed, static_cast<std::size_t>(hidden_));
return seed;
}
private:
type locked_;

View File

@ -25,7 +25,7 @@
#include <cstddef>
#include <xlnt/styles/color.hpp>
#include "../common/hash_combine.hpp"
namespace xlnt {
@ -50,16 +50,81 @@ enum class border_style
class side
{
public:
side(border_style style = border_style::none, color = color::black);
enum class color_type
{
theme,
indexed
};
side();
std::size_t hash() const
{
std::size_t seed = 0;
if(style_assigned_)
{
hash_combine(seed, static_cast<std::size_t>(style_));
}
if(color_assigned_)
{
hash_combine(seed, static_cast<std::size_t>(color_type_));
hash_combine(seed, color_);
}
return seed;
}
border_style get_style() const
{
return style_;
}
color_type get_color_type() const
{
return color_type_;
}
std::size_t get_color() const
{
return color_;
}
bool is_style_assigned() const
{
return style_assigned_;
}
bool is_color_assigned() const
{
return color_assigned_;
}
bool operator==(const side &other) const
{
return other.style_ == style_;
return other.style_ == style_ && color_type_ == other.color_type_ && color_ == other.color_;
}
void set_border_style(border_style bs)
{
style_assigned_ = true;
style_ = bs;
}
void set_color(color_type type, std::size_t color)
{
color_assigned_ = true;
color_type_ = type;
color_ = color;
}
private:
border_style style_;
color color_;
bool style_assigned_ = false;
border_style style_ = border_style::none;
bool color_assigned_ = false;
color_type color_type_ = color_type::indexed;
std::size_t color_ = 0;
};
} // namespace xlnt

View File

@ -38,6 +38,8 @@ class style
{
public:
style();
style(const style &other);
style &operator=(const style &other);
std::size_t hash() const;
@ -50,10 +52,19 @@ public:
bool pivot_button() const;
bool quote_prefix() const;
std::size_t get_fill_index() const { return fill_index_; }
std::size_t get_font_index() const { return font_index_; }
std::size_t get_border_index() const { return border_index_; }
std::size_t get_number_format_index() const { return number_format_index_; }
std::size_t get_id() const { return id_; }
std::size_t get_fill_id() const { return fill_id_; }
std::size_t get_font_id() const { return font_id_; }
std::size_t get_border_id() const { return border_id_; }
std::size_t get_number_format_id() const { return number_format_id_; }
void apply_alignment(bool apply) { alignment_apply_ = apply; }
void apply_border(bool apply) { border_apply_ = apply; }
void apply_fill(bool apply) { fill_apply_ = apply; }
void apply_font(bool apply) { font_apply_ = apply; }
void apply_number_format(bool apply) { number_format_apply_ = apply; }
void apply_protection(bool apply) { protection_apply_ = apply; }
bool operator==(const style &other) const
{
@ -61,26 +72,32 @@ public:
}
private:
friend class style_reader;
friend class style_writer;
friend class workbook;
std::size_t style_index_;
std::size_t id_;
std::size_t alignment_index_;
bool alignment_apply_;
alignment alignment_;
std::size_t border_index_;
bool border_apply_;
std::size_t border_id_;
border border_;
std::size_t fill_index_;
bool fill_apply_;
std::size_t fill_id_;
fill fill_;
std::size_t font_index_;
bool font_apply_;
std::size_t font_id_;
font font_;
std::size_t number_format_index_;
bool number_format_apply_;
std::size_t number_format_id_;
number_format number_format_;
std::size_t protection_index_;
bool protection_apply_;
protection protection_;
bool pivot_button_;

View File

@ -177,7 +177,7 @@ public:
bool save(const std::string &filename);
bool load(const std::vector<unsigned char> &data);
bool load(const std::string &filename);
bool load(const std::istream &stream);
bool load(std::istream &stream);
bool load(zip_file &archive);
bool operator==(const workbook &rhs) const;
@ -200,22 +200,20 @@ public:
relationship get_relationship(const std::string &id) const;
std::vector<relationship> get_relationships() const;
void add_alignment(alignment a);
void add_border(border b);
void add_fill(fill &f);
void add_font(font f);
void add_protection(protection p);
void add_color(color c);
void add_number_format(const std::string &format);
void add_alignment(const alignment &a);
void add_border(const border &b);
void add_fill(const fill &f);
void add_font(const font &f);
void add_color(const color &c);
void add_number_format(const number_format &format);
void add_protection(const protection &p);
std::vector<style> get_styles() const;
std::vector<alignment> get_alignments() const;
std::vector<border> get_borders() const;
std::vector<fill> get_fills() const;
std::vector<font> get_fonts() const;
std::vector<number_format> get_number_formats() const;
std::vector<protection> get_protections() const;
const number_format &get_number_format(std::size_t style_id) const;
std::size_t set_number_format(const number_format &format, std::size_t style_id);
@ -237,8 +235,8 @@ public:
bool has_loaded_theme();
std::string get_loaded_theme();
style get_style(std::size_t style_id);
std::size_t add_style(style style_);
const style &get_style(std::size_t style_id) const;
std::size_t add_style(const style &style_);
private:
friend class worksheet;

View File

@ -23,11 +23,34 @@
// @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

@ -30,29 +30,6 @@
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_string_table(zip_file &archive);
void write_images(zip_file &archive);
void write_charts(zip_file &archive);
void write_chartsheets(zip_file &archive);
void write_worksheets(zip_file &archive);
void write_external_links(zip_file &archive);
private:
workbook wb_;
style_writer style_writer_;
std::vector<std::string> shared_strings_;
};
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);
@ -63,7 +40,4 @@ std::string write_workbook(const workbook &wb);
std::string write_workbook_rels(const workbook &wb);
std::string write_defined_names(const xlnt::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

@ -558,9 +558,6 @@ std::string format_text(const std::string &text, const std::string &format)
namespace xlnt {
const xlnt::color xlnt::color::black(0);
const xlnt::color xlnt::color::white(1);
const std::unordered_map<std::string, int> cell::ErrorCodes =
{
{"#NULL!", 0},
@ -594,7 +591,7 @@ cell::cell(worksheet worksheet, const cell_reference &reference, const T &initia
bool cell::garbage_collectible() const
{
return !(get_data_type() != type::null || is_merged() || has_comment() || has_formula());
return !(get_data_type() != type::null || is_merged() || has_comment() || has_formula() || has_style());
}
@ -737,7 +734,7 @@ void cell::set_value(date d)
{
d_->type_ = type::numeric;
d_->value_numeric_ = d.to_number(get_base_date());
set_number_format(number_format(number_format::format::date_yyyymmdd2));
set_number_format(number_format::date_yyyymmdd2());
}
template<>
@ -745,7 +742,7 @@ void cell::set_value(datetime d)
{
d_->type_ = type::numeric;
d_->value_numeric_ = d.to_number(get_base_date());
set_number_format(number_format(number_format::format::date_datetime));
set_number_format(number_format::date_datetime());
}
template<>
@ -753,7 +750,7 @@ void cell::set_value(time t)
{
d_->type_ = type::numeric;
d_->value_numeric_ = t.to_number();
set_number_format(number_format(number_format::format::date_time6));
set_number_format(number_format::date_time6());
}
template<>
@ -761,7 +758,7 @@ void cell::set_value(timedelta t)
{
d_->type_ = type::numeric;
d_->value_numeric_ = t.to_number();
set_number_format(number_format(number_format::format::date_timedelta));
set_number_format(number_format::date_timedelta());
}
row_t cell::get_row() const
@ -1035,13 +1032,15 @@ std::size_t cell::get_xf_index() const
const number_format &cell::get_number_format() const
{
static const number_format default_format;
if(d_->has_style_)
{
return get_parent().get_parent().get_number_format(d_->style_id_);
}
else if(get_parent().get_parent().get_number_formats().empty())
{
return number_format::default_number_format();
return default_format;
}
else
{
@ -1238,6 +1237,17 @@ std::size_t cell::get_style_id() const
{
return d_->style_id_;
}
bool cell::has_style() const
{
return d_->has_style_;
}
void cell::set_style_id(std::size_t style_id)
{
d_->style_id_ = style_id;
d_->has_style_ = true;
}
calendar cell::get_base_date() const
{

View File

@ -152,7 +152,7 @@ struct cell_impl
{
value_numeric_ = percentage.second;
type_ = cell::type::numeric;
self().set_number_format(xlnt::number_format(xlnt::number_format::format::percentage));
self().set_number_format(xlnt::number_format::percentage());
}
else
{
@ -161,7 +161,7 @@ struct cell_impl
if (time.first)
{
type_ = cell::type::numeric;
self().set_number_format(xlnt::number_format(number_format::format::date_time6));
self().set_number_format(number_format::date_time6());
value_numeric_ = time.second.to_number();
}
else

View File

@ -19,12 +19,10 @@ struct workbook_impl
guess_types_(other.guess_types_),
data_only_(other.data_only_),
styles_(other.styles_),
alignments_(other.alignments_),
borders_(other.borders_),
fills_(other.fills_),
fonts_(other.fonts_),
number_formats_(other.number_formats_),
protections_(other.protections_)
number_formats_(other.number_formats_)
{
}
@ -41,12 +39,10 @@ struct workbook_impl
guess_types_ = other.guess_types_;
data_only_ = other.data_only_;
styles_ = other.styles_;
alignments_ = other.alignments_;
borders_ = other.borders_;
fills_ = other.fills_;
fonts_ = other.fonts_;
number_formats_ = other.number_formats_;
protections_ = other.protections_;
return *this;
}
@ -63,12 +59,12 @@ struct workbook_impl
std::vector<style> styles_;
std::vector<alignment> alignments_;
std::size_t next_custom_format_id_;
std::vector<border> borders_;
std::vector<fill> fills_;
std::vector<font> fonts_;
std::vector<number_format> number_formats_;
std::vector<protection> protections_;
};

View File

@ -1,5 +1,17 @@
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/zip_file.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/worksheet/worksheet.hpp>
#include "detail/include_pugixml.hpp"
namespace {
@ -20,17 +32,92 @@ std::string::size_type find_string_in_string(const std::string &string, const st
return possible_match_index;
}
xlnt::workbook load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only)
{
xlnt::workbook wb;
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
auto content_types = xlnt::read_content_types(archive);
auto type = xlnt::determine_document_type(content_types);
if(type != "excel")
{
throw xlnt::invalid_file_exception("");
}
wb.clear();
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
for(auto relationship : workbook_relationships)
{
wb.create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
}
pugi::xml_document doc;
doc.load(archive.read("xl/workbook.xml").c_str());
auto root_node = doc.child("workbook");
auto workbook_pr_node = root_node.child("workbookPr");
wb.get_properties().excel_base_date = (workbook_pr_node.attribute("date1904") != nullptr && workbook_pr_node.attribute("date1904").as_int() != 0) ? xlnt::calendar::mac_1904 : xlnt::calendar::windows_1900;
auto sheets_node = root_node.child("sheets");
xlnt::shared_strings_reader shared_strings_reader_;
auto shared_strings = shared_strings_reader_.read_strings(archive);
xlnt::style_reader style_reader_(wb);
style_reader_.read_styles(archive);
for(const auto &border_ : style_reader_.get_borders())
{
wb.add_border(border_);
}
for(const auto &fill_ : style_reader_.get_fills())
{
wb.add_fill(fill_);
}
for(const auto &font_ : style_reader_.get_fonts())
{
wb.add_font(font_);
}
for(const auto &number_format_ : style_reader_.get_number_formats())
{
wb.add_number_format(number_format_);
}
for(const auto &style : style_reader_.get_styles())
{
wb.add_style(style);
}
for(auto sheet_node : sheets_node.children("sheet"))
{
auto rel = wb.get_relationship(sheet_node.attribute("r:id").as_string());
auto ws = wb.create_sheet(sheet_node.attribute("name").as_string(), rel);
xlnt::read_worksheet(ws, archive, rel, shared_strings);
}
return wb;
}
} // namespace
namespace xlnt {
std::string CentralDirectorySignature()
std::string excel_reader::CentralDirectorySignature()
{
return "\x50\x4b\x05\x06";
}
std::string repair_central_directory(const std::string &original)
std::string excel_reader::repair_central_directory(const std::string &original)
{
auto pos = find_string_in_string(original, CentralDirectorySignature());
@ -42,26 +129,36 @@ std::string repair_central_directory(const std::string &original)
return original;
}
workbook load_workbook(const std::string &filename, bool guess_types, bool data_only)
workbook excel_reader::load_workbook(std::istream &stream, bool guess_types, bool data_only)
{
workbook wb;
std::vector<std::uint8_t> bytes((std::istream_iterator<char>(stream)),
std::istream_iterator<char>());
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
wb.load(filename);
return wb;
return load_workbook(bytes, guess_types, data_only);
}
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types, bool data_only)
workbook excel_reader::load_workbook(const std::string &filename, bool guess_types, bool data_only)
{
xlnt::workbook wb;
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
wb.load(bytes);
xlnt::zip_file archive;
return wb;
try
{
archive.load(filename);
}
catch(std::runtime_error)
{
throw invalid_file_exception(filename);
}
return ::load_workbook(archive, guess_types, data_only);
}
xlnt::workbook excel_reader::load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types, bool data_only)
{
xlnt::zip_file archive;
archive.load(bytes);
return ::load_workbook(archive, guess_types, data_only);
}
} // namespace xlnt

View File

@ -1,17 +1,26 @@
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/shared_strings_reader.hpp>
#include "detail/include_pugixml.hpp"
namespace xlnt {
std::vector<std::string> read_shared_strings(const std::string &xml_string)
std::vector<std::string> shared_strings_reader::read_strings(zip_file &archive)
{
std::vector<std::string> shared_strings;
static const std::string shared_strings_filename = "xl/sharedStrings.xml";
if(!archive.has_file(shared_strings_filename))
{
return { };
}
pugi::xml_document doc;
doc.load(xml_string.c_str());
doc.load(archive.read(shared_strings_filename).c_str());
auto root_node = doc.child("sst");
//int count = root_node.attribute("count").as_int();
int unique_count = root_node.attribute("uniqueCount").as_int();
std::vector<std::string> shared_strings;
for(auto si_node : root_node)
{

View File

@ -0,0 +1,505 @@
#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_int() != 0);
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_color_index();
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());
nf.set_id(num_fmt_node.attribute("numFmtId").as_int());
number_formats_.push_back(nf);
}
}
void style_reader::read_color_index()
{
}
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());
}
fonts_.push_back(new_font);
}
}
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()));
}
}
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()));
}
}
}
fills_.push_back(new_fill);
}
}
void style_reader::read_borders(pugi::xml_node borders_node)
{
for(auto border_node : borders_node)
{
border new_border;
if(border_node.child("left") != nullptr)
{
new_border.left_assigned = true;
auto left_node = border_node.child("left");
if(left_node.attribute("style") != nullptr)
{
if(left_node.attribute("style").as_string() == std::string("thin"))
{
new_border.left.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = left_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
new_border.left.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
new_border.left.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_node.child("right") != nullptr)
{
new_border.right_assigned = true;
auto right_node = border_node.child("right");
if(right_node.attribute("style") != nullptr)
{
if(right_node.attribute("style").as_string() == std::string("thin"))
{
new_border.right.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = right_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
new_border.right.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
new_border.right.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_node.child("top") != nullptr)
{
new_border.top_assigned = true;
auto top_node = border_node.child("top");
if(top_node.attribute("style") != nullptr)
{
if(top_node.attribute("style").as_string() == std::string("thin"))
{
new_border.top.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = top_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
new_border.top.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
new_border.top.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_node.child("bottom") != nullptr)
{
new_border.bottom_assigned = true;
auto bottom_node = border_node.child("bottom");
if(bottom_node.attribute("style") != nullptr)
{
if(bottom_node.attribute("style").as_string() == std::string("thin"))
{
new_border.bottom.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = bottom_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
new_border.bottom.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
new_border.bottom.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_node.child("diagonal") != nullptr)
{
new_border.diagonal_assigned = true;
auto diagonal_node = border_node.child("diagonal");
if(diagonal_node.attribute("style") != nullptr)
{
if(diagonal_node.attribute("style").as_string() == std::string("thin"))
{
new_border.diagonal.set_border_style(border_style::thin);
}
else
{
throw std::runtime_error("unknown border style");
}
}
auto color_node = diagonal_node.child("color");
if(color_node != nullptr)
{
if(color_node.attribute("indexed") != nullptr)
{
new_border.diagonal.set_color(side::color_type::indexed, color_node.attribute("indexed").as_int());
}
else if(color_node.attribute("theme") != nullptr)
{
new_border.diagonal.set_color(side::color_type::theme, color_node.attribute("theme").as_int());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
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,9 +1,9 @@
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/side.hpp>
namespace xlnt {
side::side(border_style style, color c) : style_(style), color_(c)
side::side()
{
}

View File

@ -1,11 +1,12 @@
#include <algorithm>
#include <regex>
#include <xlnt/common/hash_combine.hpp>
#include <xlnt/styles/number_format.hpp>
namespace xlnt {
const std::unordered_map<int, std::string> &number_format::builtin_formats()
namespace {
const std::unordered_map<int, std::string> &builtin_formats()
{
static const std::unordered_map<int, std::string> formats =
{
@ -32,23 +33,23 @@ const std::unordered_map<int, std::string> &number_format::builtin_formats()
{20, "h:mm"},
{21, "h:mm:ss"},
{22, "m/d/yy h:mm"},
{37, "#,##0_);(#,##0)"},
{38, "#,##0_);[Red](#,##0)"},
{39, "#,##0.00_);(#,##0.00)"},
{40, "#,##0.00_);[Red](#,##0.00)"},
{41, "_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)"},
{42, "_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)"},
{43, "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)"},
{44, "_(\"$\"* #,##0.00_)_(\"$\"* \\(#,##0.00\\)_(\"$\"* \"-\"??_)_(@_)"},
{45, "mm:ss"},
{46, "[h]:mm:ss"},
{47, "mmss.0"},
{48, "##0.0E+0"},
{49, "@"}
//EXCEL differs from the standard in the following:
//{14, "m/d/yyyy"},
//{22, "m/d/yyyy h:mm"},
@ -59,101 +60,240 @@ const std::unordered_map<int, std::string> &number_format::builtin_formats()
//{47, "mm:ss.0"},
//{55, "yyyy/mm/dd"}
};
return formats;
}
const std::unordered_map<number_format::format, std::string, number_format::format_hash> &number_format::format_strings()
{
static const std::unordered_map<number_format::format, std::string, number_format::format_hash> strings =
{
{format::general, builtin_formats().at(0)},
{format::text, builtin_formats().at(49)},
{format::number, builtin_formats().at(1)},
{format::number_00, builtin_formats().at(2)},
{format::number_comma_separated1, builtin_formats().at(4)},
{format::number_comma_separated2, "#,##0.00_-"},
{format::percentage, builtin_formats().at(9)},
{format::percentage_00, builtin_formats().at(10)},
{format::date_yyyymmdd2, "yyyy-mm-dd"},
{format::date_yyyymmdd, "yy-mm-dd"},
{format::date_ddmmyyyy, "dd/mm/yy"},
{format::date_dmyslash, "d/m/y"},
{format::date_dmyminus, "d-m-y"},
{format::date_dmminus, "d-m"},
{format::date_myminus, "m-y"},
{format::date_xlsx14, builtin_formats().at(14)},
{format::date_xlsx15, builtin_formats().at(15)},
{format::date_xlsx16, builtin_formats().at(16)},
{format::date_xlsx17, builtin_formats().at(17)},
{format::date_xlsx22, builtin_formats().at(22)},
{format::date_datetime, "yyyy-mm-dd h:mm:ss"},
{format::date_time1, builtin_formats().at(18)},
{format::date_time2, builtin_formats().at(19)},
{format::date_time3, builtin_formats().at(20)},
{format::date_time4, builtin_formats().at(21)},
{format::date_time5, builtin_formats().at(45)},
{format::date_time6, builtin_formats().at(21)},
{format::date_time7, "i:s.S"},
{format::date_time8, "h:mm:ss@"},
{format::date_timedelta, "[hh]:mm:ss"},
{format::date_yyyymmddslash, "yy/mm/dd@"},
{format::currency_usd_simple, "\"$\"#,##0.00_-"},
{format::currency_usd, "$#,##0_-"},
{format::currency_eur_simple, "[$EUR ]#,##0.00_-"}
};
return strings;
}
const std::unordered_map<std::string, int> &number_format::reversed_builtin_formats()
{
static std::unordered_map<std::string, int> formats;
static bool initialised = false;
if(!initialised)
{
for(auto format_pair : builtin_formats())
{
formats[format_pair.second] = format_pair.first;
}
initialised = true;
}
return formats;
}
} // namespace
number_format::number_format() : number_format(format::general)
namespace xlnt {
const number_format number_format::general()
{
static const number_format format(builtin_formats().at(0), 0);
return format;
}
const number_format number_format::text()
{
static const number_format format(builtin_formats().at(49), 49);
return format;
}
const number_format number_format::number()
{
static const number_format format(builtin_formats().at(1), 1);
return format;
}
const number_format number_format::number_00()
{
static const number_format format(builtin_formats().at(2), 2);
return format;
}
const number_format number_format::number_comma_separated1()
{
static const number_format format(builtin_formats().at(4), 4);
return format;
}
const number_format number_format::number_comma_separated2()
{
static const number_format format("#,##0.00_-");
return format;
}
const number_format number_format::percentage()
{
static const number_format format(builtin_formats().at(9), 9);
return format;
}
const number_format number_format::percentage_00()
{
static const number_format format(builtin_formats().at(10), 10);
return format;
}
const number_format number_format::date_yyyymmdd2()
{
static const number_format format("yyyy-mm-dd");
return format;
}
const number_format number_format::date_yyyymmdd()
{
static const number_format format("yy-mm-dd");
return format;
}
const number_format number_format::date_ddmmyyyy()
{
static const number_format format("dd/mm/yy");
return format;
}
const number_format number_format::date_dmyslash()
{
static const number_format format("d/m/y");
return format;
}
const number_format number_format::date_dmyminus()
{
static const number_format format("d-m-y");
return format;
}
const number_format number_format::date_dmminus()
{
static const number_format format("d-m");
return format;
}
const number_format number_format::date_myminus()
{
static const number_format format("m-y");
return format;
}
const number_format number_format::date_xlsx14()
{
static const number_format format(builtin_formats().at(14), 14);
return format;
}
const number_format number_format::date_xlsx15()
{
static const number_format format(builtin_formats().at(15), 15);
return format;
}
const number_format number_format::date_xlsx16()
{
static const number_format format(builtin_formats().at(16), 16);
return format;
}
const number_format number_format::date_xlsx17()
{
static const number_format format(builtin_formats().at(17), 17);
return format;
}
const number_format number_format::date_xlsx22()
{
static const number_format format(builtin_formats().at(22), 22);
return format;
}
const number_format number_format::date_datetime()
{
static const number_format format("yyyy-mm-dd h:mm:ss");
return format;
}
const number_format number_format::date_time1()
{
static const number_format format(builtin_formats().at(18), 18);
return format;
}
const number_format number_format::date_time2()
{
static const number_format format(builtin_formats().at(19), 19);
return format;
}
const number_format number_format::date_time3()
{
static const number_format format(builtin_formats().at(20), 20);
return format;
}
const number_format number_format::date_time4()
{
static const number_format format(builtin_formats().at(21), 21);
return format;
}
const number_format number_format::date_time5()
{
static const number_format format(builtin_formats().at(45), 45);
return format;
}
const number_format number_format::date_time6()
{
static const number_format format(builtin_formats().at(21), 21);
return format;
}
const number_format number_format::date_time7()
{
static const number_format format("i:s.S");
return format;
}
const number_format number_format::date_time8()
{
static const number_format format("h:mm:ss@");
return format;
}
const number_format number_format::date_timedelta()
{
static const number_format format("[hh]:mm:ss");
return format;
}
const number_format number_format::date_yyyymmddslash()
{
static const number_format format("yy/mm/dd@");
return format;
}
const number_format number_format::currency_usd_simple()
{
static const number_format format("\"$\"#,##0.00_-");
return format;
}
const number_format number_format::currency_usd()
{
static const number_format format("$#,##0_-");
return format;
}
const number_format number_format::currency_eur_simple()
{
static const number_format format("[$EUR ]#,##0.00_-");
return format;
}
number_format::number_format() : number_format(general())
{
}
number_format::number_format(format code) : format_code_(code), format_index_(0)
number_format::number_format(int id) : number_format(from_builtin_id(id))
{
set_format_code(code);
}
number_format::number_format(const std::string &format_string) : number_format(format::general)
number_format::number_format(const std::string &format_string, int id)
{
set_format_string(format_string);
set_format_string(format_string, id);
}
number_format::format number_format::lookup_format(int code)
number_format number_format::from_builtin_id(int builtin_id)
{
if(builtin_formats().find(code) == builtin_formats().end())
if(builtin_formats().find(builtin_id) == builtin_formats().end())
{
return format::unknown;
throw std::runtime_error("unknown id: " + std::to_string(builtin_id));
}
auto format_string = builtin_formats().at(code);
auto match = std::find_if(format_strings().begin(), format_strings().end(), [&](const std::pair<format, std::string> &p) { return p.second == format_string; });
if(match == format_strings().end())
{
return format::unknown;
}
return match->first;
auto format_string = builtin_formats().at(builtin_id);
return number_format(format_string, builtin_id);
}
std::string number_format::get_format_string() const
@ -161,53 +301,30 @@ std::string number_format::get_format_string() const
return format_string_;
}
number_format::format number_format::get_format_code() const
{
return format_code_;
}
std::size_t number_format::hash() const
{
std::hash<std::string> hasher;
return hasher(format_string_);
std::size_t seed = static_cast<std::size_t>(id_);
hash_combine(seed, format_string_);
return seed;
}
void number_format::set_format_string(const std::string &format_string)
void number_format::set_format_string(const std::string &format_string, int id)
{
format_string_ = format_string;
format_index_ = -1;
format_code_ = format::unknown;
id_ = id;
const auto &reversed = reversed_builtin_formats();
if(reversed.find(format_string) != reversed.end())
if(id_ == -1)
{
format_index_ = reversed.at(format_string);
for(const auto &pair : format_strings())
for(const auto &pair : builtin_formats())
{
if(pair.second == format_string)
{
format_code_ = pair.first;
id_ = pair.first;
break;
}
}
}
}
void number_format::set_format_code(xlnt::number_format::format format_code)
{
format_code_ = format_code;
format_string_ = format_strings().at(format_code);
try
{
format_index_ = reversed_builtin_formats().at(format_string_);
}
catch(std::out_of_range)
{
format_index_ = -1;
}
}
} // namespace xlnt

View File

@ -7,7 +7,7 @@ protection::protection() : protection(type::unprotected)
}
protection::protection(type t) : locked_(t), hidden_(type::inherit)
protection::protection(type t) : locked_(t), hidden_(type::unprotected)
{
}

View File

@ -18,28 +18,91 @@ void hash_combine(std::size_t& seed, const T& v)
namespace xlnt {
style::style()
const color color::black = color(color::type::indexed, 0);
const color color::white = color(color::type::indexed, 0);
style::style() :
id_(0),
alignment_apply_(false),
border_apply_(false),
border_id_(0),
fill_apply_(false),
fill_id_(0),
font_apply_(false),
font_id_(0),
number_format_apply_(false),
number_format_id_(0),
protection_apply_(false),
pivot_button_(false),
quote_prefix_(false)
{
}
style::style(const style &other) :
id_(other.id_),
alignment_(other.alignment_),
alignment_apply_(other.alignment_apply_),
border_(other.border_),
border_apply_(other.border_apply_),
border_id_(other.border_id_),
fill_(other.fill_),
fill_apply_(other.fill_apply_),
fill_id_(other.fill_id_),
font_(other.font_),
font_apply_(other.font_apply_),
font_id_(other.font_id_),
number_format_(other.number_format_),
number_format_apply_(other.number_format_apply_),
number_format_id_(other.number_format_id_),
protection_(other.protection_),
protection_apply_(other.protection_apply_),
pivot_button_(other.pivot_button_),
quote_prefix_(other.quote_prefix_)
{
}
style &style::operator=(const style &other)
{
id_ = other.id_;
alignment_ = other.alignment_;
alignment_apply_ = other.alignment_apply_;
border_ = other.border_;
border_apply_ = other.border_apply_;
border_id_ = other.border_id_;
fill_ = other.fill_;
fill_apply_ = other.fill_apply_;
fill_id_ = other.fill_id_;
font_ = other.font_;
font_apply_ = other.font_apply_;
font_id_ = other.font_id_;
number_format_ = other.number_format_;
number_format_apply_ = other.number_format_apply_;
number_format_id_ = other.number_format_id_;
protection_ = other.protection_;
protection_apply_ = other.protection_apply_;
pivot_button_ = other.pivot_button_;
quote_prefix_ = other.quote_prefix_;
return *this;
}
std::size_t style::hash() const
{
/*
std::size_t seed = 100;
std::hash<std::string> string_hash;
auto font_ = get_font();
hash_combine(seed, string_hash(font_.name_));
hash_combine(seed, font_.size_);
hash_combine(seed, static_cast<std::size_t>(font_.underline_));
std::size_t bools = font_.bold_;
bools = bools << 1 & font_.italic_;
bools = bools << 1 & font_.strikethrough_;
bools = bools << 1 & font_.superscript_;
bools = bools << 1 & font_.subscript_;
hash_combine(seed, bools);
*/
std::size_t seed = 100;
hash_combine(seed, number_format_.hash());
std::size_t seed = 0;
if(alignment_apply_) hash_combine(seed, alignment_apply_);
hash_combine(seed, alignment_.hash());
hash_combine(seed, border_apply_);
hash_combine(seed, border_id_);
hash_combine(seed, font_apply_);
hash_combine(seed, font_id_);
hash_combine(seed, fill_apply_);
hash_combine(seed, fill_id_);
hash_combine(seed, number_format_apply_);
hash_combine(seed, number_format_id_);
hash_combine(seed, protection_apply_);
if(protection_apply_) hash_combine(seed, get_protection().hash());
// hash_combine(seed, pivot_button_);
// hash_combine(seed, quote_prefix_);
return seed;
}

View File

@ -12,9 +12,7 @@
#include <xlnt/common/relationship.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/drawing/drawing.hpp>
#include <xlnt/reader/shared_strings_reader.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include <xlnt/reader/excel_reader.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
@ -26,10 +24,7 @@
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/writer/style_writer.hpp>
#include <xlnt/writer/manifest_writer.hpp>
#include <xlnt/writer/worksheet_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include <xlnt/writer/excel_writer.hpp>
#include "detail/cell_impl.hpp"
#include "detail/include_pugixml.hpp"
@ -70,7 +65,7 @@ void hash_combine(std::size_t& seed, const T& v)
namespace xlnt {
namespace detail {
workbook_impl::workbook_impl() : active_sheet_index_(0), guess_types_(false), data_only_(false)
workbook_impl::workbook_impl() : active_sheet_index_(0), guess_types_(false), data_only_(false), next_custom_format_id_(164)
{
}
@ -277,122 +272,24 @@ range workbook::get_named_range(const std::string &name)
throw std::runtime_error("named range not found");
}
bool workbook::load(const std::istream &stream)
bool workbook::load(std::istream &stream)
{
std::string temp_file = create_temporary_filename();
std::ofstream tmp;
tmp.open(temp_file, std::ios::out | std::ios::binary);
tmp << stream.rdbuf();
tmp.close();
load(temp_file);
std::remove(temp_file.c_str());
*this = excel_reader::load_workbook(stream);
return true;
}
bool workbook::load(const std::vector<unsigned char> &data)
{
xlnt::zip_file archive;
archive.load(data);
return load(archive);
*this = excel_reader::load_workbook(data);
return true;
}
bool workbook::load(const std::string &filename)
{
zip_file f;
try
{
f.load(filename);
}
catch(std::exception e)
{
throw invalid_file_exception(filename);
}
*this = excel_reader::load_workbook(filename);
return load(f);
}
bool workbook::load(xlnt::zip_file &archive)
{
auto content_types = read_content_types(archive);
auto type = determine_document_type(content_types);
if(type != "excel")
{
throw invalid_file_exception("");
}
clear();
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
for(auto relationship : workbook_relationships)
{
create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
}
pugi::xml_document doc;
doc.load(archive.read("xl/workbook.xml").c_str());
auto root_node = doc.child("workbook");
auto workbook_pr_node = root_node.child("workbookPr");
get_properties().excel_base_date = (workbook_pr_node.attribute("date1904") != nullptr && workbook_pr_node.attribute("date1904").as_int() != 0) ? calendar::mac_1904 : calendar::windows_1900;
auto sheets_node = root_node.child("sheets");
std::vector<std::string> shared_strings;
if(archive.has_file("xl/sharedStrings.xml"))
{
shared_strings = read_shared_strings(archive.read("xl/sharedStrings.xml"));
}
std::vector<int> number_format_ids;
std::unordered_map<int, std::string> custom_number_formats;
if(archive.has_file("xl/styles.xml"))
{
pugi::xml_document styles_doc;
styles_doc.load(archive.read("xl/styles.xml").c_str());
auto stylesheet_node = styles_doc.child("styleSheet");
auto cell_xfs_node = stylesheet_node.child("cellXfs");
for(auto xf_node : cell_xfs_node.children("xf"))
{
number_format_ids.push_back(xf_node.attribute("numFmtId").as_int());
}
auto num_fmts_node = stylesheet_node.child("numFmts");
for(auto num_fmt_node : num_fmts_node.children("numFmt"))
{
custom_number_formats[num_fmt_node.attribute("numFmtId").as_int()] = num_fmt_node.attribute("formatCode").as_string();
}
}
for(auto sheet_node : sheets_node.children("sheet"))
{
std::string rel_id = sheet_node.attribute("r:id").as_string();
auto rel = std::find_if(d_->relationships_.begin(), d_->relationships_.end(),
[&](relationship &r) { return r.get_id() == rel_id; });
if (rel == d_->relationships_.end())
{
throw std::runtime_error("relationship not found");
}
auto ws = create_sheet(sheet_node.attribute("name").as_string(), *rel);
auto sheet_filename = rel->get_target_uri();
read_worksheet(ws, archive.read(sheet_filename).c_str(), shared_strings, number_format_ids, custom_number_formats);
}
return true;
}
@ -671,36 +568,26 @@ void workbook::set_data_only(bool data_only)
d_->data_only_ = data_only;
}
void workbook::add_border(xlnt::border /*b*/)
void workbook::add_border(const xlnt::border &border_)
{
d_->borders_.push_back(border_);
}
void workbook::add_alignment(xlnt::alignment /*a*/)
void workbook::add_fill(const fill &fill_)
{
d_->fills_.push_back(fill_);
}
void workbook::add_protection(xlnt::protection /*p*/)
void workbook::add_font(const font &font_)
{
d_->fonts_.push_back(font_);
}
void workbook::add_number_format(const std::string &/*format*/)
void workbook::add_number_format(const number_format &number_format_)
{
d_->number_formats_.push_back(number_format_);
}
void workbook::add_fill(xlnt::fill &/*f*/)
{
}
void workbook::add_font(xlnt::font /*f*/)
{
}
void workbook::set_code_name(const std::string &/*code_name*/)
{
@ -731,19 +618,42 @@ std::vector<named_range> workbook::get_named_ranges() const
return named_ranges;
}
std::size_t workbook::add_style(xlnt::style style_)
std::size_t workbook::add_style(const xlnt::style &style_)
{
return 1;
for(std::size_t i = 0; i < d_->styles_.size(); i++)
{
if(d_->styles_[i] == style_)
{
return i;
}
}
d_->styles_.push_back(style_);
return d_->styles_.size() - 1;
}
const number_format &workbook::get_number_format(std::size_t style_id) const
{
return d_->number_formats_[d_->styles_[style_id].number_format_index_];
auto number_format_id = d_->styles_[style_id].number_format_id_;
for(const auto &number_format_ : d_->number_formats_)
{
if(number_format_.get_id() == number_format_id)
{
return number_format_;
}
}
auto nf = number_format::from_builtin_id(static_cast<int>(number_format_id));
d_->number_formats_.push_back(nf);
return d_->number_formats_.back();
}
const font &workbook::get_font(std::size_t style_id) const
{
return d_->fonts_[d_->styles_[style_id].font_index_];
return d_->fonts_[d_->styles_[style_id].font_id_];
}
std::size_t workbook::set_font(const font &font_, std::size_t style_id)
@ -763,14 +673,14 @@ std::size_t workbook::set_font(const font &font_, std::size_t style_id)
auto existing_style = d_->styles_[style_id];
if(font_index == existing_style.font_index_)
if(font_index == existing_style.font_id_)
{
// no change
return style_id;
}
auto new_style = existing_style;
new_style.font_index_ = font_index;
new_style.font_id_ = font_index;
auto style_match = std::find(d_->styles_.begin(), d_->styles_.end(), new_style);
@ -786,7 +696,7 @@ std::size_t workbook::set_font(const font &font_, std::size_t style_id)
const fill &workbook::get_fill(std::size_t style_id) const
{
return d_->fills_[d_->styles_[style_id].fill_index_];
return d_->fills_[d_->styles_[style_id].fill_id_];
}
std::size_t workbook::set_fill(const fill &fill_, std::size_t style_id)
@ -796,7 +706,7 @@ std::size_t workbook::set_fill(const fill &fill_, std::size_t style_id)
const border &workbook::get_border(std::size_t style_id) const
{
return d_->borders_[d_->styles_[style_id].border_index_];
return d_->borders_[d_->styles_[style_id].border_id_];
}
std::size_t workbook::set_border(const border &border_, std::size_t style_id)
@ -806,7 +716,7 @@ std::size_t workbook::set_border(const border &border_, std::size_t style_id)
const alignment &workbook::get_alignment(std::size_t style_id) const
{
return d_->alignments_[d_->styles_[style_id].alignment_index_];
return d_->styles_[style_id].alignment_;
}
std::size_t workbook::set_alignment(const alignment &alignment_, std::size_t style_id)
@ -816,7 +726,7 @@ std::size_t workbook::set_alignment(const alignment &alignment_, std::size_t sty
const protection &workbook::get_protection(std::size_t style_id) const
{
return d_->protections_[d_->styles_[style_id].number_format_index_];
return d_->styles_[style_id].protection_;
}
std::size_t workbook::set_protection(const protection &protection_, std::size_t style_id)
@ -837,54 +747,84 @@ bool workbook::get_quote_prefix(std::size_t style_id) const
std::size_t workbook::set_number_format(const xlnt::number_format &format, std::size_t style_id)
{
auto match = std::find(d_->number_formats_.begin(), d_->number_formats_.end(), format);
auto format_index = 0;
std::size_t format_id = 0;
if(match == d_->number_formats_.end())
{
d_->number_formats_.push_back(format);
format_index = d_->number_formats_.size() - 1;
if(format.get_id() == -1)
{
d_->number_formats_.back().set_id(d_->next_custom_format_id_++);
}
format_id = d_->number_formats_.back().get_id();
}
else
{
format_index = match - d_->number_formats_.begin();
format_id = match->get_id();
}
if(d_->styles_.empty())
{
style new_style;
new_style.style_index_ = 0;
new_style.alignment_index_ = 0;
new_style.border_index_ = 0;
new_style.fill_index_ = 0;
new_style.font_index_ = 0;
new_style.number_format_index_ = format_index;
new_style.protection_index_ = 0;
new_style.id_ = 0;
new_style.border_id_ = 0;
new_style.fill_id_ = 0;
new_style.font_id_ = 0;
new_style.number_format_id_ = format_id;
new_style.number_format_apply_ = true;
if(d_->borders_.empty())
{
d_->borders_.push_back(new_style.get_border());
}
if(d_->fills_.empty())
{
d_->fills_.push_back(new_style.get_fill());
}
if(d_->fonts_.empty())
{
d_->fonts_.push_back(new_style.get_font());
}
d_->styles_.push_back(new_style);
return 0;
}
// If the style is unchanged, just return it.
auto existing_style = d_->styles_[style_id];
existing_style.number_format_apply_ = true;
if(format_index == existing_style.number_format_index_)
if(format_id == existing_style.number_format_id_)
{
// no change
return style_id;
}
// Make a new style with this format.
auto new_style = existing_style;
new_style.number_format_index_ = format_index;
new_style.number_format_id_ = format_id;
new_style.number_format_ = format;
// Check if the new style is already applied to a different cell. If so, reuse it.
auto style_match = std::find(d_->styles_.begin(), d_->styles_.end(), new_style);
if(style_match != d_->styles_.end())
{
return style_match - d_->styles_.begin();
return style_match->get_id();
}
// No match found, so add it.
new_style.id_ = d_->styles_.size();
d_->styles_.push_back(new_style);
return d_->styles_.size() - 1;
return new_style.id_;
}
std::vector<style> workbook::get_styles() const
@ -901,5 +841,15 @@ std::vector<font> workbook::get_fonts() const
{
return d_->fonts_;
}
std::vector<border> workbook::get_borders() const
{
return d_->borders_;
}
std::vector<fill> workbook::get_fills() const
{
return d_->fills_;
}
} // namespace xlnt

View File

@ -1,5 +1,6 @@
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/workbook/workbook.hpp>
@ -10,8 +11,17 @@
namespace {
void read_worksheet_common(xlnt::worksheet ws, const pugi::xml_node &root_node, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
} // namespace
namespace xlnt {
void read_worksheet(worksheet ws, zip_file &archive, const relationship &rel, const std::vector<std::string> &string_table)
{
pugi::xml_document doc;
doc.load(archive.read(rel.get_target_uri()).c_str());
auto root_node = doc.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
auto full_range = xlnt::range_reference(dimension);
@ -62,85 +72,59 @@ void read_worksheet_common(xlnt::worksheet ws, const pugi::xml_node &root_node,
if(cell_node != nullptr)
{
bool has_value = cell_node.child("v") != nullptr;
std::string value_string = cell_node.child("v").text().as_string();
std::string value_string = has_value ? cell_node.child("v").text().as_string() : "";
bool has_type = cell_node.attribute("t") != nullptr;
std::string type = cell_node.attribute("t").as_string();
std::string type = has_type ? cell_node.attribute("t").as_string() : "";
bool has_style = cell_node.attribute("s") != nullptr;
std::string style = cell_node.attribute("s").as_string();
int style_id = has_style ? cell_node.attribute("s").as_int() : 0;
bool has_formula = cell_node.child("f") != nullptr;
bool shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && std::string(cell_node.child("f").attribute("t").as_string()) == "shared";
bool has_shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && std::string(cell_node.child("f").attribute("t").as_string()) == "shared";
if(has_formula && !shared_formula && !ws.get_parent().get_data_only())
auto cell = ws.get_cell(address);
if(has_formula && !has_shared_formula && !ws.get_parent().get_data_only())
{
std::string formula = cell_node.child("f").text().as_string();
ws.get_cell(address).set_formula(formula);
cell.set_formula(formula);
}
if(has_type && type == "inlineStr") // inline string
{
std::string inline_string = cell_node.child("is").child("t").text().as_string();
ws.get_cell(address).set_value(inline_string);
cell.set_value(inline_string);
}
else if(has_type && type == "s" && !has_formula) // shared string
{
auto shared_string_index = std::stoll(value_string);
auto shared_string = string_table.at(static_cast<std::size_t>(shared_string_index));
ws.get_cell(address).set_value(shared_string);
auto shared_string_index = std::stoull(value_string);
auto shared_string = string_table.at(shared_string_index);
cell.set_value(shared_string);
}
else if(has_type && type == "b") // boolean
{
ws.get_cell(address).set_value(value_string != "0");
cell.set_value(value_string != "0");
}
else if(has_type && type == "str")
{
ws.get_cell(address).set_value(value_string);
cell.set_value(value_string);
}
else if(has_value)
{
try
if(!value_string.empty() && value_string[0] == '#')
{
ws.get_cell(address).set_value(std::stold(value_string));
cell.set_error(value_string);
}
catch(std::invalid_argument)
else
{
ws.get_cell(address).set_value(value_string);
cell.set_value(std::stold(value_string));
}
}
if(has_style)
{
auto style_index = static_cast<std::size_t>(std::stoll(style));
if(number_format_ids.size() > style_index)
{
auto number_format_id = number_format_ids.at(style_index);
auto format = xlnt::number_format::lookup_format(number_format_id);
if(format == xlnt::number_format::format::unknown)
{
auto match = custom_number_formats.find(number_format_id);
if(match != custom_number_formats.end())
{
xlnt::number_format nf(match->second);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
else
{
xlnt::number_format nf(format);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
}
else
{
ws.get_cell(address).set_number_format(xlnt::number_format(xlnt::number_format::format::general));
cell.set_style_id(style_id);
}
}
}
@ -155,32 +139,4 @@ void read_worksheet_common(xlnt::worksheet ws, const pugi::xml_node &root_node,
}
}
} // namespace
namespace xlnt {
void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
read_worksheet_common(ws, doc.child("worksheet"), string_table, number_format_ids, custom_number_formats);
}
void read_worksheet(worksheet ws, std::istream &stream, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
pugi::xml_document doc;
doc.load(stream);
read_worksheet_common(ws, doc.child("worksheet"), string_table, number_format_ids, custom_number_formats);
}
worksheet read_worksheet(std::istream &handle, xlnt::workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats)
{
auto ws = wb.create_sheet();
ws.set_title(title);
pugi::xml_document doc;
doc.load(handle);
read_worksheet_common(ws, doc.child("worksheet"), string_table, {}, custom_number_formats);
return ws;
}
} // namespace xlnt

View File

@ -0,0 +1,145 @@
#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)
{
for(auto relationship : wb_.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet)
{
auto sheet_index = workbook::index_from_ws_filename(relationship.get_target_uri());
auto ws = wb_.get_sheet_by_index(sheet_index);
archive.writestr(relationship.get_target_uri(), write_worksheet(ws, shared_strings));
}
}
}
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

@ -33,21 +33,10 @@ std::string style_writer::write_table() const
auto num_fmts = wb_.get_number_formats();
num_fmts_node.append_attribute("count").set_value(static_cast<int>(num_fmts.size()));
int custom_index = 164;
for(auto &num_fmt : num_fmts)
{
auto num_fmt_node = num_fmts_node.append_child("numFmt");
if(num_fmt.get_format_code() == number_format::format::unknown)
{
num_fmt_node.append_attribute("numFmtId").set_value(custom_index++);
}
else
{
num_fmt_node.append_attribute("numFmtId").set_value(num_fmt.get_format_index());
}
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());
}
@ -63,16 +52,35 @@ std::string style_writer::write_table() const
for(auto &f : fonts)
{
auto font_node = fonts_node.append_child("font");
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");
color_node.append_attribute("theme").set_value(1);
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());
auto family_node = font_node.append_child("family");
family_node.append_attribute("val").set_value(2);
auto scheme_node = font_node.append_child("scheme");
scheme_node.append_attribute("val").set_value("minor");
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");
}
if(f.is_bold())
{
@ -82,52 +90,322 @@ std::string style_writer::write_table() const
}
auto fills_node = style_sheet_node.append_child("fills");
fills_node.append_attribute("count").set_value(2);
fills_node.append_child("fill").append_child("patternFill").append_attribute("patternType").set_value("none");
fills_node.append_child("fill").append_child("patternFill").append_attribute("patternType").set_value("gray125");
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");
}
}
}
}
auto borders_node = style_sheet_node.append_child("borders");
borders_node.append_attribute("count").set_value(1);
auto border_node = borders_node.append_child("border");
border_node.append_child("left");
border_node.append_child("right");
border_node.append_child("top");
border_node.append_child("bottom");
border_node.append_child("diagonal");
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");
if(border_.left_assigned)
{
auto left_node = border_node.append_child("left");
if(border_.left.is_style_assigned())
{
auto style_attribute = left_node.append_attribute("style");
switch(border_.left.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(border_.left.is_color_assigned())
{
auto color_node = left_node.append_child("color");
if(border_.left.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)border_.left.get_color());
}
else if(border_.diagonal.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)border_.left.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_.right_assigned)
{
auto right_node = border_node.append_child("right");
if(border_.right.is_style_assigned())
{
auto style_attribute = right_node.append_attribute("style");
switch(border_.right.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(border_.right.is_color_assigned())
{
auto color_node = right_node.append_child("color");
if(border_.right.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)border_.right.get_color());
}
else if(border_.diagonal.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)border_.right.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_.top_assigned)
{
auto top_node = border_node.append_child("top");
if(border_.top.is_style_assigned())
{
auto style_attribute = top_node.append_attribute("style");
switch(border_.top.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(border_.top.is_color_assigned())
{
auto color_node = top_node.append_child("color");
if(border_.top.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)border_.top.get_color());
}
else if(border_.diagonal.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)border_.top.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_.bottom_assigned)
{
auto bottom_node = border_node.append_child("bottom");
if(border_.bottom.is_style_assigned())
{
auto style_attribute = bottom_node.append_attribute("style");
switch(border_.bottom.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(border_.bottom.is_color_assigned())
{
auto color_node = bottom_node.append_child("color");
if(border_.bottom.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)border_.bottom.get_color());
}
else if(border_.diagonal.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)border_.bottom.get_color());
}
else
{
throw std::runtime_error("invalid color type");
}
}
}
if(border_.diagonal_assigned)
{
auto diagonal_node = border_node.append_child("diagonal");
if(border_.diagonal.is_style_assigned())
{
auto style_attribute = diagonal_node.append_attribute("style");
switch(border_.diagonal.get_style())
{
case border_style::none: style_attribute.set_value("none"); break;
case border_style::thin: style_attribute.set_value("thin"); break;
default: throw std::runtime_error("invalid style");
}
}
if(border_.diagonal.is_color_assigned())
{
auto color_node = diagonal_node.append_child("color");
if(border_.diagonal.get_color_type() == side::color_type::indexed)
{
color_node.append_attribute("indexed").set_value((int)border_.diagonal.get_color());
}
else if(border_.diagonal.get_color_type() == side::color_type::theme)
{
color_node.append_attribute("indexed").set_value((int)border_.diagonal.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 xf_node = cell_style_xfs_node.append_child("xf");
xf_node.append_attribute("numFmtId").set_value(0);
xf_node.append_attribute("fontId").set_value(0);
xf_node.append_attribute("fillId").set_value(0);
xf_node.append_attribute("borderId").set_value(0);
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()));
custom_index = 164;
for(auto &style : styles)
{
xf_node = cell_xfs_node.append_child("xf");
if(style.get_number_format().get_format_code() == number_format::format::unknown)
{
xf_node.append_attribute("numFmtId").set_value(custom_index++);
}
else
{
xf_node.append_attribute("numFmtId").set_value(style.get_number_format().get_format_index());
}
xf_node.append_attribute("applyNumberFormat").set_value(1);
xf_node.append_attribute("fontId").set_value((int)style.get_font_index());
xf_node.append_attribute("fillId").set_value((int)style.get_fill_index());
xf_node.append_attribute("borderId").set_value((int)style.get_border_index());
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());
auto alignment_node = xf_node.append_child("alignment");
alignment_node.append_attribute("vertical").set_value("top");
alignment_node.append_attribute("wrapText").set_value(1);
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");
@ -143,6 +421,30 @@ std::string style_writer::write_table() const
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");
const std::vector<std::string> colors =
{
"ff000000",
"ffffffff",
"ffff0000",
"ff00ff00",
"ff0000ff",
"ffffff00",
"ffff00ff",
"ff00ffff",
"ff000000",
"ffaaaaaa",
"ffbdc0bf",
"ffdbdbdb"
};
for(auto &color : colors)
{
indexed_colors_node.append_child("rgbColor").append_attribute("rgb").set_value(color.c_str());
}
auto ext_list_node = style_sheet_node.append_child("extLst");
auto ext_node = ext_list_node.append_child("ext");

View File

@ -41,121 +41,6 @@ std::string datetime_to_w3cdtf(const xlnt::datetime &dt)
namespace xlnt {
excel_writer::excel_writer(workbook &wb) : wb_(wb), style_writer_(wb_)
{
}
void excel_writer::save(const std::string &filename, bool as_template)
{
zip_file archive;
write_data(archive, as_template);
archive.save(filename);
}
void excel_writer::write_data(zip_file &archive, bool as_template)
{
archive.writestr(constants::ArcRootRels, write_root_rels(wb_));
archive.writestr(constants::ArcWorkbookRels, write_workbook_rels(wb_));
archive.writestr(constants::ArcApp, write_properties_app(wb_));
archive.writestr(constants::ArcCore, 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_));
/*
if(wb_.has_vba_archive())
{
auto &vba_archive = wb_.get_vba_archive();
for(auto name : vba_archive.namelist())
{
for(auto s : constants::ArcVba)
{
if(match(s, name))
{
archive.writestr(name, vba_archive.read(name));
break;
}
}
}
}
*/
write_charts(archive);
write_images(archive);
write_string_table(archive);
write_worksheets(archive);
write_chartsheets(archive);
write_external_links(archive);
archive.writestr(constants::ArcStyles, style_writer_.write_table());
auto manifest = write_content_types(wb_, as_template);
archive.writestr(constants::ArcContentTypes, manifest);
}
void excel_writer::write_string_table(zip_file &archive)
{
std::set<std::string> shared_strings_set;
for(auto ws : wb_)
{
for(auto row : ws.rows())
{
for(auto cell : row)
{
if(cell.get_data_type() == cell::type::string)
{
shared_strings_set.insert(cell.get_value<std::string>());
}
}
}
}
shared_strings_.assign(shared_strings_set.begin(), shared_strings_set.end());
archive.writestr(constants::ArcSharedString, 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)
{
for(auto relationship : wb_.get_relationships())
{
if(relationship.get_type() == relationship::type::worksheet)
{
auto sheet_index = workbook::index_from_ws_filename(relationship.get_target_uri());
auto ws = wb_.get_sheet_by_index(sheet_index);
archive.writestr(relationship.get_target_uri(), write_worksheet(ws, shared_strings_));
}
}
}
void excel_writer::write_external_links(zip_file &/*archive*/)
{
}
std::string write_shared_strings(const std::vector<std::string> &string_table)
{
pugi::xml_document doc;
@ -713,22 +598,4 @@ std::string write_defined_names(const xlnt::workbook &wb)
return stream.str();
}
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

@ -264,8 +264,11 @@ std::string write_worksheet(worksheet ws, const std::vector<std::string> &string
}
}
auto style_id = cell.get_style_id();
cell_node.append_attribute("s").set_value((int)style_id);
//if(cell.has_style())
{
auto style_id = cell.get_style_id();
cell_node.append_attribute("s").set_value((int)style_id);
}
}
}
}

View File

@ -9,18 +9,19 @@
#include <xlnt/cell/comment.hpp>
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/pattern_fill.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/protection.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/writer/excel_writer.hpp>
#include <xlnt/reader/excel_reader.hpp>
class test_cell : public CxxTest::TestSuite
{
private:
@ -32,6 +33,13 @@ public:
wb_guess_types.set_guess_types(true);
}
void test_debug()
{
xlnt::workbook wb;
wb.load("/Users/thomas/Development/xlnt/samples/sample1.xlsx");
wb.save("/Users/thomas/Development/xlnt/bin/sample1-out.xlsx");
}
void test_infer_numeric()
{
auto ws = wb_guess_types.create_sheet();
@ -394,14 +402,14 @@ public:
void _test_fill()
{
xlnt::pattern_fill fill;
fill.set_pattern_type("solid");
fill.set_foreground_color("FF0000");
xlnt::fill f;
f.set_type(xlnt::fill::type::solid);
// f.set_foreground_color("FF0000");
auto ws = wb.create_sheet();
ws.get_parent().add_fill(fill);
ws.get_parent().add_fill(f);
xlnt::cell cell(ws, "A1");
TS_ASSERT(cell.get_fill() == fill);
TS_ASSERT(cell.get_fill() == f);
}
void _test_border()
@ -417,7 +425,7 @@ public:
void _test_number_format()
{
auto ws = wb.create_sheet();
ws.get_parent().add_number_format("dd--hh--mm");
ws.get_parent().add_number_format(xlnt::number_format("dd--hh--mm"));
xlnt::cell cell(ws, "A1");
cell.set_number_format(xlnt::number_format("dd--hh--mm"));
@ -439,7 +447,7 @@ public:
void _test_protection()
{
xlnt::protection prot;
prot.set_locked(false);
prot.set_locked(xlnt::protection::type::protected_);
auto ws = wb.create_sheet();
ws.get_parent().add_protection(prot);

View File

@ -14,28 +14,11 @@
class test_read : public CxxTest::TestSuite
{
public:
void test_read_standalone_worksheet()
{
auto path = PathHelper::GetDataDirectory("/reader/sheet2.xml");
xlnt::workbook wb;
xlnt::worksheet ws(wb);
{
std::ifstream handle(path);
ws = xlnt::read_worksheet(handle, wb, "Sheet 2", {"hello"}, {});
}
TS_ASSERT_DIFFERS(ws, nullptr);
if(!(ws == nullptr))
{
TS_ASSERT_EQUALS(ws.get_cell("G5").get_value<std::string>(), "hello");
TS_ASSERT_EQUALS(ws.get_cell("D30").get_value<int>(), 30);
TS_ASSERT_EQUALS(ws.get_cell("K9").get_value<double>(), 0.09);
}
}
xlnt::workbook standard_workbook()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty.xlsx");
return xlnt::load_workbook(path);
return xlnt::excel_reader::load_workbook(path);
}
void test_read_standard_workbook()
@ -47,7 +30,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/genuine/empty.xlsx");
std::ifstream fo(path, std::ios::binary);
auto wb = xlnt::load_workbook(path);
auto wb = xlnt::excel_reader::load_workbook(path);
TS_ASSERT_DIFFERS(standard_workbook(), nullptr);
}
@ -65,58 +48,58 @@ public:
void test_read_nostring_workbook()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty-no-string.xlsx");
auto wb = xlnt::load_workbook(path);
auto wb = xlnt::excel_reader::load_workbook(path);
TS_ASSERT_DIFFERS(standard_workbook(), nullptr);
}
void test_read_empty_file()
{
auto path = PathHelper::GetDataDirectory("/reader/null_file.xlsx");
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::excel_reader::load_workbook(path), xlnt::invalid_file_exception);
}
void test_read_empty_archive()
{
auto path = PathHelper::GetDataDirectory("/reader/null_archive.xlsx");
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::excel_reader::load_workbook(path), xlnt::invalid_file_exception);
}
void test_read_workbook_with_no_properties()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty_with_no_properties.xlsx");
xlnt::load_workbook(path);
xlnt::excel_reader::load_workbook(path);
}
xlnt::workbook workbook_with_styles()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty-with-styles.xlsx");
return xlnt::load_workbook(path);
return xlnt::excel_reader::load_workbook(path);
}
void test_read_workbook_with_styles_general()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A1").get_number_format().get_format_code();
auto expected = xlnt::number_format::format::general;
TS_ASSERT_EQUALS(code, expected);
auto format = ws.get_cell("A1").get_number_format();
auto expected = xlnt::number_format::general();
TS_ASSERT_EQUALS(format, expected);
}
void test_read_workbook_with_styles_date()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A2").get_number_format().get_format_code();
auto expected = xlnt::number_format::format::date_xlsx14;
TS_ASSERT_EQUALS(code, expected);
auto format = ws.get_cell("A2").get_number_format();
auto expected = xlnt::number_format::date_xlsx14();
TS_ASSERT_EQUALS(format, expected);
}
void test_read_workbook_with_styles_number()
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A3").get_number_format().get_format_code();
auto expected = xlnt::number_format::format::number_00;
auto code = ws.get_cell("A3").get_number_format();
auto expected = xlnt::number_format::number_00();
TS_ASSERT_EQUALS(code, expected);
}
@ -124,8 +107,8 @@ public:
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A4").get_number_format().get_format_code();
auto expected = xlnt::number_format::format::date_time3;
auto code = ws.get_cell("A4").get_number_format();
auto expected = xlnt::number_format::date_time3();
TS_ASSERT_EQUALS(code, expected);
}
@ -133,21 +116,21 @@ public:
{
auto wb = workbook_with_styles();
auto ws = wb["Sheet1"];
auto code = ws.get_cell("A5").get_number_format().get_format_code();
auto expected = xlnt::number_format::format::percentage_00;
auto code = ws.get_cell("A5").get_number_format();
auto expected = xlnt::number_format::percentage_00();
TS_ASSERT_EQUALS(code, expected);
}
xlnt::workbook date_mac_1904()
{
auto path = PathHelper::GetDataDirectory("/reader/date_1904.xlsx");
return xlnt::load_workbook(path);
return xlnt::excel_reader::load_workbook(path);
}
xlnt::workbook date_std_1900()
{
auto path = PathHelper::GetDataDirectory("/reader/date_1900.xlsx");
return xlnt::load_workbook(path);
return xlnt::excel_reader::load_workbook(path);
}
void test_read_win_base_date()
@ -166,14 +149,14 @@ public:
{
auto wb = date_std_1900();
auto ws = wb["Sheet1"];
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format().get_format_code(), xlnt::number_format::format::date_xlsx14);
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14());
}
void test_read_date_style_mac()
{
auto wb = date_mac_1904();
auto ws = wb["Sheet1"];
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format().get_format_code(), xlnt::number_format::format::date_xlsx14);
TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14());
}
void test_read_compare_mac_win_dates()
@ -190,40 +173,23 @@ public:
void test_repair_central_directory()
{
std::string data_a = "foobarbaz" + xlnt::CentralDirectorySignature();
std::string data_a = "foobarbaz" + xlnt::excel_reader::CentralDirectorySignature();
std::string data_b = "bazbarfoo12345678901234567890";
auto f = xlnt::repair_central_directory(data_a + data_b);
auto f = xlnt::excel_reader::repair_central_directory(data_a + data_b);
TS_ASSERT_EQUALS(f, data_a + data_b.substr(0, 18));
f = xlnt::repair_central_directory(data_b);
f = xlnt::excel_reader::repair_central_directory(data_b);
TS_ASSERT_EQUALS(f, data_b);
}
void test_read_no_theme()
{
auto path = PathHelper::GetDataDirectory("/genuine/libreoffice_nrt.xlsx");
auto wb = xlnt::load_workbook(path);
auto wb = xlnt::excel_reader::load_workbook(path);
TS_ASSERT_DIFFERS(wb, nullptr);
}
void test_read_cell_formulae()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
auto path = PathHelper::GetDataDirectory("/reader/worksheet_formula.xml");
std::ifstream ws_stream(path);
xlnt::read_worksheet(ws, ws_stream, { "string", "string2" }, {}, {});
auto b1 = ws.get_cell("B1");
TS_ASSERT(b1.has_formula());
TS_ASSERT_EQUALS(b1.get_formula(), "CONCATENATE(A1,A2)");
auto a6 = ws.get_cell("A6");
TS_ASSERT(a6.has_formula());
TS_ASSERT_EQUALS(a6.get_formula(), "SUM(A4:A5)");
}
void _test_read_complex_formulae()
{
/*
@ -284,7 +250,7 @@ public:
void test_data_only()
{
auto path = PathHelper::GetDataDirectory("/reader/formulae.xlsx");
auto wb = xlnt::load_workbook(path, false, true);
auto wb = xlnt::excel_reader::load_workbook(path, false, true);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_formula_attributes().empty());
@ -448,7 +414,7 @@ public:
{
std::tie(guess, dtype) = expected;
auto path = PathHelper::GetDataDirectory("/genuine/guess_types.xlsx");
auto wb = xlnt::load_workbook(path, guess);
auto wb = xlnt::excel_reader::load_workbook(path, guess);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_cell("D2").get_data_type() == dtype);
}
@ -457,7 +423,7 @@ public:
void test_read_autofilter()
{
auto path = PathHelper::GetDataDirectory("/reader/bug275.xlsx");
auto wb = xlnt::load_workbook(path);
auto wb = xlnt::excel_reader::load_workbook(path);
auto ws = wb.get_active_sheet();
TS_ASSERT_EQUALS(ws.get_auto_filter().to_string(), "A1:B6");
}
@ -465,18 +431,18 @@ public:
void test_bad_formats_xlsb()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.xlsb");
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::excel_reader::load_workbook(path), xlnt::invalid_file_exception);
}
void test_bad_formats_xls()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.xls");
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::excel_reader::load_workbook(path), xlnt::invalid_file_exception);
}
void test_bad_formats_no()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.no-format");
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::excel_reader::load_workbook(path), xlnt::invalid_file_exception);
}
};

View File

@ -11,7 +11,7 @@ public:
void test_write_number_formats()
{
xlnt::workbook wb;
wb.add_number_format("YYYY");
wb.add_number_format(xlnt::number_format("YYYY"));
xlnt::style_writer writer(wb);
auto xml = writer.write_number_formats();
std::string expected =

View File

@ -16,7 +16,7 @@ public:
xlnt::workbook wbk;
wbk.get_active_sheet().get_cell("A2").set_value("xlnt");
wbk.get_active_sheet().get_cell("B5").set_value(88);
wbk.get_active_sheet().get_cell("B5").set_number_format(xlnt::number_format(xlnt::number_format::format::percentage_00));
wbk.get_active_sheet().get_cell("B5").set_number_format(xlnt::number_format::percentage_00());
wbk.save(temp_file.GetFilename());
if(PathHelper::FileExists(temp_file.GetFilename()))

View File

@ -68,7 +68,7 @@ public:
{
xlnt::workbook old_wb;
auto saved_wb = xlnt::save_virtual_workbook(old_wb);
auto new_wb = xlnt::load_workbook(saved_wb);
auto new_wb = xlnt::excel_reader::load_workbook(saved_wb);
TS_ASSERT(new_wb != nullptr);
}