Merge pull request #294 from Crzyrndm/dev-worksheet-test-failures

Fix all round-tripping test failures and UTF encoding test failures
This commit is contained in:
Thomas Fussell 2018-07-07 09:07:50 -04:00 committed by GitHub
commit e1e4823908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1507 additions and 369 deletions

View File

@ -0,0 +1,72 @@
// Copyright (c) 2014-2018 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/xlnt_config.hpp>
#include <xlnt/packaging/uri.hpp>
#include <string>
#include <vector>
namespace xml {
class parser;
class serializer;
}
namespace xlnt {
/// <summary>
/// A list of xml extensions that may or may not be understood by the parser
/// preservation is required for round-tripping even if extension is not understood
/// [serialised: extLst]
/// </summary>
class XLNT_API ext_list
{
public:
struct ext
{
public:
ext(xml::parser &parser, const std::string& ns);
ext(const uri& ID, const std::string& serialised);
void serialise(xml::serializer &serialiser, const std::string& ns);
uri extension_ID_;
std::string serialised_value_;
};
ext_list() = default; // default ctor required by xlnt::optional
explicit ext_list(xml::parser &parser, const std::string& ns);
void serialize(xml::serializer &serialiser, const std::string& ns);
void add_extension(const uri &ID, const std::string &element);
bool has_extension(const uri &extension_uri) const;
const ext &extension(const uri &extension_uri) const;
const std::vector<ext> &extensions() const;
private:
std::vector<ext> extensions_;
};
} // namespace xlnt

View File

@ -170,7 +170,7 @@ public:
/// <summary>
/// Returns the name of the font face.
/// </summary>
std::string name() const;
const std::string& name() const;
/// <summary>
/// Returns true if this font has a color applied.
@ -232,7 +232,7 @@ public:
/// <summary>
/// Returns the scheme of this font.
/// </summary>
std::string scheme() const;
const std::string& scheme() const;
/// <summary>
/// Returns true if left is exactly equal to right.
@ -242,7 +242,10 @@ public:
/// <summary>
/// Returns true if left is not exactly equal to right.
/// </summary>
bool operator!=(const font &other) const;
bool operator!=(const font &other) const
{
return !operator==(other);
}
private:
friend class style;

View File

@ -0,0 +1,44 @@
// Copyright (c) 2014-2018 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/xlnt_config.hpp>
#include <sstream>
namespace xlnt {
/// <summary>
/// Takes in any nuber and outputs a string form of that number which will
/// serialise and deserialise without loss of precision
/// </summary>
template <typename Number>
std::string serialize_number_to_string(Number num)
{
// more digits and excel won't match
constexpr int Excel_Digit_Precision = 15; //sf
std::stringstream ss;
ss.precision(Excel_Digit_Precision);
ss << num;
return ss.str();
}
}

View File

@ -51,6 +51,12 @@ public:
/// </summary>
optional<std::size_t> style;
/// <summary>
/// Is this column sized to fit its content as best it can
/// serialise if true
/// </summary>
bool best_fit = false;
/// <summary>
/// If true, this column will be hidden
/// </summary>

View File

@ -25,6 +25,7 @@
#pragma once
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/optional.hpp>
namespace xlnt {
@ -33,6 +34,7 @@ namespace xlnt {
/// </summary>
enum class XLNT_API orientation
{
default_orientation,
portrait,
landscape
};
@ -117,16 +119,6 @@ public:
/// </summary>
void paper_size(xlnt::paper_size paper_size);
/// <summary>
/// Returns the orientation of the worksheet using this page setup.
/// </summary>
xlnt::orientation orientation() const;
/// <summary>
/// Sets the orientation of the page.
/// </summary>
void orientation(xlnt::orientation orientation);
/// <summary>
/// Returns true if this worksheet should be scaled to fit on a single page during printing.
/// </summary>
@ -157,28 +149,6 @@ public:
/// </summary>
void fit_to_width(bool fit_to_width);
/// <summary>
/// Sets whether the worksheet should be centered horizontall on the page if it takes
/// up less than a full page.
/// </summary>
void horizontal_centered(bool horizontal_centered);
/// <summary>
/// Returns whether horizontal centering has been enabled.
/// </summary>
bool horizontal_centered() const;
/// <summary>
/// Sets whether the worksheet should be vertically centered on the page if it takes
/// up less than a full page.
/// </summary>
void vertical_centered(bool vertical_centered);
/// <summary>
/// Returns whether vertical centering has been enabled.
/// </summary>
bool vertical_centered() const;
/// <summary>
/// Sets the factor by which the page should be scaled during printing.
/// </summary>
@ -189,6 +159,19 @@ public:
/// </summary>
double scale() const;
/// <summary>
/// The orientation
/// </summary>
xlnt::optional<xlnt::orientation> orientation_;
/// <summary>
/// The horizontal dpi
/// </summary>
xlnt::optional<std::size_t> horizontal_dpi_;
/// <summary>
/// The vertical dpi
/// </summary>
xlnt::optional<std::size_t> vertical_dpi_;
private:
/// <summary>
/// The break
@ -205,11 +188,6 @@ private:
/// </summary>
xlnt::paper_size paper_size_;
/// <summary>
/// The orientation
/// </summary>
xlnt::orientation orientation_;
/// <summary>
/// Whether or not to fit to page
/// </summary>
@ -225,20 +203,10 @@ private:
/// </summary>
bool fit_to_width_;
/// <summary>
/// Whether or not to center the content horizontally
/// </summary>
bool horizontal_centered_;
/// <summary>
/// Whether or not to center the conent vertically
/// </summary>
bool vertical_centered_;
/// <summary>
/// The amount to scale the worksheet
/// </summary>
double scale_;
};
} // namespace xlnt
} // namespace xlnt

View File

@ -0,0 +1,165 @@
// Copyright (c) 2014-2018 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 <cstdint>
#include <ostream>
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/optional.hpp>
namespace xlnt {
/// <summary>
/// Phonetic properties
/// Element provides a collection of properties that affect display of East Asian Languages
/// [Serialised phoneticPr]
/// </summary>
class XLNT_API phonetic_pr
{
public:
static const std::string Serialised_ID;
/// <summary>
/// possible values for alignment property
/// </summary>
enum class align
{
center,
distributed,
left,
no_control
};
/// <summary>
/// possible values for type property
/// </summary>
enum class phonetic_type
{
full_width_katakana,
half_width_katakana,
hiragana,
no_conversion
};
/// <summary>
/// FontID represented by an unsigned 32-bit integer
/// </summary>
using font_id_t = std::uint32_t;
/// <summary>
/// Default ctor for phonetic properties
/// </summary>
phonetic_pr() = default;
/// <summary>
/// FontID ctor for phonetic properties
/// </summary>
explicit phonetic_pr(font_id_t font);
/// <summary>
/// adds the xml serialised representation of this element to the stream
/// </summary>
void serialise(std::ostream& output_stream) const;
/// <summary>
/// get the font index
/// </summary>
font_id_t font_id() const;
/// <summary>
/// set the font index
/// </summary>
void font_id(font_id_t font);
/// <summary>
/// is the phonetic type set
/// </summary>
bool has_type() const;
/// <summary>
/// returns the phonetic type
/// </summary>
phonetic_type type() const;
/// <summary>
/// sets the phonetic type
/// </summary>
void type(phonetic_type type);
/// <summary>
/// is the alignment set
/// </summary>
bool has_alignment() const;
/// <summary>
/// get the alignment
/// </summary>
align alignment() const;
/// <summary>
/// set the alignment
/// </summary>
void alignment(align align);
// serialisation
/// <summary>
/// string form of the type enum
/// </summary>
static const std::string &type_as_string(phonetic_type type);
/// <summary>
/// type enum from string
/// </summary>
static phonetic_type type_from_string(const std::string &str);
/// <summary>
/// string form of alignment enum
/// </summary>
static const std::string &alignment_as_string(xlnt::phonetic_pr::align type);
/// <summary>
/// alignment enum from string
/// </summary>
static align alignment_from_string(const std::string &str);
private:
/// <summary>
/// zero based index into style sheet font record.
/// Default: 0
/// </summary>
font_id_t font_id_ = 0;
/// <summary>
/// Type of characters to use.
/// Default: full width katakana
/// </summary>
xlnt::optional<phonetic_type> type_;
/// <summary>
/// align across the cell(s).
/// Default: Left
/// </summary>
xlnt::optional<align> alignment_;
};
}

View File

@ -0,0 +1,58 @@
// Copyright (c) 2014-2018 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/xlnt_config.hpp>
#include <xlnt/utils/optional.hpp>
namespace xlnt {
struct XLNT_API print_options
{
/// <summary>
/// if both grid_lines_set and this are true, grid lines are printed
/// </summary>
optional<bool> print_grid_lines;
/// <summary>
/// if both print grid lines and this are true, grid lines are printed
/// </summary>
optional<bool> grid_lines_set;
/// <summary>
/// print row and column headings
/// </summary>
optional<bool> print_headings;
/// <summary>
/// center on page horizontally
/// </summary>
optional<bool> horizontal_centered;
/// <summary>
/// center on page vertically
/// </summary>
optional<bool> vertical_centered;
};
} // namespace xlnt

View File

@ -54,6 +54,11 @@ public:
/// </summary>
bool hidden = false;
/// <summary>
/// True if row style should be applied
/// </summary>
optional<bool> custom_format;
/// <summary>
/// The index to the style used by all cells in this row
/// </summary>

View File

