implement reading of cell comments

This commit is contained in:
Thomas Fussell 2016-10-29 10:23:04 -04:00
parent 67174a9140
commit 82311c96e4
29 changed files with 430 additions and 250 deletions

View File

@ -435,6 +435,28 @@ public:
/// </summary> /// </summary>
std::string check_string(const std::string &to_check); std::string check_string(const std::string &to_check);
// comment
/// <summary>
/// Return true if this cell has a comment applied.
/// </summary>
bool has_comment();
/// <summary>
/// Delete the comment applied to this cell if it exists.
/// </summary>
void clear_comment();
/// <summary>
/// Get the comment applied to this cell.
/// </summary>
comment comment();
/// <summary>
/// Apply the comment provided as the ony argument to the cell.
/// </summary>
void comment(const class comment &new_comment);
// operators // operators
/// <summary> /// <summary>

View File

@ -1,5 +1,4 @@
// Copyright (c) 2014-2016 Thomas Fussell // Copyright (c) 2014-2016 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -26,64 +25,54 @@
#include <string> #include <string>
#include <xlnt/xlnt_config.hpp> #include <xlnt/xlnt_config.hpp>
#include <xlnt/cell/formatted_text.hpp>
namespace xlnt { namespace xlnt {
class cell;
namespace detail {
struct comment_impl;
}
/// <summary> /// <summary>
/// A comment can be applied to a cell to provide extra information. /// A comment can be applied to a cell to provide extra information about its contents.
/// </summary> /// </summary>
class XLNT_API comment class XLNT_API comment
{ {
public: public:
/// <summary> /// <summary>
/// The default constructor makes an invalid comment without a parent cell. /// Constructs a new blank comment.
/// </summary> /// </summary>
comment(); comment();
/// <summary> /// <summary>
/// Constructs a comment applied to the given cell, parent, and with the comment /// Constructs a new comment with the given text and author.
/// text and author set to the provided respective values. /// </summary>
comment(cell parent, const std::string &text, const std::string &auth); comment(const formatted_text &text, const std::string &author);
~comment(); /// <summary>
/// Constructs a new comment with the given unformatted text and author.
/// </summary>
comment(const std::string &text, const std::string &author);
/// <summary> /// <summary>
/// Return the text that will be displayed for this comment. /// Return the text that will be displayed for this comment.
/// </summary> /// </summary>
std::string get_text() const; formatted_text text() const;
/// <summary>
/// Return the plain text that will be displayed for this comment without formatting information.
/// </summary>
std::string plain_text() const;
/// <summary> /// <summary>
/// Return the author of this comment. /// Return the author of this comment.
/// </summary> /// </summary>
std::string get_author() const; std::string author() const;
/// <summary> /// <summary>
/// True if the comments point to the same sell (false if /// Return true if both comments are equivalent.
/// they are different cells but identical comments). Note
/// that a cell can only have one comment and a comment
/// can only be applied to one cell.
/// </summary> /// </summary>
bool operator==(const comment &other) const; friend bool operator==(const comment &left, const comment &right);
private: private:
friend class cell; // cell needs access to private constructor formatted_text text_;
std::string author_;
/// <summary>
/// Construct a comment from an implementation of a comment.
/// </summary>
comment(detail::comment_impl *d);
/// <summary>
/// Pointer to the implementation of this comment.
/// This allows comments to be passed by value while
/// retaining the ability to modify the parent cell.
/// </summary>
detail::comment_impl *d_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -25,27 +25,27 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <xlnt/xlnt_config.hpp> // for XLNT_API, XLNT_API #include <xlnt/xlnt_config.hpp>
#include <xlnt/cell/text_run.hpp> #include <xlnt/cell/text_run.hpp>
namespace xlnt { namespace xlnt {
class text_run; class XLNT_API formatted_text
class XLNT_API text
{ {
public: public:
void clear(); void clear();
void set_plain_string(const std::string &s);
std::string get_plain_string() const;
std::vector<text_run> get_runs() const;
void add_run(const text_run &t);
void set_run(const std::vector<text_run> &parts);
bool operator==(const text &rhs) const; void plain_text(const std::string &s);
std::string plain_text() const;
std::vector<text_run> runs() const;
void runs(const std::vector<text_run> &new_runs);
void add_run(const text_run &t);
bool operator==(const formatted_text &rhs) const;
private: private:
std::vector<text_run> runs_; std::vector<text_run> runs_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -24,7 +24,8 @@
#include <string> #include <string>
#include <xlnt/xlnt_config.hpp> // for XLNT_API, XLNT_API #include <xlnt/xlnt_config.hpp>
#include <xlnt/styles/color.hpp>
#include <xlnt/utils/optional.hpp> #include <xlnt/utils/optional.hpp>
namespace xlnt { namespace xlnt {
@ -45,8 +46,8 @@ public:
void set_size(std::size_t size); void set_size(std::size_t size);
bool has_color() const; bool has_color() const;
std::string get_color() const; color get_color() const;
void set_color(const std::string &color); void set_color(const color &new_color);
bool has_font() const; bool has_font() const;
std::string get_font() const; std::string get_font() const;
@ -60,14 +61,19 @@ public:
std::string get_scheme() const; std::string get_scheme() const;
void set_scheme(const std::string &scheme); void set_scheme(const std::string &scheme);
bool bold_set() const;
bool is_bold() const;
void set_bold(bool bold);
private: private:
std::string string_; std::string string_;
optional<std::size_t> size_; optional<std::size_t> size_;
optional<std::string> color_; optional<color> color_;
optional<std::string> font_; optional<std::string> font_;
optional<std::size_t> family_; optional<std::size_t> family_;
optional<std::string> scheme_; optional<std::string> scheme_;
optional<bool> bold_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -85,6 +85,7 @@ enum class XLNT_API relationship_type
single_cell_table_definitions, single_cell_table_definitions,
styles, styles,
table_definition, table_definition,
vml_drawing,
volatile_dependencies, volatile_dependencies,
worksheet, worksheet,

View File

@ -133,6 +133,9 @@ public:
void set_tint(double tint); void set_tint(double tint);
bool operator==(const color &other) const;
bool operator!=(const color &other) const { return !(*this == other); }
protected: protected:
std::string to_hash_string() const override; std::string to_hash_string() const override;

View File

@ -45,6 +45,7 @@ class drawing;
class fill; class fill;
class font; class font;
class format; class format;
class formatted_text;
class manifest; class manifest;
class named_range; class named_range;
class number_format; class number_format;
@ -56,7 +57,6 @@ class range_reference;
class relationship; class relationship;
class style; class style;
class style_serializer; class style_serializer;
class text;
class theme; class theme;
class workbook_view; class workbook_view;
class worksheet; class worksheet;
@ -429,9 +429,9 @@ public:
// shared strings // shared strings
void add_shared_string(const text &shared, bool allow_duplicates=false); void add_shared_string(const formatted_text &shared, bool allow_duplicates=false);
std::vector<text> &get_shared_strings(); std::vector<formatted_text> &get_shared_strings();
const std::vector<text> &get_shared_strings() const; const std::vector<formatted_text> &get_shared_strings() const;
// thumbnail // thumbnail
@ -470,30 +470,30 @@ public:
bool operator!=(const workbook &rhs) const; bool operator!=(const workbook &rhs) const;
private: private:
friend class worksheet; friend class worksheet;
friend class detail::xlsx_consumer; friend class detail::xlsx_consumer;
friend class detail::xlsx_producer; friend class detail::xlsx_producer;
workbook(detail::workbook_impl *impl); workbook(detail::workbook_impl *impl);
detail::workbook_impl &impl(); detail::workbook_impl &impl();
const detail::workbook_impl &impl() const; const detail::workbook_impl &impl() const;
/// <summary> /// <summary>
/// Apply the function "f" to every cell in every worksheet in this workbook. /// Apply the function "f" to every cell in every worksheet in this workbook.
/// </summary> /// </summary>
void apply_to_cells(std::function<void(cell)> f); void apply_to_cells(std::function<void(cell)> f);
void register_app_properties_in_manifest(); void register_app_properties_in_manifest();
void register_core_properties_in_manifest(); void register_core_properties_in_manifest();
void register_shared_string_table_in_manifest(); void register_shared_string_table_in_manifest();
void register_stylesheet_in_manifest(); void register_stylesheet_in_manifest();
void register_theme_in_manifest(); void register_theme_in_manifest();
/// <summary> /// <summary>
/// An opaque pointer to a structure that holds all of the data relating to this workbook. /// An opaque pointer to a structure that holds all of the data relating to this workbook.

View File

@ -30,8 +30,8 @@
#include <xlnt/cell/cell_reference.hpp> #include <xlnt/cell/cell_reference.hpp>
#include <xlnt/cell/cell_type.hpp> #include <xlnt/cell/cell_type.hpp>
#include <xlnt/cell/comment.hpp> #include <xlnt/cell/comment.hpp>
#include <xlnt/cell/formatted_text.hpp>
#include <xlnt/cell/index_types.hpp> #include <xlnt/cell/index_types.hpp>
#include <xlnt/cell/text.hpp>
#include <xlnt/cell/text_run.hpp> #include <xlnt/cell/text_run.hpp>
// packaging // packaging

View File

@ -28,7 +28,7 @@
#include <xlnt/cell/cell.hpp> #include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp> #include <xlnt/cell/cell_reference.hpp>
#include <xlnt/cell/comment.hpp> #include <xlnt/cell/comment.hpp>
#include <xlnt/cell/text.hpp> #include <xlnt/cell/formatted_text.hpp>
#include <xlnt/packaging/relationship.hpp> #include <xlnt/packaging/relationship.hpp>
#include <xlnt/styles/color.hpp> #include <xlnt/styles/color.hpp>
#include <xlnt/styles/format.hpp> #include <xlnt/styles/format.hpp>
@ -44,8 +44,6 @@
#include <xlnt/worksheet/worksheet.hpp> #include <xlnt/worksheet/worksheet.hpp>
#include <detail/cell_impl.hpp> #include <detail/cell_impl.hpp>
#include <detail/comment_impl.hpp>
namespace { namespace {
@ -316,13 +314,13 @@ XLNT_API void cell::set_value(std::string s)
} }
else else
{ {
d_->type_ = type::string; d_->type_ = type::string;
d_->value_text_.set_plain_string(s); d_->value_text_.plain_text(s);
if (s.size() > 0) if (s.size() > 0)
{ {
get_workbook().add_shared_string(d_->value_text_); get_workbook().add_shared_string(d_->value_text_);
} }
} }
if (get_workbook().get_guess_types()) if (get_workbook().get_guess_types())
@ -332,17 +330,17 @@ XLNT_API void cell::set_value(std::string s)
} }
template <> template <>
XLNT_API void cell::set_value(text t) XLNT_API void cell::set_value(formatted_text text)
{ {
if (t.get_runs().size() == 1 && !t.get_runs().front().has_formatting()) if (text.runs().size() == 1 && !text.runs().front().has_formatting())
{ {
set_value(t.get_plain_string()); set_value(text.plain_text());
} }
else else
{ {
d_->type_ = type::string; d_->type_ = type::string;
d_->value_text_ = t; d_->value_text_ = text;
get_workbook().add_shared_string(t); get_workbook().add_shared_string(text);
} }
} }
@ -518,7 +516,7 @@ void cell::set_error(const std::string &error)
throw invalid_data_type(); throw invalid_data_type();
} }
d_->value_text_.set_plain_string(error); d_->value_text_.plain_text(error);
d_->type_ = type::error; d_->type_ = type::error;
} }
@ -804,11 +802,11 @@ void cell::set_protection(const xlnt::protection &protection_)
template <> template <>
XLNT_API std::string cell::get_value() const XLNT_API std::string cell::get_value() const
{ {
return d_->value_text_.get_plain_string(); return d_->value_text_.plain_text();
} }
template <> template <>
XLNT_API text cell::get_value() const XLNT_API formatted_text cell::get_value() const
{ {
return d_->value_text_; return d_->value_text_;
} }
@ -1026,4 +1024,31 @@ bool cell::has_hyperlink() const
return d_->hyperlink_; return d_->hyperlink_;
} }
// comment
bool cell::has_comment()
{
return (bool)d_->comment_;
}
void cell::clear_comment()
{
d_->comment_.clear();
}
comment cell::comment()
{
if (!has_comment())
{
throw xlnt::exception("cell has no comment");
}
return d_->comment_.get();
}
void cell::comment(const class comment &new_comment)
{
d_->comment_.set(new_comment);
}
} // namespace xlnt } // namespace xlnt

View File

@ -21,44 +21,45 @@
// //
// @license: http://www.opensource.org/licenses/mit-license.php // @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file // @author: see AUTHORS file
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/comment.hpp> #include <xlnt/cell/comment.hpp>
#include "detail/comment_impl.hpp"
namespace xlnt { namespace xlnt {
comment::comment(detail::comment_impl *d) : d_(d) comment::comment() : comment("", "")
{ {
} }
comment::comment(cell parent, const std::string &text, const std::string &author) : d_(nullptr) comment::comment(const formatted_text &text, const std::string &author)
{ : text_(text),
/*d_->text_ = text; author_(author)
d_->author_ = author;*/
}
comment::comment() : d_(nullptr)
{ {
} }
comment::~comment() comment::comment(const std::string &text, const std::string &author)
: text_(),
author_(author)
{ {
text_.plain_text(text);
} }
std::string comment::get_author() const formatted_text comment::text() const
{ {
return d_->author_; return text_;
} }
std::string comment::get_text() const std::string comment::plain_text() const
{ {
return d_->text_; return text_.plain_text();
} }
bool comment::operator==(const xlnt::comment &other) const std::string comment::author() const
{ {
return d_ == other.d_; return author_;
}
bool operator==(const comment &left, const comment &right)
{
return left.text_ == right.text_ && left.author_ == right.author_;
} }
} // namespace xlnt } // namespace xlnt

View File

@ -21,46 +21,41 @@
// //
// @license: http://www.opensource.org/licenses/mit-license.php // @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file // @author: see AUTHORS file
#include <numeric>
#include <xlnt/cell/text.hpp> #include <xlnt/cell/formatted_text.hpp>
#include <xlnt/cell/text_run.hpp> #include <xlnt/cell/text_run.hpp>
namespace xlnt { namespace xlnt {
void text::clear() void formatted_text::clear()
{ {
runs_.clear(); runs_.clear();
} }
void text::set_plain_string(const std::string &s) void formatted_text::plain_text(const std::string &s)
{ {
clear(); clear();
add_run(text_run(s)); add_run(text_run(s));
} }
std::string text::get_plain_string() const std::string formatted_text::plain_text() const
{ {
std::string plain_string; return std::accumulate(runs_.begin(), runs_.end(), std::string(),
[](const std::string &a, const text_run &run) { return a + run.get_string(); });
for (const auto &run : runs_)
{
plain_string.append(run.get_string());
}
return plain_string;
} }
std::vector<text_run> text::get_runs() const std::vector<text_run> formatted_text::runs() const
{ {
return runs_; return runs_;
} }
void text::add_run(const text_run &t) void formatted_text::add_run(const text_run &t)
{ {
runs_.push_back(t); runs_.push_back(t);
} }
bool text::operator==(const text &rhs) const bool formatted_text::operator==(const formatted_text &rhs) const
{ {
if (runs_.size() != rhs.runs_.size()) return false; if (runs_.size() != rhs.runs_.size()) return false;

View File

@ -612,4 +612,19 @@ public:
TS_ASSERT(cell.has_hyperlink()); TS_ASSERT(cell.has_hyperlink());
TS_ASSERT_EQUALS(cell.get_hyperlink(), "http://example.com"); TS_ASSERT_EQUALS(cell.get_hyperlink(), "http://example.com");
} }
void test_comment()
{
xlnt::workbook wb;
auto ws = wb.get_active_sheet();
auto cell = ws.get_cell("A1");
TS_ASSERT(!cell.has_comment());
TS_ASSERT_THROWS(cell.comment(), xlnt::exception);
cell.comment(xlnt::comment("comment", "author"));
TS_ASSERT(cell.has_comment());
TS_ASSERT_EQUALS(cell.comment(), xlnt::comment("comment", "author"));
cell.clear_comment();
TS_ASSERT(!cell.has_comment());
TS_ASSERT_THROWS(cell.comment(), xlnt::exception);
}
}; };

View File

@ -7,13 +7,13 @@
#include <xlnt/xlnt.hpp> #include <xlnt/xlnt.hpp>
class test_text : public CxxTest::TestSuite class test_formatted_text : public CxxTest::TestSuite
{ {
public: public:
void test_operators() void test_operators()
{ {
xlnt::text text1; xlnt::formatted_text text1;
xlnt::text text2; xlnt::formatted_text text2;
TS_ASSERT_EQUALS(text1, text2); TS_ASSERT_EQUALS(text1, text2);
xlnt::text_run run_default; xlnt::text_run run_default;
text1.add_run(run_default); text1.add_run(run_default);
@ -22,42 +22,42 @@ public:
TS_ASSERT_EQUALS(text1, text2); TS_ASSERT_EQUALS(text1, text2);
xlnt::text_run run_formatted; xlnt::text_run run_formatted;
run_formatted.set_color("maroon"); run_formatted.set_color(xlnt::color::green());
run_formatted.set_font("Cambria"); run_formatted.set_font("Cambria");
run_formatted.set_scheme("ascheme"); run_formatted.set_scheme("ascheme");
run_formatted.set_size(40); run_formatted.set_size(40);
run_formatted.set_family(17); run_formatted.set_family(17);
xlnt::text text_formatted; xlnt::formatted_text text_formatted;
text_formatted.add_run(run_formatted); text_formatted.add_run(run_formatted);
xlnt::text_run run_color_differs = run_formatted; xlnt::text_run run_color_differs = run_formatted;
run_color_differs.set_color("mauve"); run_color_differs.set_color(xlnt::color::red());
xlnt::text text_color_differs; xlnt::formatted_text text_color_differs;
text_color_differs.add_run(run_color_differs); text_color_differs.add_run(run_color_differs);
TS_ASSERT_DIFFERS(text_formatted, text_color_differs); TS_ASSERT_DIFFERS(text_formatted, text_color_differs);
xlnt::text_run run_font_differs = run_formatted; xlnt::text_run run_font_differs = run_formatted;
run_font_differs.set_font("Calibri"); run_font_differs.set_font("Calibri");
xlnt::text text_font_differs; xlnt::formatted_text text_font_differs;
text_font_differs.add_run(run_font_differs); text_font_differs.add_run(run_font_differs);
TS_ASSERT_DIFFERS(text_formatted, text_font_differs); TS_ASSERT_DIFFERS(text_formatted, text_font_differs);
xlnt::text_run run_scheme_differs = run_formatted; xlnt::text_run run_scheme_differs = run_formatted;
run_scheme_differs.set_scheme("bscheme"); run_scheme_differs.set_scheme("bscheme");
xlnt::text text_scheme_differs; xlnt::formatted_text text_scheme_differs;
text_scheme_differs.add_run(run_scheme_differs); text_scheme_differs.add_run(run_scheme_differs);
TS_ASSERT_DIFFERS(text_formatted, text_scheme_differs); TS_ASSERT_DIFFERS(text_formatted, text_scheme_differs);
xlnt::text_run run_size_differs = run_formatted; xlnt::text_run run_size_differs = run_formatted;
run_size_differs.set_size(41); run_size_differs.set_size(41);
xlnt::text text_size_differs; xlnt::formatted_text text_size_differs;
text_size_differs.add_run(run_size_differs); text_size_differs.add_run(run_size_differs);
TS_ASSERT_DIFFERS(text_formatted, text_size_differs); TS_ASSERT_DIFFERS(text_formatted, text_size_differs);
xlnt::text_run run_family_differs = run_formatted; xlnt::text_run run_family_differs = run_formatted;
run_family_differs.set_family(18); run_family_differs.set_family(18);
xlnt::text text_family_differs; xlnt::formatted_text text_family_differs;
text_family_differs.add_run(run_family_differs); text_family_differs.add_run(run_family_differs);
TS_ASSERT_DIFFERS(text_formatted, text_family_differs); TS_ASSERT_DIFFERS(text_formatted, text_family_differs);
} }

View File

@ -22,6 +22,7 @@
// @author: see AUTHORS file // @author: see AUTHORS file
#include <xlnt/cell/text_run.hpp> #include <xlnt/cell/text_run.hpp>
#include <xlnt/styles/color.hpp>
namespace xlnt { namespace xlnt {
@ -68,14 +69,14 @@ bool text_run::has_color() const
return (bool)color_; return (bool)color_;
} }
std::string text_run::get_color() const color text_run::get_color() const
{ {
return *color_; return *color_;
} }
void text_run::set_color(const std::string &color) void text_run::set_color(const color &new_color)
{ {
color_ = color; color_ = new_color;
} }
bool text_run::has_font() const bool text_run::has_font() const
@ -123,4 +124,19 @@ void text_run::set_scheme(const std::string &scheme)
scheme_ = scheme; scheme_ = scheme;
} }
bool text_run::bold_set() const
{
return (bool)bold_;
}
bool text_run::is_bold() const
{
return *bold_;
}
void text_run::set_bold(bool bold)
{
bold_ = bold;
}
} // namespace xlnt } // namespace xlnt

View File

@ -24,10 +24,19 @@
#include <xlnt/worksheet/worksheet.hpp> #include <xlnt/worksheet/worksheet.hpp>
#include "cell_impl.hpp" #include "cell_impl.hpp"
#include "comment_impl.hpp"
namespace xlnt { namespace xlnt {
namespace detail { namespace detail {
cell_impl::cell_impl()
: type_(cell_type::null),
parent_(nullptr),
column_(1),
row_(1),
is_merged_(false),
value_numeric_(0)
{
}
} // namespace detail } // namespace detail
} // namespace xlnt } // namespace xlnt

View File

@ -26,19 +26,20 @@
#include <string> #include <string>
#include <xlnt/cell/cell_type.hpp> #include <xlnt/cell/cell_type.hpp>
#include <xlnt/cell/comment.hpp>
#include <xlnt/cell/formatted_text.hpp>
#include <xlnt/cell/index_types.hpp> #include <xlnt/cell/index_types.hpp>
#include <xlnt/cell/text.hpp>
#include <xlnt/utils/optional.hpp> #include <xlnt/utils/optional.hpp>
namespace xlnt { namespace xlnt {
namespace detail { namespace detail {
struct comment_impl;
struct worksheet_impl; struct worksheet_impl;
struct cell_impl struct cell_impl
{ {
cell_impl();
cell_type type_; cell_type type_;
worksheet_impl *parent_; worksheet_impl *parent_;
@ -46,18 +47,16 @@ struct cell_impl
column_t column_; column_t column_;
row_t row_; row_t row_;
bool is_merged_; bool is_merged_;
text value_text_; formatted_text value_text_;
long double value_numeric_; long double value_numeric_;
optional<std::string> formula_; optional<std::string> formula_;
optional<std::string> hyperlink_; optional<std::string> hyperlink_;
optional<std::size_t> format_id_; optional<std::size_t> format_id_;
optional<std::string> style_name_; optional<std::string> style_name_;
optional<comment> comment_;
}; };
} // namespace detail } // namespace detail

View File

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

View File

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

View File

@ -49,6 +49,10 @@ std::string to_string(relationship::type t)
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
case relationship::type::chartsheet: case relationship::type::chartsheet:
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"; return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet";
case relationship::type::comments:
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
case relationship::type::vml_drawing:
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
default: default:
return default_case("?"); return default_case("?");
} }

View File

@ -124,6 +124,7 @@ struct value_traits<xlnt::relationship_type>
relationship_type::theme, relationship_type::theme,
relationship_type::thumbnail, relationship_type::thumbnail,
relationship_type::unknown, relationship_type::unknown,
relationship_type::vml_drawing,
relationship_type::volatile_dependencies, relationship_type::volatile_dependencies,
relationship_type::worksheet relationship_type::worksheet
}; };

View File

@ -179,7 +179,7 @@ struct workbook_impl
std::size_t active_sheet_index_; std::size_t active_sheet_index_;
std::list<worksheet_impl> worksheets_; std::list<worksheet_impl> worksheets_;
std::vector<text> shared_strings_; std::vector<formatted_text> shared_strings_;
bool guess_types_; bool guess_types_;
bool data_only_; bool data_only_;

View File

@ -21,6 +21,7 @@
// @license: http://www.opensource.org/licenses/mit-license.php // @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file // @author: see AUTHORS file
#include <cctype> #include <cctype>
#include <numeric> // for std::accumulate
#include <detail/xlsx_consumer.hpp> #include <detail/xlsx_consumer.hpp>
#include <detail/constants.hpp> #include <detail/constants.hpp>
@ -532,7 +533,7 @@ void xlsx_consumer::read_workbook()
static const auto xmlns_mx = constants::get_namespace("mx"); static const auto xmlns_mx = constants::get_namespace("mx");
static const auto xmlns_r = constants::get_namespace("r"); static const auto xmlns_r = constants::get_namespace("r");
static const auto xmlns_s = constants::get_namespace("worksheet"); static const auto xmlns_s = constants::get_namespace("worksheet");
static const auto xmlns_x15 = constants::get_namespace("x15"); static const auto xmlns_x15 = constants::get_namespace("x15");
static const auto xmlns_x15ac = constants::get_namespace("x15ac"); static const auto xmlns_x15ac = constants::get_namespace("x15ac");
parser().next_expect(xml::parser::event_type::start_element, xmlns, "workbook"); parser().next_expect(xml::parser::event_type::start_element, xmlns, "workbook");
@ -844,14 +845,14 @@ void xlsx_consumer::read_shared_string_table()
parser().content(xml::content::complex); parser().content(xml::content::complex);
parser().next_expect(xml::parser::event_type::start_element); parser().next_expect(xml::parser::event_type::start_element);
text t; formatted_text t;
parser().attribute_map(); parser().attribute_map();
if (parser().name() == "t") if (parser().name() == "t")
{ {
parser().next_expect(xml::parser::event_type::characters); parser().next_expect(xml::parser::event_type::characters);
t.set_plain_string(parser().value()); t.plain_text(parser().value());
} }
else if (parser().name() == "r") // possible multiple text entities. else if (parser().name() == "r") // possible multiple text entities.
{ {
@ -884,7 +885,7 @@ void xlsx_consumer::read_shared_string_table()
} }
else if (parser().qname() == xml::qname(xmlns, "color")) else if (parser().qname() == xml::qname(xmlns, "color"))
{ {
run.set_color(parser().attribute("rgb")); run.set_color(read_color(parser()));
} }
else if (parser().qname() == xml::qname(xmlns, "family")) else if (parser().qname() == xml::qname(xmlns, "family"))
{ {
@ -1589,6 +1590,7 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
{ {
static const auto xmlns = constants::get_namespace("worksheet"); static const auto xmlns = constants::get_namespace("worksheet");
static const auto xmlns_mc = constants::get_namespace("mc"); static const auto xmlns_mc = constants::get_namespace("mc");
static const auto xmlns_r = constants::get_namespace("r");
static const auto xmlns_x14ac = constants::get_namespace("x14ac"); static const auto xmlns_x14ac = constants::get_namespace("x14ac");
auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(), auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
@ -1984,15 +1986,164 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
parser().next(); parser().next();
} }
else if (parser().qname() == xml::qname(xmlns, "legacyDrawing"))
{
parser().attribute(xml::qname(xmlns_r, "id"));
parser().next_expect(xml::parser::event_type::end_element, xmlns, "legacyDrawing");
}
} }
parser().next_expect(xml::parser::event_type::end_element, xmlns, "worksheet"); parser().next_expect(xml::parser::event_type::end_element, xmlns, "worksheet");
auto &manifest = target_.get_manifest();
const auto workbook_rel = manifest.get_relationship(path("/"), relationship::type::office_document);
const auto sheet_rel = manifest.get_relationship(workbook_rel.get_target().get_path(), rel_id);
path sheet_path(sheet_rel.get_source().get_path().parent().append(sheet_rel.get_target().get_path()));
for (const auto &rel : manifest.get_relationships(sheet_path))
{
path part_path(sheet_path.parent().append(rel.get_target().get_path()));
auto split_part_path = part_path.split();
auto part_path_iter = split_part_path.begin();
while (part_path_iter != split_part_path.end())
{
if (*part_path_iter == "..")
{
part_path_iter = split_part_path.erase(part_path_iter - 1, part_path_iter + 1);
continue;
}
++part_path_iter;
}
part_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""),
[](const path &a, const std::string &b) { return a.append(b); });
std::istringstream parser_stream(source_.read(part_path));
auto receive = xml::parser::receive_default;
xml::parser parser(parser_stream, rel.get_target().get_path().string(), receive);
parser_ = &parser;
switch (rel.get_type())
{
case relationship::type::comments:
read_comments(ws);
break;
default:
break;
}
parser_ = nullptr;
}
} }
// Sheet Relationship Target Parts // Sheet Relationship Target Parts
void xlsx_consumer::read_comments() void xlsx_consumer::read_comments(worksheet ws)
{ {
static const auto xmlns = xlnt::constants::get_namespace("worksheet");
std::vector<std::string> authors;
parser().next_expect(xml::parser::event_type::start_element, xmlns, "comments");
parser().next_expect(xml::parser::event_type::start_element, xmlns, "authors");
for (;;)
{
if (parser().peek() == xml::parser::event_type::end_element) break;
parser().next_expect(xml::parser::event_type::start_element, xmlns, "author");
parser().next_expect(xml::parser::event_type::characters);
authors.push_back(parser().value());
parser().next_expect(xml::parser::event_type::end_element, xmlns, "author");
}
parser().next_expect(xml::parser::event_type::end_element, xmlns, "authors");
parser().next_expect(xml::parser::event_type::start_element, xmlns, "commentList");
for (;;)
{
if (parser().peek() == xml::parser::event_type::end_element) break;
parser().next_expect(xml::parser::event_type::start_element, xmlns, "comment");
auto cell_ref = parser().attribute("ref");
auto author_id = parser().attribute<std::size_t>("authorId");
parser().next_expect(xml::parser::event_type::start_element, xmlns, "text");
// todo: this is duplicated from shared strings
formatted_text text;
for (;;)
{
if (parser().peek() == xml::parser::event_type::end_element) break;
parser().next_expect(xml::parser::event_type::start_element, xmlns, "r");
text_run run;
for (;;)
{
if (parser().peek() == xml::parser::event_type::end_element) break;
parser().next_expect(xml::parser::event_type::start_element);
if (parser().name() == "t")
{
parser().next_expect(xml::parser::event_type::characters);
run.set_string(parser().value());
parser().next_expect(xml::parser::event_type::end_element, xmlns, "t");
}
else if (parser().name() == "rPr")
{
for (;;)
{
if (parser().peek() == xml::parser::event_type::end_element) break;
parser().next_expect(xml::parser::event_type::start_element);
if (parser().qname() == xml::qname(xmlns, "sz"))
{
run.set_size(string_to_size_t(parser().attribute("val")));
}
else if (parser().qname() == xml::qname(xmlns, "rFont"))
{
run.set_font(parser().attribute("val"));
}
else if (parser().qname() == xml::qname(xmlns, "color"))
{
run.set_color(read_color(parser()));
}
else if (parser().qname() == xml::qname(xmlns, "family"))
{
run.set_family(string_to_size_t(parser().attribute("val")));
}
else if (parser().qname() == xml::qname(xmlns, "scheme"))
{
run.set_scheme(parser().attribute("val"));
}
else if (parser().qname() == xml::qname(xmlns, "b"))
{
run.set_bold(true);
}
parser().next_expect(xml::parser::event_type::end_element);
}
parser().next_expect(xml::parser::event_type::end_element, xmlns, "rPr");
}
}
text.add_run(run);
parser().next_expect(xml::parser::event_type::end_element, xmlns, "r");
}
ws.get_cell(cell_ref).comment(comment(text, authors.at(author_id)));
parser().next_expect(xml::parser::event_type::end_element, xmlns, "text");
parser().next_expect(xml::parser::event_type::end_element, xmlns, "comment");
}
parser().next_expect(xml::parser::event_type::end_element, xmlns, "commentList");
parser().next_expect(xml::parser::event_type::end_element, xmlns, "comments");
} }
void xlsx_consumer::read_drawings() void xlsx_consumer::read_drawings()

