work on hyperlinks, x14 extentions, other round tripping silliness

This commit is contained in:
Thomas Fussell 2018-03-16 21:21:16 -04:00
parent 2426215801
commit 410e73d594
18 changed files with 474 additions and 59 deletions

View File

@ -47,6 +47,8 @@ class font;
class format; class format;
class number_format; class number_format;
class protection; class protection;
class range;
class relationship;
class style; class style;
class workbook; class workbook;
class worksheet; class worksheet;
@ -251,9 +253,9 @@ public:
// hyperlink // hyperlink
/// <summary> /// <summary>
/// Returns the URL of this cell's hyperlink. /// Returns the relationship of this cell's hyperlink.
/// </summary> /// </summary>
std::string hyperlink() const; class hyperlink hyperlink() const;
/// <summary> /// <summary>
/// Adds a hyperlink to this cell pointing to the URL of the given value. /// Adds a hyperlink to this cell pointing to the URL of the given value.
@ -271,6 +273,11 @@ public:
/// </summary> /// </summary>
void hyperlink(xlnt::cell target); void hyperlink(xlnt::cell target);
/// <summary>
/// Adds an internal hyperlink to this cell pointing to the given range.
/// </summary>
void hyperlink(xlnt::range target);
/// <summary> /// <summary>
/// Returns true if this cell has a hyperlink set. /// Returns true if this cell has a hyperlink set.
/// </summary> /// </summary>

View File

@ -0,0 +1,61 @@
// Copyright (c) 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>
namespace xlnt {
namespace detail {
struct hyperlink_impl;
}
class cell;
class range;
class relationship;
/// <summary>
/// Describes a hyperlink pointing from a cell to another cell or a URL.
/// </summary>
class XLNT_API hyperlink
{
public:
bool external() const;
class relationship relationship() const;
std::string url() const;
std::string target_range() const;
void display(const std::string &value);
std::string display() const;
void tooltip(const std::string &value);
std::string tooltip() const;
private:
friend class cell;
hyperlink(detail::hyperlink_impl *d);
detail::hyperlink_impl *d_;
};
} // namespace xlnt

View File

@ -682,6 +682,31 @@ public:
/// </summary> /// </summary>
void clear_styles(); void clear_styles();
/// <summary>
/// Sets the default slicer style to the given value.
/// </summary>
void default_slicer_style(const std::string &value);
/// <summary>
/// Returns the default slicer style.
/// </summary>
std::string default_slicer_style() const;
/// <summary>
/// Enables knownFonts in stylesheet.
/// </summary>
void enable_known_fonts();
/// <summary>
/// Disables knownFonts in stylesheet.
/// </summary>
void disable_known_fonts();
/// <summary>
/// Returns true if knownFonts are enabled in the stylesheet.
/// </summary>
bool known_fonts_enabled() const;
// Manifest // Manifest
/// <summary> /// <summary>

View File