@ -36,6 +36,27 @@ namespace xlnt {
class XLNT_API selection
{
public:
/// <summary>
/// default ctor
/// </summary>
explicit selection() = default;
/// <summary>
/// ctor when no range selected
/// sqref == active_cell
/// </summary>
explicit selection(pane_corner quadrant, cell_reference active_cell)
: active_cell_(active_cell), sqref_(range_reference(active_cell, active_cell)), pane_(quadrant)
{}
/// <summary>
/// ctor with selected range
/// sqref must contain active_cell
/// </summary>
explicit selection(pane_corner quadrant, cell_reference active_cell, range_reference selected)
: active_cell_(active_cell), sqref_(selected), pane_(quadrant)
{}
/// <summary>
/// Returns true if this selection has a defined active cell.
/// </summary>
@ -121,17 +142,18 @@ public:
private:
/// <summary>
/// The active cell
/// The last selected cell in the selection
/// </summary>
optional<cell_reference> active_cell_;
/// <summary>
/// The range
/// The last selected block in the selection
/// contains active_cell_, normally == to active_cell_
/// </summary>
optional<range_reference> sqref_;
/// <summary>
/// The quadrant
/// The corner of the worksheet that this selection extends to
/// </summary>
pane_corner pane_;
};

View File

@ -44,6 +44,11 @@ public:
/// </summary>
optional<double> default_row_height;
/// <summary>
/// The default column width
/// </summary>
optional<double> default_column_width;
/// <summary>
/// x14ac extension, dyDescent property
/// </summary>

View File

@ -0,0 +1,81 @@
// Copyright (c) 2014-2018 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 <string>
#include <xlnt/xlnt_config.hpp>
#include <xlnt/utils/optional.hpp>
#include <xlnt/cell/cell_reference.hpp>
namespace xlnt {
struct XLNT_API sheet_pr
{
/// <summary>
/// is horizontally synced to the anchor point
/// </summary>
optional<bool> sync_horizontal;
/// <summary>
/// is vertically synced to the anchor point
/// </summary>
optional<bool> sync_vertical;
/// <summary>
/// Anchor point for worksheet's window
/// </summary>
optional<cell_reference> sync_ref;
/// <summary>
/// Lotus compatibility option
/// </summary>
optional<bool> transition_evaluation;
/// <summary>
/// Lotus compatibility option
/// </summary>
optional<bool> transition_entry;
/// <summary>
/// worksheet is published
/// </summary>
optional<bool> published;
/// <summary>
/// stable name of the sheet
/// </summary>
optional<std::string> code_name;
/// <summary>
/// worksheet has one or more autofilters or advanced filters on
/// </summary>
optional<bool> filter_mode;
/// <summary>
/// whether the conditional formatting calculations shall be evaluated
/// </summary>
optional<bool> enable_format_condition_calculation;
};
} // namespace xlnt

View File

@ -192,6 +192,30 @@ public:
return type_;
}
/// <summary>
/// has a top left cell?
/// </summary>
bool has_top_left_cell() const
{
return top_left_cell_.is_set();
}
/// <summary>
/// Sets the top left cell of this view.
/// </summary>
void top_left_cell(const cell_reference& ref)
{
top_left_cell_.set(ref);
}
/// <summary>
/// Returns the top left cell of this view.
/// </summary>
cell_reference top_left_cell() const
{
return top_left_cell_.get();
}
/// <summary>
/// Returns true if this view is equal to rhs based on its id, grid lines setting,
/// default grid color, pane, and selections.
@ -202,7 +226,8 @@ public:
&& show_grid_lines_ == rhs.show_grid_lines_
&& default_grid_color_ == rhs.default_grid_color_
&& pane_ == rhs.pane_
&& selections_ == rhs.selections_;
&& selections_ == rhs.selections_
&& top_left_cell_ == rhs.top_left_cell_;
}
private:
@ -231,6 +256,11 @@ private:
/// </summary>
optional<xlnt::pane> pane_;
/// <summary>
/// The top left cell
/// </summary>
optional<cell_reference> top_left_cell_;
/// <summary>
/// The collection of selections
/// </summary>

View File

@ -56,6 +56,7 @@ class relationship;
class row_properties;
class sheet_format_properties;
class workbook;
class phonetic_pr;
struct date;
@ -556,6 +557,21 @@ public:
/// </summary>
void reserve(std::size_t n);
/// <summary>
/// Returns true if this sheet has phonetic properties
/// </summary>
bool has_phonetic_properties() const;
/// <summary>
/// Returns the phonetic properties of this sheet.
/// </summary>
const phonetic_pr &phonetic_properties() const;
/// <summary>
/// Sets the phonetic properties of this sheet to phonetic_props
/// </summary>
void phonetic_properties(const phonetic_pr& phonetic_props);
/// <summary>
/// Returns true if this sheet has a header/footer.
/// </summary>

View File

@ -369,35 +369,25 @@ hyperlink cell::hyperlink() const
void cell::hyperlink(const std::string &url)
{
hyperlink(url, url);
}
void cell::hyperlink(const std::string &url, const std::string &display)
{
if (url.empty() || std::find(url.begin(), url.end(), ':') == url.end() || display.empty())
if (url.empty() || std::find(url.begin(), url.end(), ':') == url.end())
{
throw invalid_parameter();
}
auto ws = worksheet();
auto &manifest = ws.workbook().manifest();
bool existing = false;
d_->hyperlink_ = detail::hyperlink_impl();
// check for existing relationships
for (const auto &rel : manifest.relationships(ws.path(), relationship_type::hyperlink))
{
if (rel.target().path().string() == url)
{
d_->hyperlink_.get().relationship = rel;
existing = true;
break;
}
auto relationships = manifest.relationships(ws.path(), relationship_type::hyperlink);
auto relation = std::find_if(relationships.cbegin(), relationships.cend(),
[&url](xlnt::relationship rel) { return rel.target().path().string() == url; });
if (relation != relationships.end()) {
d_->hyperlink_.get().relationship = *relation;
}
// register a new relationship
if (!existing) {
else
{ // register a new relationship
auto rel_id = manifest.register_relationship(
uri(ws.path().string()),
relationship_type::hyperlink,
@ -405,9 +395,25 @@ void cell::hyperlink(const std::string &url, const std::string &display)
target_mode::external);
// TODO: make manifest::register_relationship return the created relationship instead of rel id
d_->hyperlink_.get().relationship = manifest.relationship(ws.path(), rel_id);
}
if (!has_value()) // hyperlink on an empty cell sets the value to the hyperlink string
{
value(url);
}
}
value(display);
void cell::hyperlink(const std::string &url, const std::string &display)
{
if (!display.empty()) // if the display string isn't empty use that
{
value(display);
}
else // empty display string sets the value to the link text
{
value(url);
}
hyperlink(url);
d_->hyperlink_.get().display = display;
}

View File

@ -29,12 +29,12 @@
namespace xlnt {
rich_text::rich_text(const std::string &plain_text)
: rich_text({plain_text, optional<font>()})
: rich_text(rich_text_run{plain_text, optional<font>(), false})
{
}
rich_text::rich_text(const std::string &plain_text, const class font &text_font)
: rich_text({plain_text, optional<font>(text_font)})
: rich_text(rich_text_run{plain_text, optional<font>(text_font), false})
{
}

View File

@ -22,6 +22,7 @@
// @author: see AUTHORS file
#include <detail/header_footer/header_footer_code.hpp>
#include <xlnt/utils/serialisation_utils.hpp>
namespace xlnt {
namespace detail {
@ -359,6 +360,11 @@ std::array<xlnt::optional<xlnt::rich_text>, 3> decode_header_footer(const std::s
case hf_code::text_single_underline:
{
if (!current_run.second.is_set())
{
current_run.second = xlnt::font();
}
current_run.second.get().underline(font::underline_style::single);
break;
}
@ -527,9 +533,25 @@ std::string encode_header_footer(const rich_text &t, header_footer::location whe
if (run.second.get().has_size())
{
encoded.push_back('&');
encoded.append(std::to_string(run.second.get().size()));
encoded.append(serialize_number_to_string(run.second.get().size()));
}
if (run.second.get().underlined())
{
switch (run.second.get().underline())
{
case font::underline_style::single:
case font::underline_style::single_accounting:
encoded.append("&U");
break;
case font::underline_style::double_:
case font::underline_style::double_accounting:
encoded.append("&E");
break;
default:
case font::underline_style::none:
break;
}
}
if (run.second.get().has_color())
{
encoded.push_back('&');

View File

@ -29,6 +29,7 @@
#include <detail/implementations/stylesheet.hpp>
#include <detail/implementations/worksheet_impl.hpp>
#include <xlnt/packaging/ext_list.hpp>
#include <xlnt/packaging/manifest.hpp>
#include <xlnt/utils/datetime.hpp>
#include <xlnt/utils/variant.hpp>
@ -123,6 +124,7 @@ struct workbook_impl
optional<calculation_properties> calculation_properties_;
optional<std::string> abs_path_;
optional<std::size_t> arch_id_flags_;
optional<ext_list> extensions_;
};
} // namespace detail

View File

@ -27,15 +27,19 @@
#include <unordered_map>
#include <vector>
#include <detail/implementations/cell_impl.hpp>
#include <xlnt/packaging/ext_list.hpp>
#include <xlnt/workbook/named_range.hpp>
#include <xlnt/worksheet/column_properties.hpp>
#include <xlnt/worksheet/header_footer.hpp>
#include <xlnt/worksheet/phonetic_pr.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/row_properties.hpp>
#include <xlnt/worksheet/sheet_format_properties.hpp>
#include <xlnt/worksheet/sheet_view.hpp>
#include <xlnt/worksheet/print_options.hpp>
#include <xlnt/worksheet/sheet_pr.hpp>
#include <detail/implementations/cell_impl.hpp>
namespace xlnt {
@ -72,6 +76,7 @@ struct worksheet_impl
page_margins_ = other.page_margins_;
merged_cells_ = other.merged_cells_;
named_ranges_ = other.named_ranges_;
phonetic_properties_ = other.phonetic_properties_;
header_footer_ = other.header_footer_;
print_title_cols_ = other.print_title_cols_;
print_title_rows_ = other.print_title_rows_;
@ -79,6 +84,9 @@ struct worksheet_impl
views_ = other.views_;
column_breaks_ = other.column_breaks_;
row_breaks_ = other.row_breaks_;
extension_list_ = other.extension_list_;
sheet_properties_ = other.sheet_properties_;
print_options_ = other.print_options_;
for (auto &row : cell_map_)
{
@ -107,6 +115,7 @@ struct worksheet_impl
std::vector<range_reference> merged_cells_;
std::unordered_map<std::string, named_range> named_ranges_;
optional<phonetic_pr> phonetic_properties_;
optional<header_footer> header_footer_;
std::string print_title_cols_;
@ -120,6 +129,10 @@ struct worksheet_impl
std::vector<row_t> row_breaks_;
std::unordered_map<std::string, comment> comments_;
optional<print_options> print_options_;
optional<sheet_pr> sheet_properties_;
optional<ext_list> extension_list_;
};
} // namespace detail

View File

@ -343,5 +343,19 @@ std::string to_string(pane_state state)
default_case("frozen");
}
std::string to_string(orientation orientation)
{
switch (orientation)
{
case orientation::default_orientation:
return "default";
case orientation::landscape:
return "landscape";
case orientation::portrait:
return "portrait";
}
default_case("default");
}
} // namespace detail
} // namespace xlnt

View File

@ -37,6 +37,7 @@
#include <xlnt/utils/exceptions.hpp>
#include <xlnt/utils/variant.hpp>
#include <xlnt/worksheet/pane.hpp>
#include <xlnt/worksheet/page_setup.hpp>
#include <xlnt/workbook/metadata_property.hpp>
namespace xlnt {
@ -76,6 +77,8 @@ std::string to_string(target_mode mode);
std::string to_string(pane_state state);
std::string to_string(orientation state);
template<typename T>
static T from_string(const std::string &string_value);
@ -198,7 +201,11 @@ pattern_fill_type from_string(const std::string &string)
auto toLower = [](std::string str) {
auto bg{ std::begin (str) };
auto en{ std::end (str) };
std::transform (bg, en, bg, tolower);
std::transform(bg, en, bg,
[](char c) {
// static cast to avoid int -> char narrowing warning
return static_cast<char>(tolower(c));
});
return str;
};
@ -390,6 +397,15 @@ pane_corner from_string(const std::string &string)
default_case(pane_corner::bottom_left);
}
template <>
orientation from_string(const std::string &string)
{
if (string == "default") return orientation::default_orientation;
else if (string == "landscape") return orientation::landscape;
else if (string == "portrait") return orientation::portrait;
default_case(orientation::default);
}
} // namespace detail
} // namespace xlnt
@ -578,6 +594,20 @@ struct value_traits<xlnt::extended_property>
}
};
template <>
struct value_traits<xlnt::orientation>
{
static xlnt::orientation parse(std::string string, const parser &)
{
return xlnt::detail::from_string<xlnt::orientation>(string);
}
static std::string serialize(xlnt::orientation orientation, const serializer &)
{
return xlnt::detail::to_string(orientation);
}
};
} // namespace xml

