mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
work on round-tripping of a workbook with complex formatting
This commit is contained in:
parent
1c6c36d1a0
commit
5a9c18834d
|
@ -38,6 +38,7 @@ class color;
|
|||
class conditional_format;
|
||||
class fill;
|
||||
class font;
|
||||
class format;
|
||||
class named_style;
|
||||
class number_format;
|
||||
class protection;
|
||||
|
@ -219,6 +220,16 @@ public:
|
|||
/// </summary>
|
||||
bool read_number_formats(const xml_node &number_formats_node);
|
||||
|
||||
/// <summary>
|
||||
/// Read a single format from the given node. In styles.xml, this is an "xf" element.
|
||||
/// A format has an optional border id, fill id, font id, and number format id where
|
||||
/// each id is an index in the corresponding list of borders, etc. A style also has
|
||||
/// optional alignment and protection children. A style also defines whether each of
|
||||
/// these is "applied". For example, a style with a defined font id, font=# but with
|
||||
/// "applyFont=0" will not use the font in formatting.
|
||||
/// </summary>
|
||||
format read_format(const xml_node &format_node);
|
||||
|
||||
/// <summary>
|
||||
/// Read a single style from the given node. In styles.xml, this is an "xf" element.
|
||||
/// A style has an optional border id, fill id, font id, and number format id where
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
namespace xlnt {
|
||||
|
||||
/// <summary>
|
||||
/// Alignment options for use in styles.
|
||||
/// Alignment options for use in cell formats.
|
||||
/// </summary>
|
||||
class XLNT_CLASS alignment : public hashable
|
||||
{
|
||||
|
@ -52,6 +52,10 @@ public:
|
|||
vertical_alignment get_vertical() const;
|
||||
|
||||
void set_vertical(vertical_alignment vertical);
|
||||
|
||||
void set_shrink_to_fit(bool shrink_to_fit);
|
||||
|
||||
bool get_shrink_to_fit() const;
|
||||
|
||||
protected:
|
||||
std::string to_hash_string() const override;
|
||||
|
|
107
include/xlnt/styles/format.hpp
Normal file
107
include/xlnt/styles/format.hpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2014-2016 Thomas Fussell
|
||||
// Copyright (c) 2010-2015 openpyxl
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
#pragma once
|
||||
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/styles/alignment.hpp>
|
||||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class workbook;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the entirety of the formatting of a particular cell.
|
||||
/// </summary>
|
||||
class XLNT_CLASS format : public hashable
|
||||
{
|
||||
public:
|
||||
format();
|
||||
format(const format &other);
|
||||
format &operator=(const format &other);
|
||||
|
||||
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_id() const;
|
||||
std::size_t get_fill_id() const;
|
||||
std::size_t get_font_id() const;
|
||||
std::size_t get_border_id() const;
|
||||
std::size_t get_number_format_id() const;
|
||||
|
||||
void apply_alignment(bool apply);
|
||||
void apply_border(bool apply);
|
||||
void apply_fill(bool apply);
|
||||
void apply_font(bool apply);
|
||||
void apply_number_format(bool apply);
|
||||
void apply_protection(bool apply);
|
||||
|
||||
protected:
|
||||
std::string to_hash_string() const override;
|
||||
|
||||
private:
|
||||
friend class style_serializer;
|
||||
friend class workbook;
|
||||
|
||||
std::size_t id_;
|
||||
|
||||
bool alignment_apply_;
|
||||
alignment alignment_;
|
||||
|
||||
bool border_apply_;
|
||||
std::size_t border_id_;
|
||||
border border_;
|
||||
|
||||
bool fill_apply_;
|
||||
std::size_t fill_id_;
|
||||
fill fill_;
|
||||
|
||||
bool font_apply_;
|
||||
std::size_t font_id_;
|
||||
font font_;
|
||||
|
||||
bool number_format_apply_;
|
||||
std::size_t number_format_id_;
|
||||
number_format number_format_;
|
||||
|
||||
bool protection_apply_;
|
||||
protection protection_;
|
||||
|
||||
bool pivot_button_;
|
||||
bool quote_prefix_;
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
|
@ -24,12 +24,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/styles/alignment.hpp>
|
||||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/utils/hashable.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
|
@ -46,62 +41,29 @@ public:
|
|||
style &operator=(const style &other);
|
||||
|
||||
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_id() const;
|
||||
std::size_t get_fill_id() const;
|
||||
std::size_t get_font_id() const;
|
||||
std::size_t get_border_id() const;
|
||||
std::size_t get_number_format_id() const;
|
||||
|
||||
void apply_alignment(bool apply);
|
||||
void apply_border(bool apply);
|
||||
void apply_fill(bool apply);
|
||||
void apply_font(bool apply);
|
||||
void apply_number_format(bool apply);
|
||||
void apply_protection(bool apply);
|
||||
|
||||
|
||||
std::string get_name() const;
|
||||
void set_name(const std::string &name);
|
||||
|
||||
std::size_t get_format_id() const;
|
||||
void set_format_id(std::size_t format_id);
|
||||
|
||||
std::size_t get_builtin_id() const;
|
||||
void set_builtin_id(std::size_t builtin_id);
|
||||
|
||||
void set_hidden(bool hidden);
|
||||
bool get_hidden() const;
|
||||
|
||||
protected:
|
||||
std::string to_hash_string() const override;
|
||||
|
||||
|
||||
private:
|
||||
friend class style_serializer;
|
||||
friend class workbook;
|
||||
|
||||
std::size_t id_;
|
||||
|
||||
bool alignment_apply_;
|
||||
alignment alignment_;
|
||||
|
||||
bool border_apply_;
|
||||
std::size_t border_id_;
|
||||
border border_;
|
||||
|
||||
bool fill_apply_;
|
||||
std::size_t fill_id_;
|
||||
fill fill_;
|
||||
|
||||
bool font_apply_;
|
||||
std::size_t font_id_;
|
||||
font font_;
|
||||
|
||||
bool number_format_apply_;
|
||||
std::size_t number_format_id_;
|
||||
number_format number_format_;
|
||||
|
||||
bool protection_apply_;
|
||||
protection protection_;
|
||||
|
||||
bool pivot_button_;
|
||||
bool quote_prefix_;
|
||||
std::string name_;
|
||||
std::size_t format_id_;
|
||||
std::size_t builtin_id_;
|
||||
bool hidden_;
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
||||
|
|
|
@ -43,6 +43,7 @@ class document_properties;
|
|||
class drawing;
|
||||
class fill;
|
||||
class font;
|
||||
class format;
|
||||
class manifest;
|
||||
class named_range;
|
||||
class number_format;
|
||||
|
@ -182,6 +183,8 @@ public:
|
|||
void add_number_format(const number_format &format);
|
||||
void add_protection(const protection &p);
|
||||
|
||||
const std::vector<format> &get_cell_style_formats() const;
|
||||
const std::vector<format> &get_cell_formats() const;
|
||||
const std::vector<style> &get_styles() const;
|
||||
|
||||
const std::vector<color> &get_colors() const;
|
||||
|
@ -213,6 +216,12 @@ public:
|
|||
bool has_loaded_theme() const;
|
||||
const theme &get_loaded_theme() const;
|
||||
|
||||
const format &get_cell_style_format(std::size_t format_id) const;
|
||||
std::size_t add_cell_style_format(const format &format_);
|
||||
|
||||
const format &get_cell_format(std::size_t format_id) const;
|
||||
std::size_t add_cell_format(const format &format_);
|
||||
|
||||
const style &get_style(std::size_t style_id) const;
|
||||
std::size_t add_style(const style &style_);
|
||||
|
||||
|
@ -224,6 +233,9 @@ public:
|
|||
void add_shared_string(const std::string &shared);
|
||||
std::vector<std::string> &get_shared_strings();
|
||||
const std::vector<std::string> &get_shared_strings() const;
|
||||
|
||||
void set_thumbnail(const std::vector<std::uint8_t> &thumbnail);
|
||||
const std::vector<std::uint8_t> &get_thumbnail() const;
|
||||
|
||||
private:
|
||||
friend class worksheet;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <xlnt/packaging/relationship.hpp>
|
||||
#include <xlnt/serialization/encoding.hpp>
|
||||
#include <xlnt/styles/color.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
#include <xlnt/utils/date.hpp>
|
||||
#include <xlnt/utils/datetime.hpp>
|
||||
#include <xlnt/utils/time.hpp>
|
||||
|
@ -596,7 +596,7 @@ const font &cell::get_font() const
|
|||
{
|
||||
if (d_->has_style_)
|
||||
{
|
||||
auto font_id = get_parent().get_parent().get_styles()[d_->style_id_].get_font_id();
|
||||
auto font_id = get_parent().get_parent().get_cell_formats()[d_->style_id_].get_font_id();
|
||||
return get_parent().get_parent().get_font(font_id);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -98,6 +98,8 @@ struct workbook_impl
|
|||
bool data_only_;
|
||||
bool read_only_;
|
||||
|
||||
std::vector<format> cell_style_formats_;
|
||||
std::vector<format> cell_formats_;
|
||||
std::vector<style> styles_;
|
||||
|
||||
std::size_t next_custom_format_id_;
|
||||
|
@ -113,6 +115,8 @@ struct workbook_impl
|
|||
theme theme_;
|
||||
|
||||
encoding encoding_;
|
||||
|
||||
std::vector<std::uint8_t> thumbnail_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
|
|
@ -162,6 +162,12 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
|
|||
worksheet_serializer.read_worksheet(worksheet_xml);
|
||||
}
|
||||
|
||||
if (archive.has_file("docProps/thumbnail.jpeg"))
|
||||
{
|
||||
auto thumbnail_data = archive.read("docProps/thumbnail.jpeg");
|
||||
wb.set_thumbnail(std::vector<std::uint8_t>(thumbnail_data.begin(), thumbnail_data.end()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -251,6 +257,12 @@ void excel_serializer::write_data(bool /*as_template*/)
|
|||
archive_.writestr(constants::ArcContentTypes(), manifest_serializer_.write_manifest().to_string());
|
||||
|
||||
write_worksheets();
|
||||
|
||||
if(!workbook_.get_thumbnail().empty())
|
||||
{
|
||||
const auto &thumbnail = workbook_.get_thumbnail();
|
||||
archive_.writestr("docProps/thumbnail.jpeg", std::string(thumbnail.begin(), thumbnail.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void excel_serializer::write_worksheets()
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
#include <cctype>
|
||||
#include <cctype> // for std::tolower
|
||||
|
||||
#include <xlnt/serialization/style_serializer.hpp>
|
||||
#include <xlnt/serialization/xml_document.hpp>
|
||||
|
@ -30,6 +30,7 @@
|
|||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
|
@ -196,6 +197,7 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node)
|
|||
alignment align;
|
||||
|
||||
align.set_wrap_text(is_true(alignment_node.get_attribute("wrapText")));
|
||||
align.set_shrink_to_fit(is_true(alignment_node.get_attribute("shrinkToFit")));
|
||||
|
||||
bool has_vertical = alignment_node.has_attribute("vertical");
|
||||
|
||||
|
@ -264,20 +266,20 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node)
|
|||
return align;
|
||||
}
|
||||
|
||||
style style_serializer::read_style(const xml_node &style_node)
|
||||
format style_serializer::read_format(const xml_node &format_node)
|
||||
{
|
||||
style s;
|
||||
format f;
|
||||
|
||||
s.apply_number_format(is_true(style_node.get_attribute("applyNumberFormat")));
|
||||
s.number_format_id_ = std::stoull(style_node.get_attribute("numFmtId"));
|
||||
f.apply_number_format(is_true(format_node.get_attribute("applyNumberFormat")));
|
||||
f.number_format_id_ = std::stoull(format_node.get_attribute("numFmtId"));
|
||||
|
||||
bool builtin_format = true;
|
||||
|
||||
for (auto num_fmt : workbook_.get_number_formats())
|
||||
{
|
||||
if (static_cast<std::size_t>(num_fmt.get_id()) == s.get_number_format_id())
|
||||
if (static_cast<std::size_t>(num_fmt.get_id()) == f.get_number_format_id())
|
||||
{
|
||||
s.number_format_ = num_fmt;
|
||||
f.number_format_ = num_fmt;
|
||||
builtin_format = false;
|
||||
break;
|
||||
}
|
||||
|
@ -285,37 +287,49 @@ style style_serializer::read_style(const xml_node &style_node)
|
|||
|
||||
if (builtin_format)
|
||||
{
|
||||
s.number_format_ = number_format::from_builtin_id(s.get_number_format_id());
|
||||
f.number_format_ = number_format::from_builtin_id(f.get_number_format_id());
|
||||
}
|
||||
|
||||
s.apply_font(is_true(style_node.get_attribute("applyFont")));
|
||||
s.font_id_ = style_node.has_attribute("fontId") ? std::stoull(style_node.get_attribute("fontId")) : 0;
|
||||
s.font_ = workbook_.get_font(s.font_id_);
|
||||
f.apply_font(is_true(format_node.get_attribute("applyFont")));
|
||||
f.font_id_ = format_node.has_attribute("fontId") ? std::stoull(format_node.get_attribute("fontId")) : 0;
|
||||
f.font_ = workbook_.get_font(f.font_id_);
|
||||
|
||||
s.apply_fill(is_true(style_node.get_attribute("applyFill")));
|
||||
s.fill_id_ = style_node.has_attribute("fillId") ? std::stoull(style_node.get_attribute("fillId")) : 0;
|
||||
s.fill_ = workbook_.get_fill(s.fill_id_);
|
||||
f.apply_fill(is_true(format_node.get_attribute("applyFill")));
|
||||
f.fill_id_ = format_node.has_attribute("fillId") ? std::stoull(format_node.get_attribute("fillId")) : 0;
|
||||
f.fill_ = workbook_.get_fill(f.fill_id_);
|
||||
|
||||
s.apply_border(is_true(style_node.get_attribute("applyBorder")));
|
||||
s.border_id_ = style_node.has_attribute("borderId") ? std::stoull(style_node.get_attribute("borderId")) : 0;
|
||||
s.border_ = workbook_.get_border(s.border_id_);
|
||||
f.apply_border(is_true(format_node.get_attribute("applyBorder")));
|
||||
f.border_id_ = format_node.has_attribute("borderId") ? std::stoull(format_node.get_attribute("borderId")) : 0;
|
||||
f.border_ = workbook_.get_border(f.border_id_);
|
||||
|
||||
s.apply_protection(style_node.has_attribute("protection"));
|
||||
f.apply_protection(format_node.has_attribute("protection"));
|
||||
|
||||
if (s.protection_apply_)
|
||||
if (f.protection_apply_)
|
||||
{
|
||||
auto inline_protection = read_protection(style_node.get_child("protection"));
|
||||
s.protection_ = inline_protection;
|
||||
auto inline_protection = read_protection(format_node.get_child("protection"));
|
||||
f.protection_ = inline_protection;
|
||||
}
|
||||
|
||||
s.apply_alignment(style_node.has_child("alignment"));
|
||||
f.apply_alignment(format_node.has_child("alignment"));
|
||||
|
||||
if (s.alignment_apply_)
|
||||
if (f.alignment_apply_)
|
||||
{
|
||||
auto inline_alignment = read_alignment(style_node.get_child("alignment"));
|
||||
s.alignment_ = inline_alignment;
|
||||
auto inline_alignment = read_alignment(format_node.get_child("alignment"));
|
||||
f.alignment_ = inline_alignment;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
style style_serializer::read_style(const xml_node &style_node)
|
||||
{
|
||||
style s;
|
||||
|
||||
s.set_name(style_node.get_attribute("name"));
|
||||
s.set_format_id(std::stoull(style_node.get_attribute("xfId")));
|
||||
s.set_hidden(style_node.has_attribute("hidden") && is_true(style_node.get_attribute("hidden")));
|
||||
s.set_builtin_id(std::stoull(style_node.get_attribute("builtinId")));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -329,16 +343,40 @@ bool style_serializer::read_stylesheet(const xml_document &xml)
|
|||
read_number_formats(stylesheet_node.get_child("numFmts"));
|
||||
read_colors(stylesheet_node.get_child("colors"));
|
||||
|
||||
auto cell_xfs_node = stylesheet_node.get_child("cellXfs");
|
||||
auto cell_style_formats_node = stylesheet_node.get_child("cellStyleXfs");
|
||||
|
||||
for (auto xf_node : cell_xfs_node.get_children())
|
||||
for (auto format_node : cell_style_formats_node.get_children())
|
||||
{
|
||||
if (xf_node.get_name() != "xf")
|
||||
if (format_node.get_name() != "xf")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
workbook_.add_style(read_style(xf_node));
|
||||
workbook_.add_cell_style_format(read_format(format_node));
|
||||
}
|
||||
|
||||
auto cell_formats_node = stylesheet_node.get_child("cellXfs");
|
||||
|
||||
for (auto format_node : cell_formats_node.get_children())
|
||||
{
|
||||
if (format_node.get_name() != "xf")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
workbook_.add_cell_format(read_format(format_node));
|
||||
}
|
||||
|
||||
auto cell_styles_node = stylesheet_node.get_child("cellStyles");
|
||||
|
||||
for (auto cell_style_node : cell_styles_node.get_children())
|
||||
{
|
||||
if (cell_style_node.get_name() != "cellStyle")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
workbook_.add_style(read_style(cell_style_node));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -405,23 +443,51 @@ font style_serializer::read_font(const xlnt::xml_node &font_node)
|
|||
|
||||
if (font_node.has_child("b"))
|
||||
{
|
||||
new_font.set_bold(is_true(font_node.get_child("b").get_attribute("val")));
|
||||
if(font_node.get_child("b").has_attribute("val"))
|
||||
{
|
||||
new_font.set_bold(is_true(font_node.get_child("b").get_attribute("val")));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_font.set_bold(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (font_node.has_child("strike"))
|
||||
{
|
||||
new_font.set_strikethrough(is_true(font_node.get_child("strike").get_attribute("val")));
|
||||
if(font_node.get_child("strike").has_attribute("val"))
|
||||
{
|
||||
new_font.set_strikethrough(is_true(font_node.get_child("strike").get_attribute("val")));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_font.set_strikethrough(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (font_node.has_child("i"))
|
||||
{
|
||||
new_font.set_italic(is_true(font_node.get_child("i").get_attribute("val")));
|
||||
if(font_node.get_child("i").has_attribute("val"))
|
||||
{
|
||||
new_font.set_italic(is_true(font_node.get_child("i").get_attribute("val")));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_font.set_italic(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (font_node.has_child("u"))
|
||||
{
|
||||
std::string underline_string = font_node.get_child("u").get_attribute("val");
|
||||
new_font.set_underline(underline_style_from_string(underline_string));
|
||||
if (font_node.get_child("u").has_attribute("val"))
|
||||
{
|
||||
std::string underline_string = font_node.get_child("u").get_attribute("val");
|
||||
new_font.set_underline(underline_style_from_string(underline_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_font.set_underline(xlnt::font::underline_style::single);
|
||||
}
|
||||
}
|
||||
|
||||
return new_font;
|
||||
|
@ -635,18 +701,18 @@ xml_document style_serializer::write_stylesheet() const
|
|||
|
||||
if (f.is_italic())
|
||||
{
|
||||
auto bold_node = font_node.add_child("i");
|
||||
bold_node.add_attribute("val", "1");
|
||||
auto italic_node = font_node.add_child("i");
|
||||
italic_node.add_attribute("val", "1");
|
||||
}
|
||||
|
||||
if (f.is_underline())
|
||||
{
|
||||
auto bold_node = font_node.add_child("u");
|
||||
auto underline_node = font_node.add_child("u");
|
||||
|
||||
switch (f.get_underline())
|
||||
{
|
||||
case font::underline_style::single:
|
||||
bold_node.add_attribute("val", "single");
|
||||
underline_node.add_attribute("val", "single");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -655,8 +721,8 @@ xml_document style_serializer::write_stylesheet() const
|
|||
|
||||
if (f.is_strikethrough())
|
||||
{
|
||||
auto bold_node = font_node.add_child("strike");
|
||||
bold_node.add_attribute("val", "1");
|
||||
auto strike_node = font_node.add_child("strike");
|
||||
strike_node.add_attribute("val", "1");
|
||||
}
|
||||
|
||||
auto size_node = font_node.add_child("sz");
|
||||
|
@ -672,7 +738,15 @@ xml_document style_serializer::write_stylesheet() const
|
|||
{
|
||||
color_node.add_attribute("theme", std::to_string(f.get_color().get_theme()));
|
||||
}
|
||||
|
||||
else if (f.get_color().get_type() == color::type::auto_)
|
||||
{
|
||||
color_node.add_attribute("auto", std::to_string(f.get_color().get_auto()));
|
||||
}
|
||||
else if (f.get_color().get_type() == color::type::rgb)
|
||||
{
|
||||
color_node.add_attribute("rgb", f.get_color().get_rgb_string());
|
||||
}
|
||||
|
||||
auto name_node = font_node.add_child("name");
|
||||
name_node.add_attribute("val", f.get_name());
|
||||
|
||||
|
@ -702,6 +776,8 @@ xml_document style_serializer::write_stylesheet() const
|
|||
auto pattern_fill_node = fill_node.add_child("patternFill");
|
||||
auto type_string = fill_.get_pattern_type_string();
|
||||
pattern_fill_node.add_attribute("patternType", type_string);
|
||||
|
||||
if (fill_.get_pattern_type() != xlnt::fill::pattern_type::solid) continue;
|
||||
|
||||
if (fill_.get_foreground_color())
|
||||
{
|
||||
|
@ -817,10 +893,10 @@ xml_document style_serializer::write_stylesheet() const
|
|||
style_string = "none";
|
||||
break;
|
||||
case border_style::dashdot:
|
||||
style_string = "dashdot";
|
||||
style_string = "dashDot";
|
||||
break;
|
||||
case border_style::dashdotdot:
|
||||
style_string = "dashdotdot";
|
||||
style_string = "dashDotDot";
|
||||
break;
|
||||
case border_style::dashed:
|
||||
style_string = "dashed";
|
||||
|
@ -838,16 +914,16 @@ xml_document style_serializer::write_stylesheet() const
|
|||
style_string = "thin";
|
||||
break;
|
||||
case border_style::mediumdashdot:
|
||||
style_string = "mediumdashdot";
|
||||
style_string = "mediumDashDot";
|
||||
break;
|
||||
case border_style::mediumdashdotdot:
|
||||
style_string = "mediumdashdotdot";
|
||||
style_string = "mediumDashDotDot";
|
||||
break;
|
||||
case border_style::mediumdashed:
|
||||
style_string = "mediumdashed";
|
||||
style_string = "mediumDashed";
|
||||
break;
|
||||
case border_style::slantdashdot:
|
||||
style_string = "slantdashdot";
|
||||
style_string = "slantDashDot";
|
||||
break;
|
||||
case border_style::thick:
|
||||
style_string = "thick";
|
||||
|
@ -888,48 +964,32 @@ xml_document style_serializer::write_stylesheet() const
|
|||
}
|
||||
|
||||
auto cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs");
|
||||
cell_style_xfs_node.add_attribute("count", "1");
|
||||
auto &cell_style_formats = workbook_.get_cell_style_formats();
|
||||
cell_style_xfs_node.add_attribute("count", std::to_string(cell_style_formats.size()));
|
||||
|
||||
auto style_xf_node = cell_style_xfs_node.add_child("xf");
|
||||
style_xf_node.add_attribute("numFmtId", "0");
|
||||
style_xf_node.add_attribute("fontId", "0");
|
||||
style_xf_node.add_attribute("fillId", "0");
|
||||
style_xf_node.add_attribute("borderId", "0");
|
||||
|
||||
auto cell_xfs_node = style_sheet_node.add_child("cellXfs");
|
||||
const auto &styles = workbook_.get_styles();
|
||||
cell_xfs_node.add_attribute("count", std::to_string(styles.size()));
|
||||
|
||||
for (auto &style : styles)
|
||||
for(auto &format : cell_style_formats)
|
||||
{
|
||||
auto xf_node = cell_xfs_node.add_child("xf");
|
||||
xf_node.add_attribute("numFmtId", std::to_string(style.get_number_format().get_id()));
|
||||
xf_node.add_attribute("fontId", std::to_string(style.get_font_id()));
|
||||
auto xf_node = cell_style_xfs_node.add_child("xf");
|
||||
|
||||
xf_node.add_attribute("numFmtId", std::to_string(format.get_number_format().get_id()));
|
||||
xf_node.add_attribute("fontId", std::to_string(format.get_font_id()));
|
||||
xf_node.add_attribute("fillId", std::to_string(format.get_fill_id()));
|
||||
xf_node.add_attribute("borderId", std::to_string(format.get_border_id()));
|
||||
|
||||
if (style.fill_apply_)
|
||||
{
|
||||
xf_node.add_attribute("fillId", std::to_string(style.get_fill_id()));
|
||||
}
|
||||
xf_node.add_attribute("applyNumberFormat", format.number_format_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFill", format.fill_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFont", format.font_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyBorder", format.border_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyAlignment", format.alignment_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyProtection", format.protection_apply_ ? "1" : "0");
|
||||
|
||||
if (style.border_apply_)
|
||||
{
|
||||
xf_node.add_attribute("borderId", std::to_string(style.get_border_id()));
|
||||
}
|
||||
|
||||
xf_node.add_attribute("applyNumberFormat", style.number_format_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFont", style.font_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFill", style.fill_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyBorder", style.border_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyAlignment", style.alignment_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyProtection", style.protection_apply_ ? "1" : "0");
|
||||
|
||||
if (style.alignment_apply_)
|
||||
if (format.alignment_apply_)
|
||||
{
|
||||
auto alignment_node = xf_node.add_child("alignment");
|
||||
|
||||
if (style.alignment_.has_vertical())
|
||||
if (format.alignment_.has_vertical())
|
||||
{
|
||||
switch (style.alignment_.get_vertical())
|
||||
switch (format.alignment_.get_vertical())
|
||||
{
|
||||
case vertical_alignment::bottom:
|
||||
alignment_node.add_attribute("vertical", "bottom");
|
||||
|
@ -948,9 +1008,9 @@ xml_document style_serializer::write_stylesheet() const
|
|||
}
|
||||
}
|
||||
|
||||
if (style.alignment_.has_horizontal())
|
||||
if (format.alignment_.has_horizontal())
|
||||
{
|
||||
switch (style.alignment_.get_horizontal())
|
||||
switch (format.alignment_.get_horizontal())
|
||||
{
|
||||
case horizontal_alignment::center:
|
||||
alignment_node.add_attribute("horizontal", "center");
|
||||
|
@ -975,19 +1035,128 @@ xml_document style_serializer::write_stylesheet() const
|
|||
}
|
||||
}
|
||||
|
||||
if (style.alignment_.get_wrap_text())
|
||||
if (format.alignment_.get_wrap_text())
|
||||
{
|
||||
alignment_node.add_attribute("wrapText", "1");
|
||||
}
|
||||
|
||||
if (format.alignment_.get_shrink_to_fit())
|
||||
{
|
||||
alignment_node.add_attribute("shrinkToFit", "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto cell_xfs_node = style_sheet_node.add_child("cellXfs");
|
||||
const auto &formats = workbook_.get_cell_formats();
|
||||
cell_xfs_node.add_attribute("count", std::to_string(formats.size()));
|
||||
|
||||
for (auto &format : formats)
|
||||
{
|
||||
auto xf_node = cell_xfs_node.add_child("xf");
|
||||
xf_node.add_attribute("numFmtId", std::to_string(format.get_number_format().get_id()));
|
||||
xf_node.add_attribute("fontId", std::to_string(format.get_font_id()));
|
||||
|
||||
if (format.fill_apply_)
|
||||
{
|
||||
xf_node.add_attribute("fillId", std::to_string(format.get_fill_id()));
|
||||
}
|
||||
|
||||
if (format.border_apply_)
|
||||
{
|
||||
xf_node.add_attribute("borderId", std::to_string(format.get_border_id()));
|
||||
}
|
||||
|
||||
xf_node.add_attribute("xfId", "0");
|
||||
|
||||
xf_node.add_attribute("applyNumberFormat", format.number_format_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFont", format.font_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyFill", format.fill_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyBorder", format.border_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyAlignment", format.alignment_apply_ ? "1" : "0");
|
||||
xf_node.add_attribute("applyProtection", format.protection_apply_ ? "1" : "0");
|
||||
|
||||
if (format.alignment_apply_)
|
||||
{
|
||||
auto alignment_node = xf_node.add_child("alignment");
|
||||
|
||||
if (format.alignment_.has_vertical())
|
||||
{
|
||||
switch (format.alignment_.get_vertical())
|
||||
{
|
||||
case vertical_alignment::bottom:
|
||||
alignment_node.add_attribute("vertical", "bottom");
|
||||
break;
|
||||
case vertical_alignment::center:
|
||||
alignment_node.add_attribute("vertical", "center");
|
||||
break;
|
||||
case vertical_alignment::justify:
|
||||
alignment_node.add_attribute("vertical", "justify");
|
||||
break;
|
||||
case vertical_alignment::top:
|
||||
alignment_node.add_attribute("vertical", "top");
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("invalid alignment");
|
||||
}
|
||||
}
|
||||
|
||||
if (format.alignment_.has_horizontal())
|
||||
{
|
||||
switch (format.alignment_.get_horizontal())
|
||||
{
|
||||
case horizontal_alignment::center:
|
||||
alignment_node.add_attribute("horizontal", "center");
|
||||
break;
|
||||
case horizontal_alignment::center_continuous:
|
||||
alignment_node.add_attribute("horizontal", "center_continuous");
|
||||
break;
|
||||
case horizontal_alignment::general:
|
||||
alignment_node.add_attribute("horizontal", "general");
|
||||
break;
|
||||
case horizontal_alignment::justify:
|
||||
alignment_node.add_attribute("horizontal", "justify");
|
||||
break;
|
||||
case horizontal_alignment::left:
|
||||
alignment_node.add_attribute("horizontal", "left");
|
||||
break;
|
||||
case horizontal_alignment::right:
|
||||
alignment_node.add_attribute("horizontal", "right");
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("invalid alignment");
|
||||
}
|
||||
}
|
||||
|
||||
if (format.alignment_.get_wrap_text())
|
||||
{
|
||||
alignment_node.add_attribute("wrapText", "1");
|
||||
}
|
||||
|
||||
if (format.alignment_.get_shrink_to_fit())
|
||||
{
|
||||
alignment_node.add_attribute("shrinkToFit", "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto cell_styles_node = style_sheet_node.add_child("cellStyles");
|
||||
cell_styles_node.add_attribute("count", "1");
|
||||
auto cell_style_node = cell_styles_node.add_child("cellStyle");
|
||||
cell_style_node.add_attribute("name", "Normal");
|
||||
cell_style_node.add_attribute("xfId", "0");
|
||||
cell_style_node.add_attribute("builtinId", "0");
|
||||
auto &styles = workbook_.get_styles();
|
||||
cell_styles_node.add_attribute("count", std::to_string(styles.size()));
|
||||
|
||||
for(auto &cell_style : styles)
|
||||
{
|
||||
auto cell_style_node = cell_styles_node.add_child("cellStyle");
|
||||
|
||||
cell_style_node.add_attribute("name", cell_style.get_name());
|
||||
cell_style_node.add_attribute("xfId", std::to_string(cell_style.get_format_id()));
|
||||
cell_style_node.add_attribute("builtinId", std::to_string(cell_style.get_builtin_id()));
|
||||
|
||||
if (cell_style.get_hidden())
|
||||
{
|
||||
cell_style_node.add_attribute("hidden", "1");
|
||||
}
|
||||
}
|
||||
|
||||
style_sheet_node.add_child("dxfs").add_attribute("count", "0");
|
||||
|
||||
|
@ -996,14 +1165,17 @@ xml_document style_serializer::write_stylesheet() const
|
|||
table_styles_node.add_attribute("defaultTableStyle", "TableStyleMedium2");
|
||||
table_styles_node.add_attribute("defaultPivotStyle", "PivotStyleMedium9");
|
||||
|
||||
auto colors_node = style_sheet_node.add_child("colors");
|
||||
auto indexed_colors_node = colors_node.add_child("indexedColors");
|
||||
|
||||
for (auto &c : workbook_.get_colors())
|
||||
if(!workbook_.get_colors().empty())
|
||||
{
|
||||
indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string());
|
||||
}
|
||||
auto colors_node = style_sheet_node.add_child("colors");
|
||||
auto indexed_colors_node = colors_node.add_child("indexedColors");
|
||||
|
||||
for (auto &c : workbook_.get_colors())
|
||||
{
|
||||
indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string());
|
||||
}
|
||||
}
|
||||
|
||||
auto ext_list_node = style_sheet_node.add_child("extLst");
|
||||
auto ext_node = ext_list_node.add_child("ext");
|
||||
ext_node.add_attribute("uri", "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}");
|
||||
|
|
|
@ -95,15 +95,18 @@ void workbook_serializer::read_properties_core(const xml_document &xml)
|
|||
{
|
||||
props.creator = root_node.get_child("dc:creator").get_text();
|
||||
}
|
||||
|
||||
if (root_node.has_child("cp:lastModifiedBy"))
|
||||
{
|
||||
props.last_modified_by = root_node.get_child("cp:lastModifiedBy").get_text();
|
||||
}
|
||||
|
||||
if (root_node.has_child("dcterms:created"))
|
||||
{
|
||||
std::string created_string = root_node.get_child("dcterms:created").get_text();
|
||||
props.created = w3cdtf_to_datetime(created_string);
|
||||
}
|
||||
|
||||
if (root_node.has_child("dcterms:modified"))
|
||||
{
|
||||
std::string modified_string = root_node.get_child("dcterms:modified").get_text();
|
||||
|
|
|
@ -31,6 +31,16 @@ bool alignment::get_wrap_text() const
|
|||
return wrap_text_;
|
||||
}
|
||||
|
||||
void alignment::set_shrink_to_fit(bool shrink_to_fit)
|
||||
{
|
||||
shrink_to_fit_ = shrink_to_fit;
|
||||
}
|
||||
|
||||
bool alignment::get_shrink_to_fit() const
|
||||
{
|
||||
return shrink_to_fit_;
|
||||
}
|
||||
|
||||
void alignment::set_wrap_text(bool wrap_text)
|
||||
{
|
||||
wrap_text_ = wrap_text;
|
||||
|
|
237
source/styles/format.cpp
Normal file
237
source/styles/format.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright (c) 2014-2016 Thomas Fussell
|
||||
// Copyright (c) 2010-2015 openpyxl
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
void hash_combine(std::size_t &seed, const T &v)
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
const color color::black()
|
||||
{
|
||||
return color(color::type::rgb, "ff000000");
|
||||
}
|
||||
|
||||
const color color::white()
|
||||
{
|
||||
return color(color::type::rgb, "ffffffff");
|
||||
}
|
||||
|
||||
format::format()
|
||||
: 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)
|
||||
{
|
||||
}
|
||||
|
||||
format::format(const format &other)
|
||||
: id_(other.id_),
|
||||
alignment_apply_(other.alignment_apply_),
|
||||
alignment_(other.alignment_),
|
||||
border_apply_(other.border_apply_),
|
||||
border_id_(other.border_id_),
|
||||
border_(other.border_),
|
||||
fill_apply_(other.fill_apply_),
|
||||
fill_id_(other.fill_id_),
|
||||
fill_(other.fill_),
|
||||
font_apply_(other.font_apply_),
|
||||
font_id_(other.font_id_),
|
||||
font_(other.font_),
|
||||
number_format_apply_(other.number_format_apply_),
|
||||
number_format_id_(other.number_format_id_),
|
||||
number_format_(other.number_format_),
|
||||
protection_apply_(other.protection_apply_),
|
||||
protection_(other.protection_),
|
||||
pivot_button_(other.pivot_button_),
|
||||
quote_prefix_(other.quote_prefix_)
|
||||
{
|
||||
}
|
||||
|
||||
format &format::operator=(const format &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::string format::to_hash_string() const
|
||||
{
|
||||
std::string hash_string("format");
|
||||
|
||||
hash_string.append(std::to_string(alignment_apply_));
|
||||
hash_string.append(alignment_apply_ ? std::to_string(alignment_.hash()) : " ");
|
||||
|
||||
hash_string.append(std::to_string(border_apply_));
|
||||
hash_string.append(border_apply_ ? std::to_string(border_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(font_apply_));
|
||||
hash_string.append(font_apply_ ? std::to_string(font_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(fill_apply_));
|
||||
hash_string.append(fill_apply_ ? std::to_string(fill_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(number_format_apply_));
|
||||
hash_string.append(number_format_apply_ ? std::to_string(number_format_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(protection_apply_));
|
||||
hash_string.append(protection_apply_ ? std::to_string(protection_.hash()) : " ");
|
||||
|
||||
return hash_string;
|
||||
}
|
||||
|
||||
const number_format format::get_number_format() const
|
||||
{
|
||||
return number_format_;
|
||||
}
|
||||
|
||||
const border format::get_border() const
|
||||
{
|
||||
return border_;
|
||||
}
|
||||
|
||||
const alignment format::get_alignment() const
|
||||
{
|
||||
return alignment_;
|
||||
}
|
||||
|
||||
const fill format::get_fill() const
|
||||
{
|
||||
return fill_;
|
||||
}
|
||||
|
||||
const font format::get_font() const
|
||||
{
|
||||
return font_;
|
||||
}
|
||||
|
||||
const protection format::get_protection() const
|
||||
{
|
||||
return protection_;
|
||||
}
|
||||
|
||||
bool format::pivot_button() const
|
||||
{
|
||||
return pivot_button_;
|
||||
}
|
||||
|
||||
bool format::quote_prefix() const
|
||||
{
|
||||
return quote_prefix_;
|
||||
}
|
||||
|
||||
std::size_t format::get_id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
std::size_t format::get_fill_id() const
|
||||
{
|
||||
return fill_id_;
|
||||
}
|
||||
|
||||
std::size_t format::get_font_id() const
|
||||
{
|
||||
return font_id_;
|
||||
}
|
||||
|
||||
std::size_t format::get_border_id() const
|
||||
{
|
||||
return border_id_;
|
||||
}
|
||||
|
||||
std::size_t format::get_number_format_id() const
|
||||
{
|
||||
return number_format_id_;
|
||||
}
|
||||
|
||||
void format::apply_alignment(bool apply)
|
||||
{
|
||||
alignment_apply_ = apply;
|
||||
}
|
||||
|
||||
void format::apply_border(bool apply)
|
||||
{
|
||||
border_apply_ = apply;
|
||||
}
|
||||
|
||||
void format::apply_fill(bool apply)
|
||||
{
|
||||
fill_apply_ = apply;
|
||||
}
|
||||
|
||||
void format::apply_font(bool apply)
|
||||
{
|
||||
font_apply_ = apply;
|
||||
}
|
||||
|
||||
void format::apply_number_format(bool apply)
|
||||
{
|
||||
number_format_apply_ = apply;
|
||||
}
|
||||
|
||||
void format::apply_protection(bool apply)
|
||||
{
|
||||
protection_apply_ = apply;
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
|
@ -21,10 +21,6 @@
|
|||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
|
||||
namespace {
|
||||
|
@ -39,199 +35,74 @@ void hash_combine(std::size_t &seed, const T &v)
|
|||
|
||||
namespace xlnt {
|
||||
|
||||
const color color::black()
|
||||
{
|
||||
return color(color::type::rgb, "ff000000");
|
||||
}
|
||||
|
||||
const color color::white()
|
||||
{
|
||||
return color(color::type::rgb, "ffffffff");
|
||||
}
|
||||
|
||||
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_apply_(other.alignment_apply_),
|
||||
alignment_(other.alignment_),
|
||||
border_apply_(other.border_apply_),
|
||||
border_id_(other.border_id_),
|
||||
border_(other.border_),
|
||||
fill_apply_(other.fill_apply_),
|
||||
fill_id_(other.fill_id_),
|
||||
fill_(other.fill_),
|
||||
font_apply_(other.font_apply_),
|
||||
font_id_(other.font_id_),
|
||||
font_(other.font_),
|
||||
number_format_apply_(other.number_format_apply_),
|
||||
number_format_id_(other.number_format_id_),
|
||||
number_format_(other.number_format_),
|
||||
protection_apply_(other.protection_apply_),
|
||||
protection_(other.protection_),
|
||||
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::string style::to_hash_string() const
|
||||
{
|
||||
std::string hash_string("style");
|
||||
|
||||
hash_string.append(std::to_string(alignment_apply_));
|
||||
hash_string.append(alignment_apply_ ? std::to_string(alignment_.hash()) : " ");
|
||||
|
||||
hash_string.append(std::to_string(border_apply_));
|
||||
hash_string.append(border_apply_ ? std::to_string(border_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(font_apply_));
|
||||
hash_string.append(font_apply_ ? std::to_string(font_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(fill_apply_));
|
||||
hash_string.append(fill_apply_ ? std::to_string(fill_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(number_format_apply_));
|
||||
hash_string.append(number_format_apply_ ? std::to_string(number_format_id_) : " ");
|
||||
|
||||
hash_string.append(std::to_string(protection_apply_));
|
||||
hash_string.append(protection_apply_ ? std::to_string(protection_.hash()) : " ");
|
||||
hash_string.append(name_);
|
||||
hash_string.append(std::to_string(format_id_));
|
||||
hash_string.append(std::to_string(builtin_id_));
|
||||
hash_string.append(std::to_string(hidden_ ? 0 : 1));
|
||||
|
||||
return hash_string;
|
||||
}
|
||||
|
||||
const number_format style::get_number_format() const
|
||||
style::style() : name_("style"), format_id_(0), builtin_id_(0), hidden_(false)
|
||||
{
|
||||
return number_format_;
|
||||
}
|
||||
|
||||
const border style::get_border() const
|
||||
style::style(const style &other) : name_(other.name_), format_id_(other.format_id_), builtin_id_(other.builtin_id_), hidden_(other.hidden_)
|
||||
{
|
||||
return border_;
|
||||
}
|
||||
|
||||
const alignment style::get_alignment() const
|
||||
style &style::operator=(const xlnt::style &other)
|
||||
{
|
||||
return alignment_;
|
||||
name_ = other.name_;
|
||||
builtin_id_ = other.builtin_id_;
|
||||
format_id_ = other.format_id_;
|
||||
hidden_ = other.hidden_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const fill style::get_fill() const
|
||||
std::string style::get_name() const
|
||||
{
|
||||
return fill_;
|
||||
return name_;
|
||||
}
|
||||
|
||||
const font style::get_font() const
|
||||
void style::set_name(const std::string &name)
|
||||
{
|
||||
return font_;
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
const protection style::get_protection() const
|
||||
std::size_t style::get_format_id() const
|
||||
{
|
||||
return protection_;
|
||||
return format_id_;
|
||||
}
|
||||
|
||||
bool style::pivot_button() const
|
||||
void style::set_format_id(std::size_t format_id)
|
||||
{
|
||||
return pivot_button_;
|
||||
format_id_ = format_id;
|
||||
}
|
||||
|
||||
bool style::quote_prefix() const
|
||||
std::size_t style::get_builtin_id() const
|
||||
{
|
||||
return quote_prefix_;
|
||||
return builtin_id_;
|
||||
}
|
||||
|
||||
std::size_t style::get_id() const
|
||||
void style::set_builtin_id(std::size_t builtin_id)
|
||||
{
|
||||
return id_;
|
||||
builtin_id_ = builtin_id;
|
||||
}
|
||||
|
||||
std::size_t style::get_fill_id() const
|
||||
void style::set_hidden(bool hidden)
|
||||
{
|
||||
return fill_id_;
|
||||
hidden_ = hidden;
|
||||
}
|
||||
|
||||
std::size_t style::get_font_id() const
|
||||
bool style::get_hidden() const
|
||||
{
|
||||
return font_id_;
|
||||
}
|
||||
|
||||
std::size_t style::get_border_id() const
|
||||
{
|
||||
return border_id_;
|
||||
}
|
||||
|
||||
std::size_t style::get_number_format_id() const
|
||||
{
|
||||
return number_format_id_;
|
||||
}
|
||||
|
||||
void style::apply_alignment(bool apply)
|
||||
{
|
||||
alignment_apply_ = apply;
|
||||
}
|
||||
|
||||
void style::apply_border(bool apply)
|
||||
{
|
||||
border_apply_ = apply;
|
||||
}
|
||||
|
||||
void style::apply_fill(bool apply)
|
||||
{
|
||||
fill_apply_ = apply;
|
||||
}
|
||||
|
||||
void style::apply_font(bool apply)
|
||||
{
|
||||
font_apply_ = apply;
|
||||
}
|
||||
|
||||
void style::apply_number_format(bool apply)
|
||||
{
|
||||
number_format_apply_ = apply;
|
||||
}
|
||||
|
||||
void style::apply_protection(bool apply)
|
||||
{
|
||||
protection_apply_ = apply;
|
||||
return hidden_;
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
|
@ -629,6 +630,22 @@ std::vector<named_range> workbook::get_named_ranges() const
|
|||
return named_ranges;
|
||||
}
|
||||
|
||||
std::size_t workbook::add_cell_style_format(const xlnt::format &format_)
|
||||
{
|
||||
d_->cell_style_formats_.push_back(format_);
|
||||
d_->cell_style_formats_.back().id_ = d_->cell_style_formats_.size() - 1;
|
||||
|
||||
return d_->cell_style_formats_.back().id_;
|
||||
}
|
||||
|
||||
std::size_t workbook::add_cell_format(const xlnt::format &format_)
|
||||
{
|
||||
d_->cell_formats_.push_back(format_);
|
||||
d_->cell_formats_.back().id_ = d_->cell_formats_.size() - 1;
|
||||
|
||||
return d_->cell_formats_.back().id_;
|
||||
}
|
||||
|
||||
std::size_t workbook::add_style(const xlnt::style &style_)
|
||||
{
|
||||
d_->styles_.push_back(style_);
|
||||
|
@ -661,9 +678,9 @@ color workbook::get_indexed_color(const color &indexed_color) const
|
|||
return d_->colors_.at(indexed_color.get_index());
|
||||
}
|
||||
|
||||
const number_format &workbook::get_number_format(std::size_t style_id) const
|
||||
const number_format &workbook::get_number_format(std::size_t format_id) const
|
||||
{
|
||||
auto number_format_id = d_->styles_[style_id].number_format_id_;
|
||||
auto number_format_id = d_->cell_formats_[format_id].number_format_id_;
|
||||
|
||||
for (const auto &number_format_ : d_->number_formats_)
|
||||
{
|
||||
|
@ -684,7 +701,7 @@ const font &workbook::get_font(std::size_t font_id) const
|
|||
return d_->fonts_[font_id];
|
||||
}
|
||||
|
||||
std::size_t workbook::set_font(const font &font_, std::size_t style_id)
|
||||
std::size_t workbook::set_font(const font &font_, std::size_t format_id)
|
||||
{
|
||||
auto match = std::find(d_->fonts_.begin(), d_->fonts_.end(), font_);
|
||||
std::size_t font_id = 0;
|
||||
|
@ -699,66 +716,66 @@ std::size_t workbook::set_font(const font &font_, std::size_t style_id)
|
|||
font_id = match - d_->fonts_.begin();
|
||||
}
|
||||
|
||||
if (d_->styles_.empty())
|
||||
if (d_->cell_formats_.empty())
|
||||
{
|
||||
style new_style;
|
||||
format new_format;
|
||||
|
||||
new_style.id_ = 0;
|
||||
new_style.border_id_ = 0;
|
||||
new_style.fill_id_ = 0;
|
||||
new_style.font_id_ = font_id;
|
||||
new_style.font_apply_ = true;
|
||||
new_style.number_format_id_ = 0;
|
||||
new_format.id_ = 0;
|
||||
new_format.border_id_ = 0;
|
||||
new_format.fill_id_ = 0;
|
||||
new_format.font_id_ = font_id;
|
||||
new_format.font_apply_ = true;
|
||||
new_format.number_format_id_ = 0;
|
||||
|
||||
if (d_->borders_.empty())
|
||||
{
|
||||
d_->borders_.push_back(new_style.get_border());
|
||||
d_->borders_.push_back(new_format.get_border());
|
||||
}
|
||||
|
||||
if (d_->fills_.empty())
|
||||
{
|
||||
d_->fills_.push_back(new_style.get_fill());
|
||||
d_->fills_.push_back(new_format.get_fill());
|
||||
}
|
||||
|
||||
if (d_->number_formats_.empty())
|
||||
{
|
||||
d_->number_formats_.push_back(new_style.get_number_format());
|
||||
d_->number_formats_.push_back(new_format.get_number_format());
|
||||
}
|
||||
|
||||
d_->styles_.push_back(new_style);
|
||||
d_->cell_formats_.push_back(new_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the style is unchanged, just return it.
|
||||
auto &existing_style = d_->styles_[style_id];
|
||||
auto &existing_style = d_->cell_formats_[format_id];
|
||||
existing_style.font_apply_ = true;
|
||||
|
||||
if (font_id == existing_style.font_id_)
|
||||
{
|
||||
// no change
|
||||
return style_id;
|
||||
return format_id;
|
||||
}
|
||||
|
||||
// Make a new style with this format.
|
||||
auto new_style = existing_style;
|
||||
auto new_format = existing_style;
|
||||
|
||||
new_style.font_id_ = font_id;
|
||||
new_style.font_ = font_;
|
||||
new_format.font_id_ = font_id;
|
||||
new_format.font_ = font_;
|
||||
|
||||
// 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);
|
||||
auto format_match = std::find(d_->cell_formats_.begin(), d_->cell_formats_.end(), new_format);
|
||||
|
||||
if (style_match != d_->styles_.end())
|
||||
if (format_match != d_->cell_formats_.end())
|
||||
{
|
||||
return style_match->get_id();
|
||||
return format_match->get_id();
|
||||
}
|
||||
|
||||
// No match found, so add it.
|
||||
new_style.id_ = d_->styles_.size();
|
||||
d_->styles_.push_back(new_style);
|
||||
new_format.id_ = d_->cell_formats_.size();
|
||||
d_->cell_formats_.push_back(new_format);
|
||||
|
||||
return new_style.id_;
|
||||
return new_format.id_;
|
||||
}
|
||||
|
||||
const fill &workbook::get_fill(std::size_t fill_id) const
|
||||
|
@ -766,9 +783,9 @@ const fill &workbook::get_fill(std::size_t fill_id) const
|
|||
return d_->fills_[fill_id];
|
||||
}
|
||||
|
||||
std::size_t workbook::set_fill(const fill & /*fill_*/, std::size_t style_id)
|
||||
std::size_t workbook::set_fill(const fill & /*fill_*/, std::size_t format_id)
|
||||
{
|
||||
return style_id;
|
||||
return format_id;
|
||||
}
|
||||
|
||||
const border &workbook::get_border(std::size_t border_id) const
|
||||
|
@ -776,52 +793,52 @@ const border &workbook::get_border(std::size_t border_id) const
|
|||
return d_->borders_[border_id];
|
||||
}
|
||||
|
||||
std::size_t workbook::set_border(const border & /*border_*/, std::size_t style_id)
|
||||
std::size_t workbook::set_border(const border & /*border_*/, std::size_t format_id)
|
||||
{
|
||||
return style_id;
|
||||
return format_id;
|
||||
}
|
||||
|
||||
const alignment &workbook::get_alignment(std::size_t style_id) const
|
||||
const alignment &workbook::get_alignment(std::size_t format_id) const
|
||||
{
|
||||
return d_->styles_[style_id].alignment_;
|
||||
return d_->cell_formats_[format_id].alignment_;
|
||||
}
|
||||
|
||||
std::size_t workbook::set_alignment(const alignment & /*alignment_*/, std::size_t style_id)
|
||||
std::size_t workbook::set_alignment(const alignment & /*alignment_*/, std::size_t format_id)
|
||||
{
|
||||
return style_id;
|
||||
return format_id;
|
||||
}
|
||||
|
||||
const protection &workbook::get_protection(std::size_t style_id) const
|
||||
const protection &workbook::get_protection(std::size_t format_id) const
|
||||
{
|
||||
return d_->styles_[style_id].protection_;
|
||||
return d_->cell_formats_[format_id].protection_;
|
||||
}
|
||||
|
||||
std::size_t workbook::set_protection(const protection & /*protection_*/, std::size_t style_id)
|
||||
std::size_t workbook::set_protection(const protection & /*protection_*/, std::size_t format_id)
|
||||
{
|
||||
return style_id;
|
||||
return format_id;
|
||||
}
|
||||
|
||||
bool workbook::get_pivot_button(std::size_t style_id) const
|
||||
bool workbook::get_pivot_button(std::size_t format_id) const
|
||||
{
|
||||
return d_->styles_[style_id].pivot_button_;
|
||||
return d_->cell_formats_[format_id].pivot_button_;
|
||||
}
|
||||
|
||||
bool workbook::get_quote_prefix(std::size_t style_id) const
|
||||
bool workbook::get_quote_prefix(std::size_t format_id) const
|
||||
{
|
||||
return d_->styles_[style_id].quote_prefix_;
|
||||
return d_->cell_formats_[format_id].quote_prefix_;
|
||||
}
|
||||
|
||||
//TODO: this is terrible!
|
||||
std::size_t workbook::set_number_format(const xlnt::number_format &format, std::size_t style_id)
|
||||
std::size_t workbook::set_number_format(const xlnt::number_format &nf, std::size_t style_id)
|
||||
{
|
||||
auto match = std::find(d_->number_formats_.begin(), d_->number_formats_.end(), format);
|
||||
auto match = std::find(d_->number_formats_.begin(), d_->number_formats_.end(), nf);
|
||||
std::size_t format_id = 0;
|
||||
|
||||
if (match == d_->number_formats_.end())
|
||||
{
|
||||
d_->number_formats_.push_back(format);
|
||||
d_->number_formats_.push_back(nf);
|
||||
|
||||
if (!format.has_id())
|
||||
if (!nf.has_id())
|
||||
{
|
||||
d_->number_formats_.back().set_id(d_->next_custom_format_id_++);
|
||||
}
|
||||
|
@ -833,39 +850,39 @@ std::size_t workbook::set_number_format(const xlnt::number_format &format, std::
|
|||
format_id = match->get_id();
|
||||
}
|
||||
|
||||
if (d_->styles_.empty())
|
||||
if (d_->cell_formats_.empty())
|
||||
{
|
||||
style new_style;
|
||||
format new_format;
|
||||
|
||||
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;
|
||||
new_format.id_ = 0;
|
||||
new_format.border_id_ = 0;
|
||||
new_format.fill_id_ = 0;
|
||||
new_format.font_id_ = 0;
|
||||
new_format.number_format_id_ = format_id;
|
||||
new_format.number_format_apply_ = true;
|
||||
|
||||
if (d_->borders_.empty())
|
||||
{
|
||||
d_->borders_.push_back(new_style.get_border());
|
||||
d_->borders_.push_back(new_format.get_border());
|
||||
}
|
||||
|
||||
if (d_->fills_.empty())
|
||||
{
|
||||
d_->fills_.push_back(new_style.get_fill());
|
||||
d_->fills_.push_back(new_format.get_fill());
|
||||
}
|
||||
|
||||
if (d_->fonts_.empty())
|
||||
{
|
||||
d_->fonts_.push_back(new_style.get_font());
|
||||
d_->fonts_.push_back(new_format.get_font());
|
||||
}
|
||||
|
||||
d_->styles_.push_back(new_style);
|
||||
d_->cell_formats_.push_back(new_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the style is unchanged, just return it.
|
||||
auto existing_style = d_->styles_[style_id];
|
||||
auto existing_style = d_->cell_formats_[format_id];
|
||||
existing_style.number_format_apply_ = true;
|
||||
|
||||
if (format_id == existing_style.number_format_id_)
|
||||
|
@ -875,24 +892,34 @@ std::size_t workbook::set_number_format(const xlnt::number_format &format, std::
|
|||
}
|
||||
|
||||
// Make a new style with this format.
|
||||
auto new_style = existing_style;
|
||||
auto new_format = existing_style;
|
||||
|
||||
new_style.number_format_id_ = format_id;
|
||||
new_style.number_format_ = format;
|
||||
new_format.number_format_id_ = format_id;
|
||||
new_format.number_format_ = nf;
|
||||
|
||||
// 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);
|
||||
auto format_match = std::find(d_->cell_formats_.begin(), d_->cell_formats_.end(), new_format);
|
||||
|
||||
if (style_match != d_->styles_.end())
|
||||
if (format_match != d_->cell_formats_.end())
|
||||
{
|
||||
return style_match->get_id();
|
||||
return format_match->get_id();
|
||||
}
|
||||
|
||||
// No match found, so add it.
|
||||
new_style.id_ = d_->styles_.size();
|
||||
d_->styles_.push_back(new_style);
|
||||
new_format.id_ = d_->cell_formats_.size();
|
||||
d_->cell_formats_.push_back(new_format);
|
||||
|
||||
return new_style.id_;
|
||||
return new_format.id_;
|
||||
}
|
||||
|
||||
const std::vector<format> &workbook::get_cell_formats() const
|
||||
{
|
||||
return d_->cell_formats_;
|
||||
}
|
||||
|
||||
const std::vector<format> &workbook::get_cell_style_formats() const
|
||||
{
|
||||
return d_->cell_style_formats_;
|
||||
}
|
||||
|
||||
const std::vector<style> &workbook::get_styles() const
|
||||
|
@ -985,4 +1012,14 @@ bool workbook::contains(const std::string &sheet_title) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void workbook::set_thumbnail(const std::vector<std::uint8_t> &thumbnail)
|
||||
{
|
||||
d_->thumbnail_.assign(thumbnail.begin(), thumbnail.end());
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t> &workbook::get_thumbnail() const
|
||||
{
|
||||
return d_->thumbnail_;
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
class test_worksheet : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_roundtrip()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load("/Users/thomas/Development/xlnt/tests/data/complex-styles.xlsx");
|
||||
wb.save("/Users/thomas/Desktop/comparison_temp/out.zip");
|
||||
}
|
||||
|
||||
void test_new_worksheet()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
|
|
Loading…
Reference in New Issue
Block a user