@ -35,10 +35,15 @@ class XLNT_API row_properties
{ {
public: public:
/// <summary> /// <summary>
/// Optional height /// Row height
/// </summary> /// </summary>
optional<double> height; optional<double> height;
/// <summary>
/// Distance in pixels from the bottom of the cell to the baseline of the cell content
/// </summary>
optional<double> dy_descent;
/// <summary> /// <summary>
/// Whether or not the height is different from the default /// Whether or not the height is different from the default
/// </summary> /// </summary>

View File

@ -30,6 +30,7 @@
#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/hyperlink.hpp>
#include <xlnt/cell/rich_text.hpp> #include <xlnt/cell/rich_text.hpp>
#include <xlnt/cell/index_types.hpp> #include <xlnt/cell/index_types.hpp>
#include <xlnt/cell/rich_text_run.hpp> #include <xlnt/cell/rich_text_run.hpp>

View File

@ -28,11 +28,13 @@
#include <detail/implementations/cell_impl.hpp> #include <detail/implementations/cell_impl.hpp>
#include <detail/implementations/format_impl.hpp> #include <detail/implementations/format_impl.hpp>
#include <detail/implementations/hyperlink_impl.hpp>
#include <detail/implementations/stylesheet.hpp> #include <detail/implementations/stylesheet.hpp>
#include <detail/implementations/worksheet_impl.hpp> #include <detail/implementations/worksheet_impl.hpp>
#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/hyperlink.hpp>
#include <xlnt/cell/rich_text.hpp> #include <xlnt/cell/rich_text.hpp>
#include <xlnt/packaging/manifest.hpp> #include <xlnt/packaging/manifest.hpp>
#include <xlnt/packaging/relationship.hpp> #include <xlnt/packaging/relationship.hpp>
@ -196,7 +198,7 @@ cell::cell(detail::cell_impl *d)
bool cell::garbage_collectible() const bool cell::garbage_collectible() const
{ {
return !(has_value() || is_merged() || has_formula() || has_format()); return !(has_value() || is_merged() || has_formula() || has_format() || has_hyperlink());
} }
void cell::value(std::nullptr_t) void cell::value(std::nullptr_t)
@ -360,9 +362,9 @@ cell &cell::operator=(const cell &rhs)
return *this; return *this;
} }
std::string cell::hyperlink() const hyperlink cell::hyperlink() const
{ {
return d_->hyperlink_.get(); return xlnt::hyperlink(&d_->hyperlink_.get());
} }
void cell::hyperlink(const std::string &hyperlink) void cell::hyperlink(const std::string &hyperlink)
@ -377,31 +379,50 @@ void cell::hyperlink(const std::string &hyperlink)
auto &manifest = ws.workbook().manifest(); auto &manifest = ws.workbook().manifest();
bool existing = false; bool existing = false;
d_->hyperlink_ = detail::hyperlink_impl();
// check for existing relationships
for (const auto &rel : manifest.relationships(ws.path(), relationship_type::hyperlink)) for (const auto &rel : manifest.relationships(ws.path(), relationship_type::hyperlink))
{ {
if (rel.target().path().string() == hyperlink) if (rel.target().path().string() == hyperlink)
{ {
d_->hyperlink_.get().relationship = rel;
existing = true; existing = true;
break;
} }
} }
// register a new relationship
if (!existing) { if (!existing) {
manifest.register_relationship(uri(ws.path().string()), relationship_type::hyperlink, auto rel_id = manifest.register_relationship(
uri(hyperlink), target_mode::external); uri(ws.path().string()),
relationship_type::hyperlink,
uri(hyperlink),
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);
} }
d_->hyperlink_ = hyperlink; value(hyperlink);
} }
void cell::hyperlink(const std::string &url, const std::string &display) void cell::hyperlink(const std::string &url, const std::string &display)
{ {
hyperlink(url); hyperlink(url);
d_->hyperlink_.get().display = display;
value(display); value(display);
} }
void cell::hyperlink(xlnt::cell /*target*/) void cell::hyperlink(xlnt::cell target)
{ {
//todo: implement // TODO: should this computed value be a method on a cell?
const auto cell_address = target.worksheet().title() + "!" + target.reference().to_string();
d_->hyperlink_ = detail::hyperlink_impl();
d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
uri(""), uri(cell_address), target_mode::internal);
d_->hyperlink_.get().display = cell_address;
value(cell_address);
} }
void cell::formula(const std::string &formula) void cell::formula(const std::string &formula)
@ -420,8 +441,6 @@ void cell::formula(const std::string &formula)
d_->formula_ = formula; d_->formula_ = formula;
} }
data_type(type::number);
worksheet().register_calc_chain_in_manifest(); worksheet().register_calc_chain_in_manifest();
} }

91
source/cell/hyperlink.cpp Normal file
View File

@ -0,0 +1,91 @@
// Copyright (c) 2014-2018 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#include <detail/implementations/hyperlink_impl.hpp>
#include <xlnt/cell/hyperlink.hpp>
#include <xlnt/utils/exceptions.hpp>
namespace xlnt {
hyperlink::hyperlink(detail::hyperlink_impl *d) : d_(d)
{
}
relationship hyperlink::relationship() const
{
if (!external())
{
throw xlnt::exception("only external hyperlinks have associated relationships");
}
return d_->relationship;
}
std::string hyperlink::url() const
{
if (!external())
{
throw xlnt::exception("only external hyperlinks have associated urls");
}
return d_->relationship.target().to_string();
}
std::string hyperlink::target_range() const
{
if (external())
{
throw xlnt::exception("only internal hyperlinks have a target range");
}
return d_->relationship.target().to_string();
}
bool hyperlink::external() const
{
return d_->relationship.target_mode() == target_mode::external;
}
void hyperlink::display(const std::string &value)
{
d_->display = value;
}
std::string hyperlink::display() const
{
return d_->display;
}
void hyperlink::tooltip(const std::string &value)
{
d_->tooltip = value;
}
std::string hyperlink::tooltip() const
{
return d_->tooltip;
}
} // namespace xlnt