View File

@ -26,13 +26,6 @@
#include <sstream>
#include <unordered_map>
#include <detail/constants.hpp>
#include <detail/header_footer/header_footer_code.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/serialization/custom_value_traits.hpp>
#include <detail/serialization/vector_streambuf.hpp>
#include <detail/serialization/xlsx_consumer.hpp>
#include <detail/serialization/zstream.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/comment.hpp>
#include <xlnt/cell/hyperlink.hpp>
@ -42,6 +35,13 @@
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/selection.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <detail/constants.hpp>
#include <detail/header_footer/header_footer_code.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/serialization/custom_value_traits.hpp>
#include <detail/serialization/vector_streambuf.hpp>
#include <detail/serialization/xlsx_consumer.hpp>
#include <detail/serialization/zstream.hpp>
namespace std {
@ -221,9 +221,9 @@ cell xlsx_consumer::read_cell()
row_properties.dy_descent = parser().attribute<double>(qn("x14ac", "dyDescent"));
}
skip_attributes({ "customFormat", "s", "customFont",
skip_attributes({"customFormat", "s", "customFont",
"outlineLevel", "collapsed", "thickTop", "thickBot",
"ph", "spans" });
"ph", "spans"});
}
if (!in_element(qn("spreadsheetml", "row")))
@ -246,7 +246,7 @@ cell xlsx_consumer::read_cell()
if (parser().attribute_present("s"))
{
cell.format(target_.format(std::stoull(parser().attribute("s"))));
cell.format(target_.format(static_cast<std::size_t>(std::stoull(parser().attribute("s")))));
}
auto has_value = false;
@ -274,8 +274,8 @@ cell xlsx_consumer::read_cell()
has_shared_formula = parser().attribute("t") == "shared";
}
skip_attributes({ "aca", "ref", "dt2D", "dtr", "del1",
"del2", "r1", "r2", "ca", "si", "bx" });
skip_attributes({"aca", "ref", "dt2D", "dtr", "del1",
"del2", "r1", "r2", "ca", "si", "bx"});
formula_value_string = read_text();
}
@ -367,13 +367,13 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
target_.d_->sheet_title_rel_id_map_.end(),
[&](const std::pair<std::string, std::string> &p) {
return p.second == rel_id;
})->first;
return p.second == rel_id;
})->first;
auto ws = worksheet(current_worksheet_);
expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet
skip_attributes({ qn("mc", "Ignorable") });
skip_attributes({qn("mc", "Ignorable")});
while (in_element(qn("spreadsheetml", "worksheet")))
{
@ -381,6 +381,44 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1
{
sheet_pr props;
if (parser().attribute_present("syncHorizontal"))
{ // optional, boolean, false
props.sync_horizontal.set(parser().attribute<bool>("syncHorizontal"));
}
if (parser().attribute_present("syncVertical"))
{// optional, boolean, false
props.sync_vertical.set(parser().attribute<bool>("syncVertical"));
}
if (parser().attribute_present("syncRef"))
{ // optional, ST_Ref, false
props.sync_ref.set(cell_reference(parser().attribute("syncRef")));
}
if (parser().attribute_present("transitionEvaluation"))
{ // optional, boolean, false
props.transition_evaluation.set(parser().attribute<bool>("transitionEvaluation"));
}
if (parser().attribute_present("transitionEntry"))
{// optional, boolean, false
props.transition_entry.set(parser().attribute<bool>("transitionEntry"));
}
if (parser().attribute_present("published"))
{// optional, boolean, true
props.published.set(parser().attribute<bool>("published"));
}
if (parser().attribute_present("codeName"))
{ // optional, string
props.code_name.set(parser().attribute<std::string>("codeName"));
}
if (parser().attribute_present("filterMode"))
{// optional, boolean, false
props.filter_mode.set(parser().attribute<bool>("filterMode"));
}
if (parser().attribute_present("enableFormatConditionsCalculation"))
{// optional, boolean, true
props.enable_format_condition_calculation.set(parser().attribute<bool>("enableFormatConditionsCalculation"));
}
ws.d_->sheet_properties_.set(props);
while (in_element(current_worksheet_element))
{
auto sheet_pr_child_element = expect_start_element(xml::content::simple);
@ -408,16 +446,6 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
expect_end_element(sheet_pr_child_element);
}
skip_attribute("syncHorizontal"); // optional, boolean, false
skip_attribute("syncVertical"); // optional, boolean, false
skip_attribute("syncRef"); // optional, ST_Ref, false
skip_attribute("transitionEvaluation"); // optional, boolean, false
skip_attribute("transitionEntry"); // optional, boolean, false
skip_attribute("published"); // optional, boolean, true
skip_attribute("codeName"); // optional, string
skip_attribute("filterMode"); // optional, boolean, false
skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true
}
else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1
{
@ -436,6 +464,10 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
{
new_view.show_grid_lines(is_true(parser().attribute("showGridLines")));
}
if (parser().attribute_present("topLeftCell"))
{
new_view.top_left_cell(cell_reference(parser().attribute("topLeftCell")));
}
if (parser().attribute_present("defaultGridColor")) // default="true"
{
@ -446,8 +478,8 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
&& parser().attribute("view") != "normal")
{
new_view.type(parser().attribute("view") == "pageBreakPreview"
? sheet_view_type::page_break_preview
: sheet_view_type::page_layout);
? sheet_view_type::page_break_preview
: sheet_view_type::page_layout);
}
if (parser().attribute_present("tabSelected")
@ -456,9 +488,9 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
target_.d_->view_.get().active_tab = ws.id() - 1;
}
skip_attributes({ "windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", "showRuler", "showOutlineSymbols", "showWhiteSpace",
skip_attributes({"windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", "showRuler", "showOutlineSymbols", "showWhiteSpace",
"view", "topLeftCell", "colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView",
"zoomScalePageLayoutView" });
"zoomScalePageLayoutView"});
while (in_element(qn("spreadsheetml", "sheetView")))
{
@ -509,7 +541,7 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
const auto sqref = range_reference(parser().attribute("sqref"));
current_selection.sqref(sqref);
}
current_selection.pane(pane_corner::top_left);
new_view.add_selection(current_selection);
@ -544,7 +576,11 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
ws.d_->format_properties_.base_col_width =
parser().attribute<double>("baseColWidth");
}
if (parser().attribute_present("defaultColWidth"))
{
ws.d_->format_properties_.default_column_width =
parser().attribute<double>("defaultColWidth");
}
if (parser().attribute_present("defaultRowHeight"))
{
ws.d_->format_properties_.default_row_height =
@ -565,7 +601,7 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
{
expect_start_element(qn("spreadsheetml", "col"), xml::content::simple);
skip_attributes({ "bestFit", "collapsed", "outlineLevel" });
skip_attributes({"bestFit", "collapsed", "outlineLevel"});
auto min = static_cast<column_t::index_t>(std::stoull(parser().attribute("min")));
auto max = static_cast<column_t::index_t>(std::stoull(parser().attribute("max")));
@ -585,9 +621,14 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
}
auto custom = parser().attribute_present("customWidth")
? is_true(parser().attribute("customWidth")) : false;
? is_true(parser().attribute("customWidth"))
: false;
auto hidden = parser().attribute_present("hidden")
? is_true(parser().attribute("hidden")) : false;
? is_true(parser().attribute("hidden"))
: false;
auto best_fit = parser().attribute_present("bestFit")
? is_true(parser().attribute("bestFit"))
: false;
expect_end_element(qn("spreadsheetml", "col"));
@ -607,6 +648,7 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
props.hidden = hidden;
props.custom_width = custom;
props.best_fit = best_fit;
ws.add_column_properties(column, props);
}
}
@ -659,9 +701,18 @@ void xlsx_consumer::read_worksheet_sheetdata()
row_properties.dy_descent = parser().attribute<double>(qn("x14ac", "dyDescent"));
}
skip_attributes({ "customFormat", "s", "customFont",
if (parser().attribute_present("s"))
{
row_properties.style.set(static_cast<std::size_t>(std::stoull(parser().attribute("s"))));
}
if (parser().attribute_present("customFormat"))
{
row_properties.custom_format.set(parser().attribute<bool>("customFormat"));
}
skip_attributes({"customFont",
"outlineLevel", "collapsed", "thickTop", "thickBot",
"ph", "spans" });
"ph", "spans"});
while (in_element(qn("spreadsheetml", "row")))
{
@ -673,7 +724,7 @@ void xlsx_consumer::read_worksheet_sheetdata()
if (parser().attribute_present("s"))
{
cell.format(target_.format(std::stoull(parser().attribute("s"))));
cell.format(target_.format(static_cast<std::size_t>(std::stoull(parser().attribute("s")))));
}
auto has_value = false;
@ -702,7 +753,7 @@ void xlsx_consumer::read_worksheet_sheetdata()
}
skip_attributes(
{ "aca", "ref", "dt2D", "dtr", "del1", "del2", "r1", "r2", "ca", "si", "bx" });
{"aca", "ref", "dt2D", "dtr", "del1", "del2", "r1", "r2", "ca", "si", "bx"});
formula_value_string = read_text();
}
@ -757,7 +808,6 @@ void xlsx_consumer::read_worksheet_sheetdata()
cell.error(value_string);
}
}
}
expect_end_element(qn("spreadsheetml", "row"));
@ -835,7 +885,16 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
}
else if (current_worksheet_element == qn("spreadsheetml", "phoneticPr")) // CT_PhoneticPr 0-1
{
skip_remaining_content(current_worksheet_element);
phonetic_pr phonetic_properties(parser().attribute<std::uint32_t>("fontId"));
if (parser().attribute_present("type"))
{
phonetic_properties.type(phonetic_pr::type_from_string(parser().attribute("type")));
}
if (parser().attribute_present("alignment"))
{
phonetic_properties.alignment(phonetic_pr::alignment_from_string(parser().attribute("alignment")));
}
current_worksheet_->phonetic_properties_.set(phonetic_properties);
}
else if (current_worksheet_element == qn("spreadsheetml", "conditionalFormatting")) // CT_ConditionalFormatting 0+
{
@ -847,7 +906,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
}
else if (current_worksheet_element == qn("spreadsheetml", "hyperlinks")) // CT_Hyperlinks 0-1
{
while (in_element(qn("spreadsheetml", "hyperlinks")))
while (in_element(current_worksheet_element))
{
// CT_Hyperlink
expect_start_element(qn("spreadsheetml", "hyperlink"), xml::content::simple);
@ -900,6 +959,28 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
}
else if (current_worksheet_element == qn("spreadsheetml", "printOptions")) // CT_PrintOptions 0-1
{
print_options opts;
if (parser().attribute_present("gridLines"))
{
opts.print_grid_lines.set(parser().attribute<bool>("gridLines"));
}
if (parser().attribute_present("gridLinesSet"))
{
opts.print_grid_lines.set(parser().attribute<bool>("gridLinesSet"));
}
if (parser().attribute_present("headings"))
{
opts.print_grid_lines.set(parser().attribute<bool>("headings"));
}
if (parser().attribute_present("horizontalCentered"))
{
opts.print_grid_lines.set(parser().attribute<bool>("horizontalCentered"));
}
if (parser().attribute_present("verticalCentered"))
{
opts.print_grid_lines.set(parser().attribute<bool>("verticalCentered"));
}
ws.d_->print_options_.set(opts);
skip_remaining_content(current_worksheet_element);
}
else if (current_worksheet_element == qn("spreadsheetml", "pageMargins")) // CT_PageMargins 0-1
@ -917,6 +998,20 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
}
else if (current_worksheet_element == qn("spreadsheetml", "pageSetup")) // CT_PageSetup 0-1
{
page_setup setup;
if (parser().attribute_present("orientation"))
{
setup.orientation_.set(parser().attribute<orientation>("orientation"));
}
if (parser().attribute_present("horizontalDpi"))
{
setup.horizontal_dpi_.set(parser().attribute<std::size_t>("horizontalDpi"));
}
if (parser().attribute_present("verticalDpi"))
{
setup.vertical_dpi_.set(parser().attribute<std::size_t>("verticalDpi"));
}
ws.page_setup(setup);
skip_remaining_content(current_worksheet_element);
}
else if (current_worksheet_element == qn("spreadsheetml", "headerFooter")) // CT_HeaderFooter 0-1
@ -980,7 +1075,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
for (std::size_t i = 0; i < 3; ++i)
{
auto loc = i == 0 ? header_footer::location::left
: i == 1 ? header_footer::location::center : header_footer::location::right;
: i == 1 ? header_footer::location::center : header_footer::location::right;
if (different_odd_even)
{
@ -1020,7 +1115,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
{
auto count = parser().attribute_present("count") ? parser().attribute<std::size_t>("count") : 0;
auto manual_break_count = parser().attribute_present("manualBreakCount")
? parser().attribute<std::size_t>("manualBreakCount") : 0;
? parser().attribute<std::size_t>("manualBreakCount")
: 0;
while (in_element(qn("spreadsheetml", "rowBreaks")))
{
@ -1037,7 +1133,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
--manual_break_count;
}
skip_attributes({ "min", "max", "pt" });
skip_attributes({"min", "max", "pt"});
expect_end_element(qn("spreadsheetml", "brk"));
}
}
@ -1063,7 +1159,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
--manual_break_count;
}
skip_attributes({ "min", "max", "pt" });
skip_attributes({"min", "max", "pt"});
expect_end_element(qn("spreadsheetml", "brk"));
}
}
@ -1093,7 +1189,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
}
else if (current_worksheet_element == qn("spreadsheetml", "extLst"))
{
skip_remaining_content(current_worksheet_element);
ext_list extensions(parser(), current_worksheet_element.namespace_());
ws.d_->extension_list_.set(extensions);
}
else
{
@ -1107,8 +1204,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments))
{
auto comments_part = manifest.canonicalize({ workbook_rel, sheet_rel,
manifest.relationship(sheet_path, xlnt::relationship_type::comments) });
auto comments_part = manifest.canonicalize({workbook_rel, sheet_rel,
manifest.relationship(sheet_path, xlnt::relationship_type::comments)});
auto receive = xml::parser::receive_default;
auto comments_part_streambuf = archive_->open(comments_part);
@ -1120,8 +1217,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing))
{
auto vml_drawings_part = manifest.canonicalize({ workbook_rel, sheet_rel,
manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing) });
auto vml_drawings_part = manifest.canonicalize({workbook_rel, sheet_rel,
manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)});
auto vml_drawings_part_streambuf = archive_->open(comments_part);
std::istream vml_drawings_part_stream(comments_part_streambuf.get());
@ -1148,8 +1245,7 @@ bool xlsx_consumer::has_cell()
std::vector<relationship> xlsx_consumer::read_relationships(const path &part)
{
const auto part_rels_path = part.parent().append("_rels")
.append(part.filename() + ".rels").relative_to(path("/"));
const auto part_rels_path = part.parent().append("_rels").append(part.filename() + ".rels").relative_to(path("/"));
std::vector<xlnt::relationship> relationships;
if (!archive_->has_file(part_rels_path)) return relationships;
@ -1357,8 +1453,8 @@ void xlsx_consumer::populate_workbook(bool streaming)
}
}
read_part({ manifest().relationship(root_path,
relationship_type::office_document) });
read_part({manifest().relationship(root_path,
relationship_type::office_document)});
}
// Package Parts
@ -1455,14 +1551,16 @@ void xlsx_consumer::read_custom_properties()
void xlsx_consumer::read_office_document(const std::string &content_type) // CT_Workbook
{
if (content_type != "application/vnd."
"openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
&& content_type != "application/vnd."
"openxmlformats-officedocument.spreadsheetml.template.main+xml")
if (content_type !=
"application/vnd."
"openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
&& content_type !=
"application/vnd."
"openxmlformats-officedocument.spreadsheetml.template.main+xml")
{
throw xlnt::invalid_file(content_type);
}
target_.d_->calculation_properties_.clear();
expect_start_element(qn("workbook", "workbook"), xml::content::complex);
@ -1532,8 +1630,9 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
else if (current_workbook_element == qn("workbook", "workbookPr")) // CT_WorkbookPr 0-1
{
target_.base_date(parser().attribute_present("date1904") // optional, bool=false
&& is_true(parser().attribute("date1904"))
? calendar::mac_1904 : calendar::windows_1900);
&& is_true(parser().attribute("date1904"))
? calendar::mac_1904
: calendar::windows_1900);
skip_attribute("showObjects"); // optional, ST_Objects="all"
skip_attribute("showBorderUnselectedTables"); // optional, bool=true
skip_attribute("filterPrivacy"); // optional, bool=false
@ -1759,7 +1858,7 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
if (!streaming_)
{
read_part({ workbook_rel, worksheet_rel });
read_part({workbook_rel, worksheet_rel});
}
}
}
@ -2224,36 +2323,40 @@ void xlsx_consumer::read_stylesheet()
expect_start_element(qn("spreadsheetml", "xf"), xml::content::complex);
auto &record = *(!in_style_records
? format_records.emplace(format_records.end())
: style_records.emplace(style_records.end()));
? format_records.emplace(format_records.end())
: style_records.emplace(style_records.end()));
if (parser().attribute_present("applyBorder"))
{
record.first.border_applied = is_true(parser().attribute("applyBorder"));
}
record.first.border_id = parser().attribute_present("borderId")
? parser().attribute<std::size_t>("borderId") : 0;
? parser().attribute<std::size_t>("borderId")
: 0;
if (parser().attribute_present("applyFill"))
{
record.first.fill_applied = is_true(parser().attribute("applyFill"));
}
record.first.fill_id = parser().attribute_present("fillId")
? parser().attribute<std::size_t>("fillId") : 0;
? parser().attribute<std::size_t>("fillId")
: 0;
if (parser().attribute_present("applyFont"))
{
record.first.font_applied = is_true(parser().attribute("applyFont"));
}
record.first.font_id = parser().attribute_present("fontId")
? parser().attribute<std::size_t>("fontId") : 0;
? parser().attribute<std::size_t>("fontId")
: 0;
if (parser().attribute_present("applyNumberFormat"))
{
record.first.number_format_applied = is_true(parser().attribute("applyNumberFormat"));
}
record.first.number_format_id = parser().attribute_present("numFmtId")
? parser().attribute<std::size_t>("numFmtId") : 0;
? parser().attribute<std::size_t>("numFmtId")
: 0;
auto apply_alignment_present = parser().attribute_present("applyAlignment");
if (apply_alignment_present)
@ -2544,7 +2647,7 @@ void xlsx_consumer::read_comments(worksheet ws)
expect_start_element(qn("spreadsheetml", "comments"), xml::content::complex);
// name space can be ignored
skip_attribute(qn("mc","Ignorable"));
skip_attribute(qn("mc", "Ignorable"));
expect_start_element(qn("spreadsheetml", "authors"), xml::content::complex);
while (in_element(qn("spreadsheetml", "authors")))
@ -2571,12 +2674,12 @@ void xlsx_consumer::read_comments(worksheet ws)
expect_end_element(qn("spreadsheetml", "text"));
if (in_element(xml::qname(qn("spreadsheetml", "comment"))))
{
expect_start_element(qn("mc", "AlternateContent"), xml::content::complex);
skip_remaining_content(qn("mc", "AlternateContent"));
expect_end_element(qn("mc", "AlternateContent"));
}
if (in_element(xml::qname(qn("spreadsheetml", "comment"))))
{
expect_start_element(qn("mc", "AlternateContent"), xml::content::complex);
skip_remaining_content(qn("mc", "AlternateContent"));
expect_end_element(qn("mc", "AlternateContent"));
}
expect_end_element(qn("spreadsheetml", "comment"));
}
@ -2779,7 +2882,8 @@ rich_text xlsx_consumer::read_rich_text(const xml::qname &parent)
auto text_element = expect_start_element(xml::content::mixed);
const auto xml_space = qn("xml", "space");
const auto preserve_space = parser().attribute_present(xml_space)
? parser().attribute(xml_space) == "preserve" : false;
? parser().attribute(xml_space) == "preserve"
: false;
skip_attributes();
auto text = read_text();
@ -2831,12 +2935,14 @@ rich_text xlsx_consumer::read_rich_text(const xml::qname &parent)
else if (current_run_property_element == xml::qname(xmlns, "b"))
{
run.second.get().bold(parser().attribute_present("val")
? is_true(parser().attribute("val")) : true);
? is_true(parser().attribute("val"))
: true);
}
else if (current_run_property_element == xml::qname(xmlns, "i"))
{
run.second.get().italic(parser().attribute_present("val")
? is_true(parser().attribute("val")) : true);
? is_true(parser().attribute("val"))
: true);
}
else if (current_run_property_element == xml::qname(xmlns, "u"))
{
@ -2931,4 +3037,4 @@ manifest &xlsx_consumer::manifest()
}
} // namespace detail
} // namepsace xlnt
} // namespace xlnt