View File

@ -37,6 +37,7 @@ namespace xlnt {
class path; class path;
class relationship; class relationship;
class workbook; class workbook;
class worksheet;
namespace detail { namespace detail {
@ -200,7 +201,7 @@ private:
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
void read_comments(); void read_comments(worksheet ws);
/// <summary> /// <summary>
/// ///

View File

@ -154,8 +154,8 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
auto header_length = read_int<std::uint32_t>(offset, encryption_info); auto header_length = read_int<std::uint32_t>(offset, encryption_info);
auto index_at_start = offset; auto index_at_start = offset;
auto skip_flags = read_int<std::uint32_t>(offset, encryption_info); /*auto skip_flags = */read_int<std::uint32_t>(offset, encryption_info);
auto size_extra = read_int<std::uint32_t>(offset, encryption_info); /*auto size_extra = */read_int<std::uint32_t>(offset, encryption_info);
auto alg_id = read_int<std::uint32_t>(offset, encryption_info); auto alg_id = read_int<std::uint32_t>(offset, encryption_info);
if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610) if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)

View File

@ -655,10 +655,10 @@ void xlsx_producer::write_shared_string_table(const relationship &rel)
for (const auto &string : source_.get_shared_strings()) for (const auto &string : source_.get_shared_strings())
{ {
if (string.get_runs().size() == 1 && !string.get_runs().at(0).has_formatting()) if (string.runs().size() == 1 && !string.runs().at(0).has_formatting())
{ {
serializer().start_element(xmlns, "si"); serializer().start_element(xmlns, "si");
serializer().element(xmlns, "t", string.get_plain_string()); serializer().element(xmlns, "t", string.plain_text());
serializer().end_element(xmlns, "si"); serializer().end_element(xmlns, "si");
continue; continue;
@ -666,7 +666,7 @@ void xlsx_producer::write_shared_string_table(const relationship &rel)
serializer().start_element(xmlns, "si"); serializer().start_element(xmlns, "si");
for (const auto &run : string.get_runs()) for (const auto &run : string.runs())
{ {
serializer().start_element(xmlns, "r"); serializer().start_element(xmlns, "r");
@ -684,7 +684,7 @@ void xlsx_producer::write_shared_string_table(const relationship &rel)
if (run.has_color()) if (run.has_color())
{ {
serializer().start_element(xmlns, "color"); serializer().start_element(xmlns, "color");
serializer().attribute("val", run.get_color()); write_color(run.get_color());
serializer().end_element(xmlns, "color"); serializer().end_element(xmlns, "color");
} }
@ -1974,7 +1974,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
for (std::size_t i = 0; i < shared_strings.size(); i++) for (std::size_t i = 0; i < shared_strings.size(); i++)
{ {
if (shared_strings[i] == cell.get_value<text>()) if (shared_strings[i] == cell.get_value<formatted_text>())
{ {
match_index = static_cast<int>(i); match_index = static_cast<int>(i);
break; break;

View File

@ -240,4 +240,20 @@ void color::assert_type(type t) const
} }
} }
bool color::operator==(const xlnt::color &other) const
{
if (type_ != other.type_ || tint_ != other.tint_) return false;
switch(type_)
{
case type::auto_:
case type::indexed :
return indexed_.get_index() == other.indexed_.get_index();
case type::theme:
return theme_.get_index() == other.theme_.get_index();
case type::rgb:
return rgb_.get_hex_string() == other.rgb_.get_hex_string();
}
return false;
}
} // namespace xlnt } // namespace xlnt

View File

@ -34,4 +34,20 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
wb.load(path_helper::get_data_directory("17_encrypted_numbers.xlsx"), "secret"); wb.load(path_helper::get_data_directory("17_encrypted_numbers.xlsx"), "secret");
} }
void test_comments()
{
xlnt::workbook wb;
wb.load("data/18_basic_comments.xlsx");
auto sheet1 = wb[0];
TS_ASSERT_EQUALS(sheet1.get_cell("A1").get_value<std::string>(), "Sheet1!A1");
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().plain_text(), "Sheet1 comment");
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().author(), "Microsoft Office User");
auto sheet2 = wb[1];
TS_ASSERT_EQUALS(sheet2.get_cell("A1").get_value<std::string>(), "Sheet2!A1");
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().plain_text(), "Sheet2 comment");
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().author(), "Microsoft Office User");
}
}; };

View File

@ -954,17 +954,17 @@ const manifest &workbook::get_manifest() const
return d_->manifest_; return d_->manifest_;
} }
std::vector<text> &workbook::get_shared_strings() std::vector<formatted_text> &workbook::get_shared_strings()
{ {
return d_->shared_strings_; return d_->shared_strings_;
} }
const std::vector<text> &workbook::get_shared_strings() const const std::vector<formatted_text> &workbook::get_shared_strings() const
{ {
return d_->shared_strings_; return d_->shared_strings_;
} }
void workbook::add_shared_string(const text &shared, bool allow_duplicates) void workbook::add_shared_string(const formatted_text &shared, bool allow_duplicates)
{ {
register_shared_string_table_in_manifest(); register_shared_string_table_in_manifest();

Binary file not shown.