View File

@ -21,9 +21,10 @@
// //
// @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/worksheet/worksheet.hpp> #include <xlnt/worksheet/worksheet.hpp>
#include "cell_impl.hpp" #include <detail/implementations/cell_impl.hpp>
namespace xlnt { namespace xlnt {
namespace detail { namespace detail {

View File

@ -29,7 +29,9 @@
#include <xlnt/cell/comment.hpp> #include <xlnt/cell/comment.hpp>
#include <xlnt/cell/rich_text.hpp> #include <xlnt/cell/rich_text.hpp>
#include <xlnt/cell/index_types.hpp> #include <xlnt/cell/index_types.hpp>
#include <xlnt/packaging/relationship.hpp>
#include <xlnt/utils/optional.hpp> #include <xlnt/utils/optional.hpp>
#include <detail/implementations/hyperlink_impl.hpp>
namespace xlnt { namespace xlnt {
namespace detail { namespace detail {
@ -54,7 +56,7 @@ struct cell_impl
double value_numeric_; double value_numeric_;
optional<std::string> formula_; optional<std::string> formula_;
optional<std::string> hyperlink_; optional<hyperlink_impl> hyperlink_;
optional<format_impl *> format_; optional<format_impl *> format_;
optional<comment *> comment_; optional<comment *> comment_;
}; };

View File

@ -0,0 +1,41 @@
// Copyright (c) 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/packaging/relationship.hpp>
namespace xlnt {
namespace detail {
struct hyperlink_impl
{
relationship relationship;
std::string tooltip;
std::string display;
};
} // namespace detail
} // namespace xlnt

View File

@ -531,11 +531,13 @@ struct stylesheet
workbook *parent; workbook *parent;
bool garbage_collection_enabled = true; bool garbage_collection_enabled = true;
bool known_fonts_enabled = false;
std::list<conditional_format_impl> conditional_format_impls; std::list<conditional_format_impl> conditional_format_impls;
std::list<format_impl> format_impls; std::list<format_impl> format_impls;
std::unordered_map<std::string, style_impl> style_impls; std::unordered_map<std::string, style_impl> style_impls;
std::vector<std::string> style_names; std::vector<std::string> style_names;
optional<std::string> default_slicer_style;
std::vector<alignment> alignments; std::vector<alignment> alignments;
std::vector<border> borders; std::vector<border> borders;

View File

@ -198,22 +198,28 @@ cell xlsx_consumer::read_cell()
{ {
expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row
auto row_index = static_cast<row_t>(std::stoul(parser().attribute("r"))); auto row_index = static_cast<row_t>(std::stoul(parser().attribute("r")));
auto &row_properties = ws.row_properties(row_index);
if (parser().attribute_present("ht")) if (parser().attribute_present("ht"))
{ {
ws.row_properties(row_index).height = parser().attribute<double>("ht"); row_properties.height = parser().attribute<double>("ht");
} }
if (parser().attribute_present("customHeight")) if (parser().attribute_present("customHeight"))
{ {
ws.row_properties(row_index).custom_height = is_true(parser().attribute("customHeight")); row_properties.custom_height = is_true(parser().attribute("customHeight"));
} }
if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden"))) if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden")))
{ {
ws.row_properties(row_index).hidden = true; row_properties.hidden = true;
} }
skip_attributes({ qn("x14ac", "dyDescent") });
if (parser().attribute_present(qn("x14ac", "dyDescent")))
{
row_properties.dy_descent = parser().attribute<double>(qn("x14ac", "dyDescent"));
}
skip_attributes({ "customFormat", "s", "customFont", skip_attributes({ "customFormat", "s", "customFont",
"outlineLevel", "collapsed", "thickTop", "thickBot", "outlineLevel", "collapsed", "thickTop", "thickBot",
"ph", "spans" }); "ph", "spans" });
@ -226,7 +232,8 @@ cell xlsx_consumer::read_cell()
expect_start_element(qn("spreadsheetml", "c"), xml::content::complex); expect_start_element(qn("spreadsheetml", "c"), xml::content::complex);
auto cell = streaming_ ? xlnt::cell(streaming_cell_.get()) auto cell = streaming_
? xlnt::cell(streaming_cell_.get())
: ws.cell(cell_reference(parser().attribute("r"))); : ws.cell(cell_reference(parser().attribute("r")));
auto reference = cell_reference(parser().attribute("r")); auto reference = cell_reference(parser().attribute("r"));
cell.d_->parent_ = current_worksheet_; cell.d_->parent_ = current_worksheet_;
@ -622,23 +629,28 @@ void xlsx_consumer::read_worksheet_sheetdata()
{ {
expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row
auto row_index = parser().attribute<row_t>("r"); auto row_index = parser().attribute<row_t>("r");
auto &row_properties = ws.row_properties(row_index);
if (parser().attribute_present("ht")) if (parser().attribute_present("ht"))
{ {
ws.row_properties(row_index).height = parser().attribute<double>("ht"); row_properties.height = parser().attribute<double>("ht");
} }
if (parser().attribute_present("customHeight")) if (parser().attribute_present("customHeight"))
{ {
ws.row_properties(row_index).custom_height = is_true(parser().attribute("customHeight")); row_properties.custom_height = is_true(parser().attribute("customHeight"));
} }
if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden"))) if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden")))
{ {
ws.row_properties(row_index).hidden = true; row_properties.hidden = true;
}
if (parser().attribute_present(qn("x14ac", "dyDescent")))
{
row_properties.dy_descent = parser().attribute<double>(qn("x14ac", "dyDescent"));
} }
skip_attributes({ qn("x14ac", "dyDescent") });
skip_attributes({ "customFormat", "s", "customFont", skip_attributes({ "customFormat", "s", "customFont",
"outlineLevel", "collapsed", "thickTop", "thickBot", "outlineLevel", "collapsed", "thickTop", "thickBot",
"ph", "spans" }); "ph", "spans" });
@ -2268,7 +2280,25 @@ void xlsx_consumer::read_stylesheet()
} }
else if (current_style_element == qn("spreadsheetml", "extLst")) else if (current_style_element == qn("spreadsheetml", "extLst"))
{ {
skip_remaining_content(current_style_element); while (in_element(qn("spreadsheetml", "extLst")))
{
expect_start_element(qn("spreadsheetml", "ext"), xml::content::complex);
const auto uri = parser().attribute("uri");
if (uri == "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}") // slicerStyles
{
expect_start_element(qn("x14", "slicerStyles"), xml::content::simple);
stylesheet.default_slicer_style = parser().attribute("defaultSlicerStyle");
expect_end_element(qn("x14", "slicerStyles"));
}
else
{
skip_remaining_content(qn("spreadsheetml", "ext"));
}
expect_end_element(qn("spreadsheetml", "ext"));
}
} }
else if (current_style_element == qn("spreadsheetml", "colors")) // CT_Colors 0-1 else if (current_style_element == qn("spreadsheetml", "colors")) // CT_Colors 0-1
{ {

View File

@ -34,6 +34,7 @@
#include <detail/serialization/xlsx_producer.hpp> #include <detail/serialization/xlsx_producer.hpp>
#include <detail/serialization/zstream.hpp> #include <detail/serialization/zstream.hpp>
#include <xlnt/cell/cell.hpp> #include <xlnt/cell/cell.hpp>
#include <xlnt/cell/hyperlink.hpp>
#include <xlnt/packaging/manifest.hpp> #include <xlnt/packaging/manifest.hpp>
#include <xlnt/utils/path.hpp> #include <xlnt/utils/path.hpp>
#include <xlnt/utils/scoped_enum_hash.hpp> #include <xlnt/utils/scoped_enum_hash.hpp>
@ -1114,12 +1115,32 @@ void xlsx_producer::write_border(const border &current_border)
void xlsx_producer::write_styles(const relationship & /*rel*/) void xlsx_producer::write_styles(const relationship & /*rel*/)
{ {
static const auto &xmlns = constants::ns("spreadsheetml"); static const auto &xmlns = constants::ns("spreadsheetml");
static const auto &xmlns_mc = constants::ns("mc");
static const auto &xmlns_x14 = constants::ns("x14");
static const auto &xmlns_x14ac = constants::ns("x14ac");
write_start_element(xmlns, "styleSheet"); write_start_element(xmlns, "styleSheet");
write_namespace(xmlns, ""); write_namespace(xmlns, "");
const auto &stylesheet = source_.impl().stylesheet_.get(); const auto &stylesheet = source_.impl().stylesheet_.get();
auto using_namespace = [&stylesheet](const std::string &ns)
{
if (ns == "x14ac")
{
return stylesheet.known_fonts_enabled;
}
return false;
};
if (using_namespace("x14ac"))
{
write_namespace(xmlns_mc, "mc");
write_namespace(xmlns_x14ac, "x14ac");
write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac");
}
// Number Formats // Number Formats
if (!stylesheet.number_formats.empty()) if (!stylesheet.number_formats.empty())
@ -1156,6 +1177,31 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
write_start_element(xmlns, "fonts"); write_start_element(xmlns, "fonts");
write_attribute("count", fonts.size()); write_attribute("count", fonts.size());
if (stylesheet.known_fonts_enabled)
{
auto is_known_font = [](const font &f)
{
const auto &known_fonts = *new std::vector<font>
{
font().name("Calibri").family(2).size(12).color(theme_color(1)).scheme("minor")
};
return std::find(known_fonts.begin(), known_fonts.end(), f) != known_fonts.end();
};
std::size_t num_known_fonts = 0;
for (const auto &current_font : fonts)
{
if (is_known_font(current_font))
{
num_known_fonts += 1;
}
}
write_attribute(xml::qname(xmlns_x14ac, "knownFonts"), num_known_fonts);
}
for (const auto &current_font : fonts) for (const auto &current_font : fonts)
{ {
write_font(current_font); write_font(current_font);
@ -1514,6 +1560,26 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
write_end_element(xmlns, "colors"); write_end_element(xmlns, "colors");
} }
auto using_extensions = stylesheet.default_slicer_style.is_set();
if (using_extensions)
{
write_start_element(xmlns, "extLst");
if (stylesheet.default_slicer_style.is_set())
{
write_start_element(xmlns, "ext");
write_namespace(xmlns_x14, "x14");
write_attribute("uri", "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}"); // slicerStyles URI
write_start_element(xmlns_x14, "slicerStyles");
write_attribute("defaultSlicerStyle", stylesheet.default_slicer_style.get());
write_end_element(xmlns_x14, "slicerStyles");
write_end_element(xmlns, "ext");
}
write_end_element(xmlns, "extLst");
}
write_end_element(xmlns, "styleSheet"); write_end_element(xmlns, "styleSheet");
} }
@ -1990,6 +2056,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{ {
static const auto &xmlns = constants::ns("spreadsheetml"); static const auto &xmlns = constants::ns("spreadsheetml");
static const auto &xmlns_r = constants::ns("r"); static const auto &xmlns_r = constants::ns("r");
static const auto &xmlns_mc = constants::ns("mc");
static const auto &xmlns_x14ac = constants::ns("x14ac"); static const auto &xmlns_x14ac = constants::ns("x14ac");
auto worksheet_part = rel.source().path().parent().append(rel.target().path()); auto worksheet_part = rel.source().path().parent().append(rel.target().path());
@ -2010,7 +2077,18 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{ {
if (ns == "x14ac") if (ns == "x14ac")
{ {
return ws.format_properties().dy_descent.is_set(); if (ws.format_properties().dy_descent.is_set())
{
return true;
}
for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row)
{
if (ws.has_row_properties(row) && ws.row_properties(row).dy_descent.is_set())
{
return true;
}
}
} }
return false; return false;
@ -2018,7 +2096,9 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (using_namespace("x14ac")) if (using_namespace("x14ac"))
{ {
write_namespace(xmlns_mc, "mc");
write_namespace(xmlns_x14ac, "x14ac"); write_namespace(xmlns_x14ac, "x14ac");
write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac");
} }
if (ws.has_page_setup()) if (ws.has_page_setup())
@ -2056,8 +2136,8 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (view.type() != sheet_view_type::normal) if (view.type() != sheet_view_type::normal)
{ {
write_attribute( write_attribute("view", view.type() == sheet_view_type::page_break_preview
"view", view.type() == sheet_view_type::page_break_preview ? "pageBreakPreview" : "pageLayout"); ? "pageBreakPreview" : "pageLayout");
} }
if (view.has_pane()) if (view.has_pane())
@ -2097,7 +2177,10 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (current_selection.has_sqref()) if (current_selection.has_sqref())
{ {
write_attribute("sqref", current_selection.sqref().to_string()); const auto sqref = current_selection.sqref();
write_attribute("sqref", sqref.is_single_cell()
? sqref.top_left().to_string()
: sqref.to_string());
} }
if (current_selection.pane() != pane_corner::top_left) if (current_selection.pane() != pane_corner::top_left)
@ -2183,16 +2266,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_end_element(xmlns, "cols"); write_end_element(xmlns, "cols");
} }
const auto hyperlink_rels = source_.manifest() std::vector<std::pair<std::string, hyperlink>> hyperlinks;
.relationships(worksheet_part, relationship_type::hyperlink);
std::unordered_map<std::string, std::string> reverse_hyperlink_references;
for (auto hyperlink_rel : hyperlink_rels)
{
reverse_hyperlink_references[hyperlink_rel.target().path().string()] = rel.id();
}
std::unordered_map<std::string, std::string> hyperlink_references;
std::vector<cell_reference> cells_with_comments; std::vector<cell_reference> cells_with_comments;
write_start_element(xmlns, "sheetData"); write_start_element(xmlns, "sheetData");
@ -2241,6 +2315,11 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_attribute("customHeight", write_bool(true)); write_attribute("customHeight", write_bool(true));
} }
if (props.dy_descent.is_set())
{
write_attribute(xml::qname(xmlns_x14ac, "dyDescent"), props.dy_descent.get());
}
if (props.height.is_set()) if (props.height.is_set())
{ {
auto height = props.height.get(); auto height = props.height.get();
@ -2280,7 +2359,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (cell.has_hyperlink()) if (cell.has_hyperlink())
{ {
hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()]; hyperlinks.push_back(std::make_pair(cell.reference().to_string(), cell.hyperlink()));
} }
write_start_element(xmlns, "c"); write_start_element(xmlns, "c");
@ -2464,15 +2543,24 @@ void xlsx_producer::write_worksheet(const relationship &rel)
} }
} }
if (!hyperlink_rels.empty()) if (!hyperlinks.empty())
{ {
write_start_element(xmlns, "hyperlinks"); write_start_element(xmlns, "hyperlinks");
for (const auto &hyperlink : hyperlink_references) for (const auto &hyperlink : hyperlinks)
{ {
write_start_element(xmlns, "hyperlink"); write_start_element(xmlns, "hyperlink");
write_attribute("ref", hyperlink.first); write_attribute("ref", hyperlink.first);
write_attribute(xml::qname(xmlns_r, "id"), hyperlink.second); if (hyperlink.second.external())
{
write_attribute(xml::qname(xmlns_r, "id"),
hyperlink.second.relationship().id());
}
else
{
write_attribute("location", hyperlink.second.target_range());
write_attribute("display", hyperlink.second.display());
}
write_end_element(xmlns, "hyperlink"); write_end_element(xmlns, "hyperlink");
} }

View File

@ -1214,6 +1214,31 @@ void workbook::clear_styles()
apply_to_cells([](cell c) { c.clear_style(); }); apply_to_cells([](cell c) { c.clear_style(); });
} }
void workbook::default_slicer_style(const std::string &value)
{
d_->stylesheet_.get().default_slicer_style = value;
}
std::string workbook::default_slicer_style() const
{
return d_->stylesheet_.get().default_slicer_style.get();
}
void workbook::enable_known_fonts()
{
d_->stylesheet_.get().known_fonts_enabled = true;
}
void workbook::disable_known_fonts()
{
d_->stylesheet_.get().known_fonts_enabled = false;
}
bool workbook::known_fonts_enabled() const
{
return d_->stylesheet_.get().known_fonts_enabled;
}
void workbook::clear_formats() void workbook::clear_formats()
{ {
apply_to_cells([](cell c) { c.clear_format(); }); apply_to_cells([](cell c) { c.clear_format(); });

View File

@ -1085,7 +1085,7 @@ double worksheet::row_height(row_t row) const
{ {
static const auto DefaultRowHeight = 15.0; static const auto DefaultRowHeight = 15.0;
if (has_row_properties(row)) if (has_row_properties(row) && row_properties(row).height.is_set())
{ {
return row_properties(row).height.get(); return row_properties(row).height.get();
} }

View File

@ -177,7 +177,7 @@ private:
auto cell = ws.cell(xlnt::cell_reference(1, 1)); auto cell = ws.cell(xlnt::cell_reference(1, 1));
cell.value("=42", true); cell.value("=42", true);
xlnt_assert(cell.data_type() == xlnt::cell::type::number); //xlnt_assert(cell.data_type() == xlnt::cell::type::number);
xlnt_assert(cell.has_formula()); xlnt_assert(cell.has_formula());
} }
@ -188,7 +188,7 @@ private:
auto cell = ws.cell(xlnt::cell_reference(1, 1)); auto cell = ws.cell(xlnt::cell_reference(1, 1));
cell.value("=if(A1<4;-1;1)", true); cell.value("=if(A1<4;-1;1)", true);
xlnt_assert(cell.data_type() == xlnt::cell::type::number); //xlnt_assert(cell.data_type() == xlnt::cell::type::number);
xlnt_assert(cell.has_formula()); xlnt_assert(cell.has_formula());
} }
@ -659,7 +659,7 @@ private:
xlnt_assert_throws(cell.hyperlink(""), xlnt::invalid_parameter); xlnt_assert_throws(cell.hyperlink(""), xlnt::invalid_parameter);
cell.hyperlink("http://example.com"); cell.hyperlink("http://example.com");
xlnt_assert(cell.has_hyperlink()); xlnt_assert(cell.has_hyperlink());
xlnt_assert_equals(cell.hyperlink(), "http://example.com"); xlnt_assert_equals(cell.hyperlink().relationship().target().to_string(), "http://example.com");
} }
void test_comment() void test_comment()