View File

@ -38,6 +38,7 @@
#include <xlnt/packaging/manifest.hpp>
#include <xlnt/utils/path.hpp>
#include <xlnt/utils/scoped_enum_hash.hpp>
#include <xlnt/utils/serialisation_utils.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/workbook/workbook_view.hpp>
#include <xlnt/worksheet/header_footer.hpp>
@ -45,14 +46,6 @@
namespace {
/// <summary>
/// Returns true if d is exactly equal to an integer.
/// </summary>
bool is_integral(double d)
{
return std::fabs(d - static_cast<double>(static_cast<long long int>(d))) == 0.0;
}
std::vector<std::pair<std::string, std::string>> core_property_namespace(xlnt::core_property type)
{
using xlnt::core_property;
@ -2214,9 +2207,46 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac");
}
if (ws.has_page_setup())
if (ws.d_->sheet_properties_.is_set())
{
write_start_element(xmlns, "sheetPr");
auto &props = ws.d_->sheet_properties_.get();
if (props.sync_horizontal.is_set())
{
write_attribute("syncHorizontal", props.sync_horizontal.get());
}
if (props.sync_vertical.is_set())
{
write_attribute("syncVertical", props.sync_vertical.get());
}
if (props.sync_ref.is_set())
{
write_attribute("syncRef", props.sync_ref.get().to_string());
}
if (props.transition_evaluation.is_set())
{
write_attribute("transitionEvaluation", props.transition_evaluation.get());
}
if (props.transition_entry.is_set())
{
write_attribute("transitionEntry", props.transition_entry.get());
}
if (props.published.is_set())
{
write_attribute("published", props.published.get());
}
if (props.code_name.is_set())
{
write_attribute("codeName", props.code_name.get());
}
if (props.filter_mode.is_set())
{
write_attribute("filterMode", props.filter_mode.get());
}
if (props.enable_format_condition_calculation.is_set())
{
write_attribute("enableFormatConditionsCalculation", props.enable_format_condition_calculation.get());
}
write_start_element(xmlns, "outlinePr");
write_attribute("summaryBelow", "1");
@ -2251,13 +2281,17 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_attribute("tabSelected", write_bool(true));
}
write_attribute("workbookViewId", view.id());
if (view.type() != sheet_view_type::normal)
{
write_attribute("view", view.type() == sheet_view_type::page_break_preview
? "pageBreakPreview" : "pageLayout");
}
if (view.has_top_left_cell())
{
write_attribute("topLeftCell", view.top_left_cell().to_string());
}
write_attribute("workbookViewId", view.id());
if (view.has_pane())
{
@ -2322,7 +2356,11 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_attribute("baseColWidth",
format_properties.base_col_width.get());
}
if (format_properties.default_column_width.is_set())
{
write_attribute("defaultColWidth",
format_properties.default_column_width.get());
}
if (format_properties.default_row_height.is_set())
{
write_attribute("defaultRowHeight",
@ -2359,12 +2397,13 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (props.width.is_set())
{
write_attribute("width", (props.width.get() * 7 + 5) / 7);
double width = (props.width.get() * 7 + 5) / 7;
write_attribute("width", serialize_number_to_string(width));
}
if (props.custom_width)
if (props.best_fit)
{
write_attribute("customWidth", write_bool(true));
write_attribute("bestFit", write_bool(true));
}
if (props.style.is_set())
@ -2377,6 +2416,11 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_attribute("hidden", write_bool(true));
}
if (props.custom_width)
{
write_attribute("customWidth", write_bool(true));
}
write_end_element(xmlns, "col");
}
@ -2404,9 +2448,9 @@ void xlsx_producer::write_worksheet(const relationship &rel)
// See note for CT_Row, span attribute about block optimization
if (first_row_in_block)
{
first_check_row = first_row;
first_check_row = row;
// round up to the next multiple of 16
last_check_row = (((first_row - 1) / 16) + 1) * 16;
last_check_row = ((row / 16) + 1) * 16;
}
for (auto check_row = first_check_row; check_row <= last_check_row; ++check_row)
@ -2440,18 +2484,19 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{
const auto &props = ws.row_properties(row);
if (props.style.is_set())
{
write_attribute("s", props.style.get());
}
if (props.custom_format.is_set())
{
write_attribute("customFormat", write_bool(props.custom_format.get()));
}
if (props.height.is_set())
{
auto height = props.height.get();
if (std::fabs(height - std::floor(height)) == 0.0)
{
write_attribute("ht", std::to_string(static_cast<int>(height)) + ".0");
}
else
{
write_attribute("ht", height);
}
write_attribute("ht", serialize_number_to_string(height));
}
if (props.hidden)
@ -2574,19 +2619,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
case cell::type::number:
write_start_element(xmlns, "v");
if (is_integral(cell.value<double>()))
{
write_characters(static_cast<std::int64_t>(cell.value<double>()));
}
else
{
std::stringstream ss;
ss.precision(20);
ss << cell.value<double>();
write_characters(ss.str());
}
write_characters(serialize_number_to_string(cell.value<double>()));
write_end_element(xmlns, "v");
break;
@ -2697,14 +2730,49 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_end_element(xmlns, "hyperlinks");
}
if (ws.has_page_setup())
if (ws.d_->print_options_.is_set())
{
auto &opts = ws.d_->print_options_.get();
write_start_element(xmlns, "printOptions");
write_attribute("horizontalCentered", write_bool(ws.page_setup().horizontal_centered()));
write_attribute("verticalCentered", write_bool(ws.page_setup().vertical_centered()));
if (opts.print_grid_lines.is_set())
{
write_attribute("gridLines", write_bool(opts.print_grid_lines.get()));
}
if (opts.grid_lines_set.is_set())
{
write_attribute("gridLineSet", write_bool(opts.grid_lines_set.get()));
}
if (opts.print_headings.is_set())
{
write_attribute("headings", write_bool(opts.print_headings.get()));
}
if (opts.horizontal_centered.is_set())
{
write_attribute("horizontalCentered", write_bool(opts.horizontal_centered.get()));
}
if (opts.vertical_centered.is_set())
{
write_attribute("verticalCentered", write_bool(opts.vertical_centered.get()));
}
write_end_element(xmlns, "printOptions");
}
if (ws.has_phonetic_properties())
{
write_start_element(xmlns, phonetic_pr::Serialised_ID);
const auto &ph_props = ws.phonetic_properties();
write_attribute("fontId", ph_props.font_id());
if (ph_props.has_type())
{
write_attribute("type", phonetic_pr::type_as_string(ph_props.type()));
}
if (ph_props.has_alignment())
{
write_attribute("alignment", phonetic_pr::alignment_as_string(ph_props.alignment()));
}
write_end_element(xmlns, phonetic_pr::Serialised_ID);
}
if (ws.has_page_margins())
{
write_start_element(xmlns, "pageMargins");
@ -2743,11 +2811,21 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (ws.has_page_setup())
{
write_start_element(xmlns, "pageSetup");
write_attribute(
"orientation", ws.page_setup().orientation() == xlnt::orientation::landscape ? "landscape" : "portrait");
write_attribute("paperSize", static_cast<std::size_t>(ws.page_setup().paper_size()));
if (ws.page_setup().orientation_.is_set())
{
write_attribute("orientation", ws.page_setup().orientation_.get());
}
if (ws.page_setup().horizontal_dpi_.is_set())
{
write_attribute("horizontalDpi", ws.page_setup().horizontal_dpi_.get());
}
if (ws.page_setup().vertical_dpi_.is_set())
{
write_attribute("verticalDpi", ws.page_setup().vertical_dpi_.get());
}
/*write_attribute("paperSize", static_cast<std::size_t>(ws.page_setup().paper_size()));
write_attribute("fitToHeight", write_bool(ws.page_setup().fit_to_height()));
write_attribute("fitToWidth", write_bool(ws.page_setup().fit_to_width()));
write_attribute("fitToWidth", write_bool(ws.page_setup().fit_to_width()));*/
write_end_element(xmlns, "pageSetup");
}
@ -2903,6 +2981,11 @@ void xlsx_producer::write_worksheet(const relationship &rel)
}
}
if (ws.d_->extension_list_.is_set())
{
ws.d_->extension_list_.get().serialize(*current_part_serializer_, xmlns);
}
write_end_element(xmlns, "worksheet");
if (!worksheet_rels.empty())

View File

@ -0,0 +1,140 @@
#include <xlnt/packaging/ext_list.hpp>
#include <algorithm>
#include <detail/external/include_libstudxml.hpp>
namespace {
// send elements straight from parser to serialiser without modification
// runs until the end of the current open element
xlnt::uri roundtrip(xml::parser& p, xml::serializer &s)
{
xlnt::uri ext_uri;
int nest_level = 0;
while (nest_level > 0 || (p.peek() != xml::parser::event_type::end_element && p.peek() != xml::parser::event_type::eof))
{
switch (p.next())
{
case xml::parser::start_element:
{
++nest_level;
auto attribs = p.attribute_map();
s.start_element(p.qname());
if (nest_level == 1)
{
ext_uri = xlnt::uri(attribs.at(xml::qname("uri")).value);
}
auto current_ns = p.namespace_();
p.peek(); // to look into the new namespace
auto new_ns = p.namespace_(); // only before attributes?
if (new_ns != current_ns)
{
auto pref = p.prefix();
s.namespace_decl(new_ns, pref);
}
for (auto &ele : attribs)
{
s.attribute(ele.first.string(), ele.second.value);
}
break;
}
case xml::parser::end_element:
{
--nest_level;
s.end_element();
break;
}
case xml::parser::start_namespace_decl:
{
s.namespace_decl(p.namespace_(), p.prefix());
break;
}
case xml::parser::end_namespace_decl:
{ // nothing required here
break;
}
case xml::parser::characters:
{
s.characters(p.value());
break;
}
case xml::parser::eof:
return ext_uri;
case xml::parser::start_attribute:
case xml::parser::end_attribute:
default:
break;
}
}
return ext_uri;
}
}
namespace xlnt {
ext_list::ext::ext(xml::parser &parser, const std::string& ns)
{
std::ostringstream serialisation_stream;
xml::serializer s(serialisation_stream, "", 0);
s.start_element(xml::qname(ns, "wrap")); // wrapper for the xmlns declaration
s.namespace_decl(ns, "");
extension_ID_ = roundtrip(parser, s);
s.end_element(xml::qname(ns, "wrap"));
serialised_value_ = serialisation_stream.str();
}
ext_list::ext::ext(const uri &ID, const std::string &serialised)
: extension_ID_(ID), serialised_value_(serialised)
{}
void ext_list::ext::serialise(xml::serializer &serialiser, const std::string& ns)
{
std::istringstream ser(serialised_value_);
xml::parser p(ser, "", xml::parser::receive_default);
p.next_expect(xml::parser::event_type::start_element, xml::qname(ns, "wrap"));
roundtrip(p, serialiser);
p.next_expect(xml::parser::event_type::end_element, xml::qname(ns, "wrap"));
}
ext_list::ext_list(xml::parser &parser, const std::string& ns)
{
// begin with the start element already parsed
while (parser.peek() == xml::parser::start_element)
{
extensions_.push_back(ext(parser, ns));
}
// end without parsing the end element
}
void ext_list::serialize(xml::serializer &serialiser, const std::string& ns)
{
serialiser.start_element(ns, "extLst");
for (auto &ext : extensions_)
{
ext.serialise(serialiser, ns);
}
serialiser.end_element();
}
void ext_list::add_extension(const uri &ID, const std::string &element)
{
extensions_.push_back(ext{ID, element});
}
bool ext_list::has_extension(const uri &extension_uri) const
{
return extensions_.end() != std::find_if(extensions_.begin(), extensions_.end(),
[&extension_uri](const ext &ext) { return extension_uri == ext.extension_ID_; });
}
const ext_list::ext &ext_list::extension(const uri &extension_uri) const
{
return *std::find_if(extensions_.begin(), extensions_.end(),
[&extension_uri](const ext &ext) { return extension_uri == ext.extension_ID_; });
}
const std::vector<ext_list::ext> &ext_list::extensions() const
{
return extensions_;
}
}