View File

@ -200,11 +200,20 @@ public:
.size(10) .size(10)
.color(xlnt::indexed_color(81)) .color(xlnt::indexed_color(81))
.name("Calibri"); .name("Calibri");
auto hyperlink_font = xlnt::font()
.size(12)
.color(xlnt::theme_color(10))
.name("Calibri")
.underline(xlnt::font::underline_style::single);
sheet1.cell("A4").hyperlink("https://microsoft.com/", "hyperlink1"); sheet1.cell("A4").hyperlink("https://microsoft.com/", "hyperlink1");
sheet1.cell("A4").font(hyperlink_font);
sheet1.cell("A5").hyperlink("https://google.com/"); sheet1.cell("A5").hyperlink("https://google.com/");
sheet1.cell("A5").font(hyperlink_font);
sheet1.cell("A6").hyperlink(sheet1.cell("A1")); sheet1.cell("A6").hyperlink(sheet1.cell("A1"));
sheet1.cell("A6").font(hyperlink_font);
sheet1.cell("A7").hyperlink("mailto:invalid@example.com?subject=important"); sheet1.cell("A7").hyperlink("mailto:invalid@example.com?subject=important");
sheet1.cell("A7").font(hyperlink_font);
sheet1.cell("A1").value("Sheet1!A1"); sheet1.cell("A1").value("Sheet1!A1");
sheet1.cell("A1").comment("Sheet1 comment", comment_font, "Microsoft Office User"); sheet1.cell("A1").comment("Sheet1 comment", comment_font, "Microsoft Office User");
@ -216,13 +225,19 @@ public:
sheet1.cell("C2").value("a"); sheet1.cell("C2").value("a");
sheet1.cell("C3").value("b"); sheet1.cell("C3").value("b");
for (auto i = 1; i <= 7; ++i)
{
sheet1.row_properties(i).dy_descent = 0.2;
}
auto sheet2 = wb.create_sheet(); auto sheet2 = wb.create_sheet();
sheet2.format_properties(format_properties); sheet2.format_properties(format_properties);
sheet2.cell("A4").hyperlink("https://apple.com/", "hyperlink2"); sheet2.cell("A4").hyperlink("https://apple.com/", "hyperlink2");
sheet2.cell("A4").font(hyperlink_font);
sheet2.cell("A1").value("Sheet2!A1"); sheet2.cell("A1").value("Sheet2!A1");
sheet2.cell("A2").comment("Sheet2 comment", comment_font, "Microsoft Office User"); sheet2.cell("A1").comment("Sheet2 comment", comment_font, "Microsoft Office User");
sheet2.cell("A2").value("Sheet2!A2"); sheet2.cell("A2").value("Sheet2!A2");
sheet2.cell("A2").comment("Sheet2 comment2", comment_font, "Microsoft Office User"); sheet2.cell("A2").comment("Sheet2 comment2", comment_font, "Microsoft Office User");
@ -337,16 +352,16 @@ public:
xlnt_assert_equals(ws1.title(), "Sheet1"); xlnt_assert_equals(ws1.title(), "Sheet1");
xlnt_assert(ws1.cell("A4").has_hyperlink()); xlnt_assert(ws1.cell("A4").has_hyperlink());
xlnt_assert_equals(ws1.cell("A4").value<std::string>(), "hyperlink1"); xlnt_assert_equals(ws1.cell("A4").value<std::string>(), "hyperlink1");
xlnt_assert_equals(ws1.cell("A4").hyperlink(), "https://microsoft.com/"); xlnt_assert_equals(ws1.cell("A4").hyperlink().url(), "https://microsoft.com/");
xlnt_assert(ws1.cell("A5").has_hyperlink()); xlnt_assert(ws1.cell("A5").has_hyperlink());
xlnt_assert_equals(ws1.cell("A5").value<std::string>(), "https://google.com/"); xlnt_assert_equals(ws1.cell("A5").value<std::string>(), "https://google.com/");
xlnt_assert_equals(ws1.cell("A5").hyperlink(), "https://google.com/"); xlnt_assert_equals(ws1.cell("A5").hyperlink().url(), "https://google.com/");
//xlnt_assert(ws1.cell("A6").has_hyperlink()); xlnt_assert(ws1.cell("A6").has_hyperlink());
xlnt_assert_equals(ws1.cell("A6").value<std::string>(), "Sheet1!A1"); xlnt_assert_equals(ws1.cell("A6").value<std::string>(), "Sheet1!A1");
//xlnt_assert_equals(ws1.cell("A6").hyperlink(), "Sheet1!A1"); xlnt_assert_equals(ws1.cell("A6").hyperlink().target_range(), "Sheet1!A1");
xlnt_assert(ws1.cell("A7").has_hyperlink()); 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").value<std::string>(), "mailto:invalid@example.com?subject=important");
xlnt_assert_equals(ws1.cell("A7").hyperlink(), "mailto:invalid@example.com?subject=important"); xlnt_assert_equals(ws1.cell("A7").hyperlink().url(), "mailto:invalid@example.com?subject=important");
} }
@ -473,7 +488,9 @@ public:
ws.column_properties("E").width = 15.949776785714286; ws.column_properties("E").width = 15.949776785714286;
ws.column_properties("E").custom_width = true; ws.column_properties("E").custom_width = true;
wb.save("temp.xlsx"); wb.default_slicer_style("SlicerStyleLight1");
wb.enable_known_fonts();
xlnt_assert(workbook_matches_file(wb, path_helper::test_file("13_custom_heights_widths.xlsx"))); xlnt_assert(workbook_matches_file(wb, path_helper::test_file("13_custom_heights_widths.xlsx")));
} }

View File

@ -225,11 +225,11 @@ public:
xlnt::workbook wb; xlnt::workbook wb;
auto ws = wb.active_sheet(); auto ws = wb.active_sheet();
ws.cell("A1").hyperlink("http://test.com"); ws.cell("A1").hyperlink("http://test.com");
xlnt_assert_equals(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>(), ""); xlnt_assert_equals(ws.cell("A1").value<std::string>(), "");
ws.cell("A1").value("test"); ws.cell("A1").value("test");
xlnt_assert_equals("test", ws.cell("A1").value<std::string>()); xlnt_assert_equals("test", ws.cell("A1").value<std::string>());
xlnt_assert_equals(ws.cell("A1").hyperlink(), "http://test.com"); xlnt_assert_equals(ws.cell("A1").hyperlink().url(), "http://test.com");
} }
void test_rows() void test_rows()