View File

@ -78,16 +78,25 @@ path manifest::canonicalize(const std::vector<xlnt::relationship> &rels) const
return result;
}
bool manifest::has_relationship(const path &part, relationship_type type) const
bool manifest::has_relationship(const path &path, relationship_type type) const
{
if (relationships_.find(part) == relationships_.end()) return false;
for (const auto &rel : relationships_.at(part))
auto rels = relationships_.find(path);
if (rels == relationships_.end())
{
if (rel.second.type() == type) return true;
return false;
}
return rels->second.end() != std::find_if(rels->second.begin(), rels->second.end(),
[type](const std::pair<std::string, xlnt::relationship> &rel) { return rel.second.type() == type; });
}
return false;
bool manifest::has_relationship(const path &path, const std::string &rel_id) const
{
auto rels = relationships_.find(path);
if (rels == relationships_.end())
{
return false;
}
return rels->second.find(rel_id) != rels->second.end();
}
relationship manifest::relationship(const path &part, relationship_type type) const

View File

@ -26,11 +26,14 @@
#include <xlnt/styles/font.hpp>
namespace {
const std::string Default_Name = "Calibri";
constexpr double Default_Size = 12.0;
} // namespace
namespace xlnt {
font::font()
: name_("Calibri"),
size_(12.0)
{
}
@ -140,7 +143,11 @@ font &font::size(double size)
double font::size() const
{
return size_.get();
if (size_.is_set())
{
return size_.get();
}
return Default_Size;
}
bool font::has_name() const
@ -154,9 +161,13 @@ font &font::name(const std::string &name)
return *this;
}
std::string font::name() const
const std::string& font::name() const
{
return name_.get();
if (name_.is_set())
{
return name_.get();
}
return Default_Name;
}
bool font::has_color() const
@ -218,7 +229,7 @@ std::size_t font::family() const
return family_.get();
}
std::string font::scheme() const
const std::string& font::scheme() const
{
return scheme_.get();
}
@ -241,8 +252,8 @@ bool font::operator==(const font &other) const
if (has_color() != other.has_color()) return false;
if (has_color() && color() != other.color()) return false;
// charset
if (has_charset()!= other.has_charset()) return false;
if (has_charset() && charset()!= other.charset()) return false;
if (has_charset() != other.has_charset()) return false;
if (has_charset() && charset() != other.charset()) return false;
// modifiers
if (bold() != other.bold()) return false;
if (italic() != other.italic()) return false;

View File

@ -29,12 +29,9 @@ page_setup::page_setup()
: break_(xlnt::page_break::none),
sheet_state_(xlnt::sheet_state::visible),
paper_size_(xlnt::paper_size::letter),
orientation_(xlnt::orientation::portrait),
fit_to_page_(false),
fit_to_height_(false),
fit_to_width_(false),
horizontal_centered_(false),
vertical_centered_(false),
scale_(1)
{
}
@ -69,16 +66,6 @@ void page_setup::paper_size(xlnt::paper_size paper_size)
paper_size_ = paper_size;
}
orientation page_setup::orientation() const
{
return orientation_;
}
void page_setup::orientation(xlnt::orientation orientation)
{
orientation_ = orientation;
}
bool page_setup::fit_to_page() const
{
return fit_to_page_;
@ -109,26 +96,6 @@ void page_setup::fit_to_width(bool fit_to_width)
fit_to_width_ = fit_to_width;
}
void page_setup::horizontal_centered(bool horizontal_centered)
{
horizontal_centered_ = horizontal_centered;
}
bool page_setup::horizontal_centered() const
{
return horizontal_centered_;
}
void page_setup::vertical_centered(bool vertical_centered)
{
vertical_centered_ = vertical_centered;
}
bool page_setup::vertical_centered() const
{
return vertical_centered_;
}
void page_setup::scale(double scale)
{
scale_ = scale;

View File

@ -0,0 +1,143 @@
// Copyright (c) 2014-2018 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 <xlnt/worksheet/phonetic_pr.hpp>
#include <array>
namespace {
// Order of elements defined by phonetic_pr::Type enum
const std::array<std::string, 4> Types{
"fullwidthKatakana",
"halfwidthKatakana",
"Hiragana",
"noConversion"};
// Order of elements defined by phonetic_pr::alignment enum
const std::array<std::string, 4> alignments{
"Center",
"Distributed",
"Left",
"NoControl"};
} // namespace
namespace xlnt {
/// <summary>
/// out of line initialiser for static const member
/// </summary>
const std::string phonetic_pr::Serialised_ID = "phoneticPr";
phonetic_pr::phonetic_pr(font_id_t font)
: font_id_(font)
{
}
void phonetic_pr::serialise(std::ostream &output_stream) const
{
output_stream << '<' << Serialised_ID << R"( fontID=")" << std::to_string(font_id_) << '"';
if (has_type())
{
output_stream << R"( type=")" << type_as_string(type_.get()) << '"';
}
if (has_alignment())
{
output_stream << R"( alignment=")" << alignment_as_string(alignment_.get()) << '"';
}
output_stream << "/>";
}
phonetic_pr::font_id_t phonetic_pr::font_id() const
{
return font_id_;
}
void phonetic_pr::font_id(font_id_t font)
{
font_id_ = font;
}
bool phonetic_pr::has_type() const
{
return type_.is_set();
}
phonetic_pr::phonetic_type phonetic_pr::type() const
{
return type_.get();
}
void phonetic_pr::type(phonetic_type type)
{
type_.set(type);
}
bool phonetic_pr::has_alignment() const
{
return alignment_.is_set();
}
phonetic_pr::align phonetic_pr::alignment() const
{
return alignment_.get();
}
void phonetic_pr::alignment(align align)
{
alignment_.set(align);
}
// serialisation
const std::string &phonetic_pr::type_as_string(phonetic_pr::phonetic_type type)
{
return Types[static_cast<int>(type)];
}
phonetic_pr::phonetic_type phonetic_pr::type_from_string(const std::string &str)
{
for (std::size_t i = 0; i < Types.size(); ++i)
{
if (str == Types[i])
{
return static_cast<phonetic_type>(i);
}
}
return phonetic_type::no_conversion;
}
const std::string &phonetic_pr::alignment_as_string(align type)
{
return alignments[static_cast<int>(type)];
}
phonetic_pr::align phonetic_pr::alignment_from_string(const std::string &str)
{
for (std::size_t i = 0; i < alignments.size(); ++i)
{
if (str == alignments[i])
{
return static_cast<align>(i);
}
}
return align::no_control;
}
} // namespace xlnt

View File

@ -26,10 +26,6 @@
#include <cmath>
#include <limits>
#include <detail/constants.hpp>
#include <detail/implementations/cell_impl.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/implementations/worksheet_impl.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/cell/index_types.hpp>
@ -46,6 +42,10 @@
#include <xlnt/worksheet/range_iterator.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <detail/constants.hpp>
#include <detail/implementations/cell_impl.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/implementations/worksheet_impl.hpp>
namespace {
@ -242,25 +242,31 @@ std::string worksheet::title() const
void worksheet::title(const std::string &title)
{
if (title.length() > 31)
// do no work if we don't need to
if (d_->title_ == title)
{
return;
}
// excel limits worksheet titles to 31 characters
if (title.empty() || title.length() > 31)
{
throw invalid_sheet_title(title);
}
// invalid characters in a worksheet name
if (title.find_first_of("*:/\\?[]") != std::string::npos)
{
throw invalid_sheet_title(title);
}
auto same_title = std::find_if(workbook().begin(), workbook().end(),
[&](worksheet ws) { return ws.title() == title; });
if (same_title != workbook().end() && *same_title != *this)
// try and insert the new name into the worksheets map
// if the insert fails, we have a duplicate sheet name
auto insert_result = workbook().d_->sheet_title_rel_id_map_.insert(
std::make_pair(title, workbook().d_->sheet_title_rel_id_map_[d_->title_]));
if (!insert_result.second) // insert failed, duplication detected
{
throw invalid_sheet_title(title);
}
workbook().d_->sheet_title_rel_id_map_[title] = workbook().d_->sheet_title_rel_id_map_[d_->title_];
// if the insert succeeded (i.e. wasn't a duplicate sheet name)
// update the worksheet title and remove the old relation
workbook().d_->sheet_title_rel_id_map_.erase(d_->title_);
d_->title_ = title;
@ -284,13 +290,17 @@ void worksheet::freeze_panes(xlnt::cell top_left_cell)
void worksheet::freeze_panes(const cell_reference &ref)
{
if (ref == "A1")
{
unfreeze_panes();
return;
}
if (!has_view())
{
d_->views_.push_back(sheet_view());
}
auto &primary_view = d_->views_.front();
if (!primary_view.has_pane())
{
primary_view.pane(pane());
@ -300,40 +310,23 @@ void worksheet::freeze_panes(const cell_reference &ref)
primary_view.pane().state = pane_state::frozen;
primary_view.clear_selections();
primary_view.add_selection(selection());
if (ref == "A1")
if (ref.column() == "A") // no column is frozen
{
unfreeze_panes();
}
else if (ref.column() == "A")
{
primary_view.add_selection(selection());
primary_view.selection(0).pane(pane_corner::bottom_left);
primary_view.selection(0).active_cell(ref.make_offset(0, -1)); // cell above
primary_view.selection(1).active_cell(ref);
primary_view.add_selection(selection(pane_corner::bottom_left, ref));
primary_view.pane().active_pane = pane_corner::bottom_left;
primary_view.pane().y_split = ref.row() - 1;
}
else if (ref.row() == 1)
else if (ref.row() == 1) // no row is frozen
{
primary_view.add_selection(selection());
primary_view.selection(0).pane(pane_corner::top_right);
primary_view.selection(0).active_cell(ref.make_offset(-1, 0)); // cell to the left
primary_view.selection(1).active_cell(ref);
primary_view.add_selection(selection(pane_corner::top_right, ref));
primary_view.pane().active_pane = pane_corner::top_right;
primary_view.pane().x_split = ref.column_index() - 1;
}
else
else // column and row is frozen
{
primary_view.add_selection(selection());
primary_view.add_selection(selection());
primary_view.selection(0).pane(pane_corner::top_right);
primary_view.selection(0).active_cell(ref.make_offset(0, -1)); // cell above
primary_view.selection(1).pane(pane_corner::bottom_left);
primary_view.selection(1).active_cell(ref.make_offset(-1, 0)); // cell to the left
primary_view.selection(2).pane(pane_corner::bottom_right);
primary_view.selection(2).active_cell(ref);
primary_view.add_selection(selection(pane_corner::top_right, cell_reference(ref.column(), 1)));
primary_view.add_selection(selection(pane_corner::bottom_left, cell_reference(1, ref.row())));
primary_view.add_selection(selection(pane_corner::bottom_right, ref));
primary_view.pane().active_pane = pane_corner::bottom_right;
primary_view.pane().x_split = ref.column_index() - 1;
primary_view.pane().y_split = ref.row() - 1;
@ -361,11 +354,12 @@ void worksheet::active_cell(const cell_reference &ref)
if (!primary_view.has_selections())
{
primary_view.add_selection(selection());
primary_view.add_selection(selection(pane_corner::bottom_right, ref));
}
else
{
primary_view.selection(0).active_cell(ref);
}
auto &primary_selection = primary_view.selection(0);
primary_selection.active_cell(ref);
}
bool worksheet::has_active_cell() const
@ -757,6 +751,7 @@ void worksheet::clear_cell(const cell_reference &ref)
void worksheet::clear_row(row_t row)
{
d_->cell_map_.erase(row);
d_->row_properties_.erase(row);
// TODO: garbage collect newly unreferenced resources such as styles?
}
@ -1032,6 +1027,21 @@ void worksheet::register_calc_chain_in_manifest()
workbook().register_workbook_part(relationship_type::calculation_chain);
}
bool worksheet::has_phonetic_properties() const
{
return d_->phonetic_properties_.is_set();
}
const phonetic_pr& worksheet::phonetic_properties() const
{
return d_->phonetic_properties_.get();
}
void worksheet::phonetic_properties(const phonetic_pr& phonetic_props)
{
d_->phonetic_properties_.set(phonetic_props);
}
bool worksheet::has_header_footer() const
{
return d_->header_footer_.is_set();
@ -1108,7 +1118,7 @@ void worksheet::parent(xlnt::workbook &wb)
conditional_format worksheet::conditional_format(const range_reference &ref, const condition &when)
{
return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when);
return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when);
}
path worksheet::path() const

View File

@ -3,28 +3,58 @@
#include <cmath>
#include <exception>
#define xlnt_assert(expression) do\
{\
try { if (expression) break; }\
catch (...) {}\
throw xlnt::exception("test failed");\
#define XLNT_STRINGIFYX(x) #x
#define XLNT_STRINGIFY(x) XLNT_STRINGIFYX(x)
#define xlnt_assert(expression) \
do \
{ \
try \
{ \
if (expression) break; \
} \
catch (std::exception & ex) \
{ \
throw ex; \
} \
catch (...) \
{ \
} \
throw xlnt::exception( \
"assert failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \
} while (false)
#define xlnt_assert_throws_nothing(expression) do\
{\
try { expression; break; }\
catch (...) {}\
throw xlnt::exception("test failed");\
#define xlnt_assert_throws_nothing(expression) \
do \
{ \
try \
{ \
expression; \
break; \
} \
catch (...) \
{ \
} \
throw xlnt::exception("assert throws nothing failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \
} while (false)
#define xlnt_assert_throws(expression, exception_type) do\
{\
try { expression; }\
catch (exception_type) { break; }\
catch (...) {}\
throw xlnt::exception("test failed");\
#define xlnt_assert_throws(expression, exception_type) \
do \
{ \
try \
{ \
expression; \
} \
catch (exception_type) \
{ \
break; \
} \
catch (...) \
{ \
} \
throw xlnt::exception("assert throws failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \
} while (false)
#define xlnt_assert_equals(left, right) xlnt_assert(left == right)
#define xlnt_assert_differs(left, right) xlnt_assert(left != right)
#define xlnt_assert_delta(left, right, delta) xlnt_assert(std::abs(left - right) <= delta)
#define xlnt_assert_equals(left, right) xlnt_assert((left) == (right))
#define xlnt_assert_differs(left, right) xlnt_assert((left) != (right))
#define xlnt_assert_delta(left, right, delta) xlnt_assert(std::abs((left) - (right)) <= (delta))

View File

@ -39,9 +39,17 @@ public:
std::cout << ".";
status.tests_passed++;
}
catch (std::exception &ex)
{
std::string fail_msg = test.second + " failed with:\n" + std::string(ex.what());
std::cout << "*\n"
<< fail_msg << '\n';
status.tests_failed++;
status.failures.push_back(fail_msg);
}
catch (...)
{
std::cout << "*";
std::cout << "*\n" << test.second << " failed\n";
status.tests_failed++;
status.failures.push_back(test.second);
}

View File

@ -63,11 +63,11 @@ void run_tests()
void print_summary()
{
std::cout << std::endl;
std::cout << "\n\n";
for (auto failure : overall_status.failures)
{
std::cout << failure << " failed" << std::endl;
std::cout << failure << "\n\n";
}
}

View File

@ -320,7 +320,7 @@ public:
xlnt::workbook wb;
const auto path = path_helper::test_file("6_encrypted_libre.xlsx");
xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception);
xlnt_assert_throws_nothing(wb.load(path, u8"пароль"));
xlnt_assert_throws_nothing(wb.load(path, u8"\u043F\u0430\u0440\u043E\u043B\u044C")); // u8"пароль"
}
void test_decrypt_standard()
@ -343,16 +343,20 @@ public:
{
#ifdef _MSC_VER
xlnt::workbook wb;
const auto path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_Λ.xlsx";
// L"/9_unicode_Λ.xlsx" doesn't use wchar_t(0x039B) for the capital lambda...
// L"/9_unicode_\u039B.xlsx" gives the corrct output
const auto path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_\u039B.xlsx"; // L"/9_unicode_Λ.xlsx"
wb.load(path);
xlnt_assert_equals(wb.active_sheet().cell("A1").value<std::string>(), u8"unicodê!");
xlnt_assert_equals(wb.active_sheet().cell("A1").value<std::string>(), u8"un\u00EFc\u00F4d\u0117!"); // u8"unïcôdė!"
#endif
#ifndef __MINGW32__
xlnt::workbook wb2;
const auto path2 = U8STRING_LITERAL(XLNT_TEST_DATA_DIR) u8"/9_unicode_Λ.xlsx";
// u8"/9_unicode_Λ.xlsx" doesn't use 0xc3aa for the capital lambda...
// u8"/9_unicode_\u039B.xlsx" gives the corrct output
const auto path2 = U8STRING_LITERAL(XLNT_TEST_DATA_DIR) u8"/9_unicode_\u039B.xlsx"; // u8"/9_unicode_Λ.xlsx"
wb2.load(path2);
xlnt_assert_equals(wb2.active_sheet().cell("A1").value<std::string>(), u8"unicodê!");
xlnt_assert_equals(wb2.active_sheet().cell("A1").value<std::string>(), u8"un\u00EFc\u00F4d\u0117!"); // u8"unïcôdė!"
#endif
}
@ -393,7 +397,6 @@ public:
xlnt_assert(ws1.cell("A7").has_hyperlink());
xlnt_assert_equals(ws1.cell("A7").value<std::string>(), "mailto:invalid@example.com?subject=important");
xlnt_assert_equals(ws1.cell("A7").hyperlink().url(), "mailto:invalid@example.com?subject=important");
}
void test_read_formulae()
@ -471,11 +474,11 @@ public:
xlnt_assert_equals(ws.cell("B4").value<std::string>(), "B4");
xlnt_assert_equals(ws.cell("D4").value<std::string>(), "D4");
xlnt_assert_equals(ws.row_properties(1).height.get(), 100);
xlnt_assert_equals(ws.row_properties(1).height.get(), 99.95);
xlnt_assert(!ws.row_properties(2).height.is_set());
xlnt_assert_equals(ws.row_properties(3).height.get(), 100);
xlnt_assert_equals(ws.row_properties(3).height.get(), 99.95);
xlnt_assert(!ws.row_properties(4).height.is_set());
xlnt_assert_equals(ws.row_properties(5).height.get(), 100);
xlnt_assert_equals(ws.row_properties(5).height.get(), 99.95);
auto width = ((16.0 * 7) - 5) / 7;
@ -520,13 +523,13 @@ public:
ws.row_properties(i).dy_descent = 0.2;
}
ws.row_properties(1).height = 100;
ws.row_properties(1).height = 99.95;
ws.row_properties(1).custom_height = true;
ws.row_properties(3).height = 100;
ws.row_properties(3).height = 99.95;
ws.row_properties(3).custom_height = true;
ws.row_properties(5).height = 100;
ws.row_properties(5).height = 99.95;
ws.row_properties(5).custom_height = true;
auto width = ((16.0 * 7) - 5) / 7;
@ -540,9 +543,6 @@ public:
ws.column_properties("E").width = width;
ws.column_properties("E").custom_width = true;
wb.default_slicer_style("SlicerStyleLight1");
wb.enable_known_fonts();
xlnt_assert(workbook_matches_file(wb,
path_helper::test_file("13_custom_heights_widths.xlsx")));
}
@ -610,7 +610,9 @@ public:
void test_round_trip_rw_unicode()
{
xlnt_assert(round_trip_matches_rw(path_helper::test_file(u8"9_unicode_Λ.xlsx")));
// u8"/9_unicode_Λ.xlsx" doesn't use 0xc3aa for the capital lambda...
// u8"/9_unicode_\u039B.xlsx" gives the corrct output
xlnt_assert(round_trip_matches_rw(path_helper::test_file(u8"9_unicode_\u039B.xlsx")));
}
void test_round_trip_rw_comments_hyperlinks_formulae()
@ -640,7 +642,7 @@ public:
void test_round_trip_rw_encrypted_libre()
{
xlnt_assert(round_trip_matches_rw(path_helper::test_file("6_encrypted_libre.xlsx"), u8"пароль"));
xlnt_assert(round_trip_matches_rw(path_helper::test_file("6_encrypted_libre.xlsx"), u8"\u043F\u0430\u0440\u043E\u043B\u044C")); // u8"пароль"
}
void test_round_trip_rw_encrypted_standard()

View File

@ -44,9 +44,9 @@ public:
ps.paper_size(xlnt::paper_size::executive);
xlnt_assert_equals(ps.paper_size(), xlnt::paper_size::executive);
xlnt_assert_equals(ps.orientation(), xlnt::orientation::portrait);
ps.orientation(xlnt::orientation::landscape);
xlnt_assert_equals(ps.orientation(), xlnt::orientation::landscape);
xlnt_assert(!ps.orientation_.is_set());
ps.orientation_.set(xlnt::orientation::landscape);
xlnt_assert_equals(ps.orientation_.get(), xlnt::orientation::landscape);
xlnt_assert(!ps.fit_to_page());
ps.fit_to_page(true);
@ -59,13 +59,5 @@ public:
xlnt_assert(!ps.fit_to_width());
ps.fit_to_width(true);
xlnt_assert(ps.fit_to_width());
xlnt_assert(!ps.horizontal_centered());
ps.horizontal_centered(true);
xlnt_assert(ps.horizontal_centered());
xlnt_assert(!ps.vertical_centered());
ps.vertical_centered(true);
xlnt_assert(ps.vertical_centered());
}
};

View File

@ -25,10 +25,10 @@
#include <iostream>
#include <helpers/test_suite.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/header_footer.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <helpers/test_suite.hpp>
class worksheet_test_suite : public test_suite
{
@ -101,6 +101,7 @@ public:
register_test(test_view_properties_serialization);
register_test(test_clear_cell);
register_test(test_clear_row);
register_test(test_set_title);
}
void test_new_worksheet()
@ -224,12 +225,44 @@ public:
{
xlnt::workbook wb;
auto ws = wb.active_sheet();
ws.cell("A1").hyperlink("http://test.com");
xlnt_assert_equals(ws.cell("A1").hyperlink().url(), "http://test.com");
xlnt_assert_equals(ws.cell("A1").value<std::string>(), "");
ws.cell("A1").value("test");
xlnt_assert_equals("test", ws.cell("A1").value<std::string>());
xlnt_assert_equals(ws.cell("A1").hyperlink().url(), "http://test.com");
std::string test_link = "http://test.com";
xlnt::cell_reference test_cell("A1");
ws.cell(test_cell).hyperlink(test_link);
// when a hyperlink is added to an empty cell, the display text becomes == to the link
xlnt_assert_equals(ws.cell(test_cell).hyperlink().url(), test_link);
xlnt_assert_equals(ws.cell(test_cell).value<std::string>(), test_link);
// if the display value changes, the hyperlink remains the same
std::string test_string = "test";
ws.cell(test_cell).value(test_string);
xlnt_assert_equals(test_string, ws.cell(test_cell).value<std::string>());
xlnt_assert_equals(ws.cell(test_cell).hyperlink().url(), test_link);
// changing the link doesn't change the cell value
std::string test_link2 = "http://test-123.com";
ws.cell(test_cell).hyperlink(test_link2);
xlnt_assert_equals(test_string, ws.cell(test_cell).value<std::string>());
xlnt_assert_equals(ws.cell(test_cell).hyperlink().url(), test_link2);
// and we can edit both value and hyperlink together
std::string test_string3 = "123test";
std::string test_link3 = "http://123-test.com";
ws.cell(test_cell).hyperlink(test_link3, test_string3);
xlnt_assert_equals(test_string3, ws.cell(test_cell).value<std::string>());
xlnt_assert_equals(ws.cell(test_cell).hyperlink().url(), test_link3);
// hyperlinks can also be applied to cells with non-string values
int numeric_test_val = 123;
std::string test_link4 = "http://test-numeric.com";
xlnt::cell_reference numeric_test_cell("B1");
ws.cell(numeric_test_cell).value(numeric_test_val);
ws.cell(numeric_test_cell).hyperlink(test_link4);
xlnt_assert_equals(ws.cell(numeric_test_cell).hyperlink().url(), test_link4);
xlnt_assert_equals(ws.cell(numeric_test_cell).value<int>(), numeric_test_val);
// and there should be no issues if two cells use the same hyperlink
ws.cell(numeric_test_cell).hyperlink(test_link3); // still in use on 'A1'
// 'A1'
xlnt_assert_equals(test_string3, ws.cell(test_cell).value<std::string>());
xlnt_assert_equals(ws.cell(test_cell).hyperlink().url(), test_link3);
// 'B1'
xlnt_assert_equals(ws.cell(numeric_test_cell).hyperlink().url(), test_link3);
xlnt_assert_equals(ws.cell(numeric_test_cell).value<int>(), numeric_test_val);
}
void test_rows()
@ -376,7 +409,6 @@ public:
xlnt_assert(!merged.contains("O1"));
}
void test_merged_cell_ranges()
{
xlnt::workbook wb;
@ -391,7 +423,7 @@ public:
ws.cell("A1").value(1);
ws.cell("D4").value(16);
ws.merge_cells("A1:D4");
std::vector<xlnt::range_reference> expected = { xlnt::range_reference("A1:D4") };
std::vector<xlnt::range_reference> expected = {xlnt::range_reference("A1:D4")};
xlnt_assert_equals(ws.merged_ranges(), expected);
xlnt_assert(!ws.cell("D4").has_value());
}
@ -460,10 +492,13 @@ public:
ws.freeze_panes("A4");
auto view = ws.view();
xlnt_assert_equals(view.selections().size(), 2);
xlnt_assert_equals(view.selections()[0].active_cell(), "A3");
xlnt_assert_equals(view.selections().size(), 1);
// pane is the corner of the worksheet that this selection extends to
// active cell is the last selected cell in the selection
// sqref is the last selected block in the selection
xlnt_assert_equals(view.selections()[0].pane(), xlnt::pane_corner::bottom_left);
xlnt_assert_equals(view.selections()[0].sqref(), "A1");
xlnt_assert_equals(view.selections()[0].active_cell(), "A4");
xlnt_assert_equals(view.selections()[0].sqref(), "A4");
xlnt_assert_equals(view.pane().active_pane, xlnt::pane_corner::bottom_left);
xlnt_assert_equals(view.pane().state, xlnt::pane_state::frozen);
xlnt_assert_equals(view.pane().top_left_cell.get(), "A4");
@ -477,10 +512,13 @@ public:
ws.freeze_panes("D1");
auto view = ws.view();
xlnt_assert_equals(view.selections().size(), 2);
xlnt_assert_equals(view.selections()[0].active_cell(), "C1");
xlnt_assert_equals(view.selections().size(), 1);
// pane is the corner of the worksheet that this selection extends to
// active cell is the last selected cell in the selection
// sqref is the last selected block in the selection
xlnt_assert_equals(view.selections()[0].pane(), xlnt::pane_corner::top_right);
xlnt_assert_equals(view.selections()[0].sqref(), "A1");
xlnt_assert_equals(view.selections()[0].active_cell(), "D1");
xlnt_assert_equals(view.selections()[0].sqref(), "D1");
xlnt_assert_equals(view.pane().active_pane, xlnt::pane_corner::top_right);
xlnt_assert_equals(view.pane().state, xlnt::pane_state::frozen);
xlnt_assert_equals(view.pane().top_left_cell.get(), "D1");
@ -495,11 +533,18 @@ public:
auto view = ws.view();
xlnt_assert_equals(view.selections().size(), 3);
// pane is the corner of the worksheet that this selection extends to
// active cell is the last selected cell in the selection
// sqref is the last selected block in the selection
xlnt_assert_equals(view.selections()[0].pane(), xlnt::pane_corner::top_right);
xlnt_assert_equals(view.selections()[0].active_cell(), "D1");
xlnt_assert_equals(view.selections()[0].sqref(), "D1");
xlnt_assert_equals(view.selections()[1].pane(), xlnt::pane_corner::bottom_left);
xlnt_assert_equals(view.selections()[2].active_cell(), "D4");
xlnt_assert_equals(view.selections()[1].active_cell(), "A4");
xlnt_assert_equals(view.selections()[1].sqref(), "A4");
xlnt_assert_equals(view.selections()[2].pane(), xlnt::pane_corner::bottom_right);
xlnt_assert_equals(view.selections()[2].sqref(), "A1");
xlnt_assert_equals(view.selections()[2].active_cell(), "D4");
xlnt_assert_equals(view.selections()[2].sqref(), "D4");
xlnt_assert_equals(view.pane().active_pane, xlnt::pane_corner::bottom_right);
xlnt_assert_equals(view.pane().state, xlnt::pane_state::frozen);
xlnt_assert_equals(view.pane().top_left_cell.get(), "D4");
@ -825,7 +870,7 @@ public:
xlnt::header_footer hf;
using hf_loc = xlnt::header_footer::location;
for (auto location : { hf_loc::left, hf_loc::center, hf_loc::right })
for (auto location : {hf_loc::left, hf_loc::center, hf_loc::right})
{
xlnt_assert(!hf.has_header(location));
xlnt_assert(!hf.has_odd_even_header(location));
@ -850,7 +895,7 @@ public:
xlnt::header_footer hf;
using hf_loc = xlnt::header_footer::location;
for (auto location : { hf_loc::left, hf_loc::center, hf_loc::right })
for (auto location : {hf_loc::left, hf_loc::center, hf_loc::right})
{
xlnt_assert(!hf.has_footer(location));
xlnt_assert(!hf.has_odd_even_footer(location));
@ -1186,11 +1231,11 @@ public:
xlnt_assert_equals(ws.highest_row(), last_row);
wb.save("temp.xlsx");
xlnt::workbook wb2;
wb2.load("temp.xlsx");
auto ws2 = wb2.active_sheet();
xlnt_assert_equals(ws2.calculate_dimension().height(), height);
xlnt_assert(!ws2.has_cell(xlnt::cell_reference(1, last_row)));
}
@ -1212,12 +1257,42 @@ public:
xlnt_assert_equals(ws.highest_row(), last_row - 1);
wb.save("temp.xlsx");
xlnt::workbook wb2;
wb2.load("temp.xlsx");
auto ws2 = wb2.active_sheet();
xlnt_assert_equals(ws2.calculate_dimension().height(), height - 1);
xlnt_assert(!ws2.has_cell(xlnt::cell_reference(1, last_row)));
}
void test_set_title()
{
xlnt::workbook wb;
auto ws1 = wb.active_sheet();
// empty titles are invalid
xlnt_assert_throws(ws1.title(""), xlnt::invalid_sheet_title);
// titles longer than 31 chars are invalid
std::string test_long_title(32, 'a');
xlnt_assert(test_long_title.size() > 31);
xlnt_assert_throws(ws1.title(test_long_title), xlnt::invalid_sheet_title);
// titles containing any of the following characters are invalid
std::string invalid_chars = "*:/\\?[]";
for (char &c : invalid_chars)
{
std::string invalid_char = std::string("Sheet") + c;
xlnt_assert_throws(ws1.title(invalid_char), xlnt::invalid_sheet_title);
}
// duplicate names are invalid
auto ws2 = wb.create_sheet();
xlnt_assert_throws(ws2.title(ws1.title()), xlnt::invalid_sheet_title);
xlnt_assert_throws(ws1.title(ws2.title()), xlnt::invalid_sheet_title);
// naming as self is valid and is ignored
auto ws1_title = ws1.title();
auto ws2_title = ws2.title();
xlnt_assert_throws_nothing(ws1.title(ws1.title()));
xlnt_assert_throws_nothing(ws2.title(ws2.title()));
xlnt_assert(ws1_title == ws1.title());
xlnt_assert(ws2_title == ws2.title());
}
};