re-write workbook::reorder_relationships

Issue #279
This commit is contained in:
Crzyrndm 2018-07-10 17:29:26 +12:00
parent c0a90ccb7f
commit 322490b397

View File

@ -28,16 +28,6 @@
#include <functional> #include <functional>
#include <set> #include <set>
#include <detail/constants.hpp>
#include <detail/default_case.hpp>
#include <detail/implementations/cell_impl.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/implementations/worksheet_impl.hpp>
#include <detail/serialization/excel_thumbnail.hpp>
#include <detail/serialization/vector_streambuf.hpp>
#include <detail/serialization/open_stream.hpp>
#include <detail/serialization/xlsx_consumer.hpp>
#include <detail/serialization/xlsx_producer.hpp>
#include <xlnt/cell/cell.hpp> #include <xlnt/cell/cell.hpp>
#include <xlnt/packaging/manifest.hpp> #include <xlnt/packaging/manifest.hpp>
#include <xlnt/packaging/relationship.hpp> #include <xlnt/packaging/relationship.hpp>
@ -61,12 +51,22 @@
#include <xlnt/worksheet/header_footer.hpp> #include <xlnt/worksheet/header_footer.hpp>
#include <xlnt/worksheet/range.hpp> #include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/worksheet.hpp> #include <xlnt/worksheet/worksheet.hpp>
#include <detail/constants.hpp>
#include <detail/default_case.hpp>
#include <detail/implementations/cell_impl.hpp>
#include <detail/implementations/workbook_impl.hpp>
#include <detail/implementations/worksheet_impl.hpp>
#include <detail/serialization/excel_thumbnail.hpp>
#include <detail/serialization/open_stream.hpp>
#include <detail/serialization/vector_streambuf.hpp>
#include <detail/serialization/xlsx_consumer.hpp>
#include <detail/serialization/xlsx_producer.hpp>
namespace { namespace {
using xlnt::detail::open_stream; using xlnt::detail::open_stream;
template<typename T> template <typename T>
std::vector<T> keys(const std::vector<std::pair<T, xlnt::variant>> &container) std::vector<T> keys(const std::vector<std::pair<T, xlnt::variant>> &container)
{ {
auto result = std::vector<T>(); auto result = std::vector<T>();
@ -80,7 +80,7 @@ std::vector<T> keys(const std::vector<std::pair<T, xlnt::variant>> &container)
return result; return result;
} }
template<typename T> template <typename T>
bool contains(const std::vector<std::pair<T, xlnt::variant>> &container, const T key) bool contains(const std::vector<std::pair<T, xlnt::variant>> &container, const T key)
{ {
for (const auto &iter : container) for (const auto &iter : container)
@ -451,29 +451,29 @@ workbook workbook::empty()
stylesheet.parent = &wb; stylesheet.parent = &wb;
auto default_border = border() auto default_border = border()
.side(border_side::bottom, border::border_property()) .side(border_side::bottom, border::border_property())
.side(border_side::top, border::border_property()) .side(border_side::top, border::border_property())
.side(border_side::start, border::border_property()) .side(border_side::start, border::border_property())
.side(border_side::end, border::border_property()) .side(border_side::end, border::border_property())
.side(border_side::diagonal, border::border_property()); .side(border_side::diagonal, border::border_property());
wb.d_->stylesheet_.get().borders.push_back(default_border); wb.d_->stylesheet_.get().borders.push_back(default_border);
auto default_fill = fill(pattern_fill() auto default_fill = fill(pattern_fill()
.type(pattern_fill_type::none)); .type(pattern_fill_type::none));
stylesheet.fills.push_back(default_fill); stylesheet.fills.push_back(default_fill);
auto gray125_fill = pattern_fill() auto gray125_fill = pattern_fill()
.type(pattern_fill_type::gray125); .type(pattern_fill_type::gray125);
stylesheet.fills.push_back(gray125_fill); stylesheet.fills.push_back(gray125_fill);
auto default_font = font() auto default_font = font()
.name("Calibri") .name("Calibri")
.size(12) .size(12)
.scheme("minor") .scheme("minor")
.family(2) .family(2)
.color(theme_color(1)); .color(theme_color(1));
stylesheet.fonts.push_back(default_font); stylesheet.fonts.push_back(default_font);
wb.create_builtin_style(0) wb.create_builtin_style(0)
.border(default_border) .border(default_border)
.fill(default_fill) .fill(default_fill)
.font(default_font) .font(default_font)
@ -550,7 +550,7 @@ void workbook::register_package_part(relationship_type type)
void workbook::register_workbook_part(relationship_type type) void workbook::register_workbook_part(relationship_type type)
{ {
auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document); auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
auto wb_path = manifest().canonicalize({ wb_rel }); auto wb_path = manifest().canonicalize({wb_rel});
if (!manifest().has_relationship(wb_path, type)) if (!manifest().has_relationship(wb_path, type))
{ {
@ -1025,7 +1025,8 @@ void workbook::remove_sheet(worksheet ws)
for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_) for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_)
{ {
title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0 title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0
? rel_id_map[title_rel_id_pair.second] : title_rel_id_pair.second; ? rel_id_map[title_rel_id_pair.second]
: title_rel_id_pair.second;
} }
update_sheet_properties(); update_sheet_properties();
@ -1390,7 +1391,7 @@ style workbook::create_style(const std::string &name)
style workbook::create_builtin_style(const std::size_t builtin_id) style workbook::create_builtin_style(const std::size_t builtin_id)
{ {
return d_->stylesheet_.get().create_builtin_style(builtin_id); return d_->stylesheet_.get().create_builtin_style(builtin_id);
} }
style workbook::style(const std::string &name) style workbook::style(const std::string &name)
@ -1563,69 +1564,69 @@ void workbook::update_sheet_properties()
} }
} }
void workbook::reorder_relationships() namespace {
// true if a sheet index is != worksheet relationship index
bool needs_reorder(const std::unordered_map<std::string, std::string> &title_to_rels,
const std::vector<std::string> &titles,
std::vector<std::string> &relation_ids)
{ {
const auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document); bool all_match = true;
const auto wb_path = wb_rel.target().path(); for (std::size_t title_index = 0; title_index < titles.size(); ++title_index)
const auto relationships = manifest().relationships(wb_path);
std::unordered_map<std::string, relationship> rel_map;
const auto titles = sheet_titles();
const auto title_rel_id_map = d_->sheet_title_rel_id_map_;
bool needs_reorder = false;
for (const auto &rel : relationships)
{ {
rel_map[rel.id()] = rel; const auto &rel = title_to_rels.at(titles[title_index]);
relation_ids.push_back(rel);
if (rel.type() == relationship_type::worksheet) const auto expected_rel_id = "rId" + std::to_string(title_index + 1);
if (rel != expected_rel_id)
{ {
for (auto title_index = std::size_t(0); title_index < titles.size(); ++title_index) all_match = false;
{
const auto title = titles[title_index];
if (title_rel_id_map.at(title) == rel.id())
{
const auto expected_rel_id = "rId" + std::to_string(title_index + 1);
if (expected_rel_id != rel.id())
{
needs_reorder = true;
}
break;
}
}
} }
} }
return !all_match; // if all are as expected, reorder not required
};
} // namespace
if (!needs_reorder) void workbook::reorder_relationships()
{
const auto titles = sheet_titles();
// the relation ID corresponding to the title at the same index is copied into here
std::vector<std::string> worksheet_rel_ids;
worksheet_rel_ids.reserve(titles.size());
if (!needs_reorder(d_->sheet_title_rel_id_map_, titles, worksheet_rel_ids))
{ {
return; return;
} }
// copy of existing relations
for (const auto &rel : relationships) const auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
const auto wb_path = wb_rel.target().path();
auto rel_copy = manifest().relationships(wb_path);
// clear existing relations
for (const auto &rel : rel_copy)
{ {
manifest().unregister_relationship(uri(wb_path.string()), rel.id()); manifest().unregister_relationship(uri(wb_path.string()), rel.id());
} }
// create new relations
for (auto index = std::size_t(0); index < rel_map.size(); ++index) std::size_t index = 0;
auto new_id = [&index]() { return "rId" + std::to_string(++index); }; // ids start from 1
// worksheets first
while (index < worksheet_rel_ids.size())
{ {
auto rel_id = "rId" + std::to_string(index + 1); auto rel_it = std::find_if(rel_copy.begin(), rel_copy.end(),
auto old_rel_id = std::string(); [&](const relationship &rel) { return rel.id() == worksheet_rel_ids[index]; });
if (index < titles.size()) std::string rel_id = new_id();
d_->sheet_title_rel_id_map_.at(titles[index - 1]) = rel_id; // update title -> relation mapping
manifest().register_relationship(relationship(rel_id, rel_it->type(),
rel_it->source(), rel_it->target(), rel_it->target_mode()));
}
// then all the other relations in the same order they started (just new indices)
for (const auto& old_rel : rel_copy)
{
if (old_rel.type() == relationship_type::worksheet)
{ {
auto title = titles[index]; continue;
old_rel_id = title_rel_id_map.at(title);
d_->sheet_title_rel_id_map_[title] = rel_id;
} else {
old_rel_id = "rId" + std::to_string(index - titles.size() + 2);
} }
manifest().register_relationship(relationship(new_id(), old_rel.type(),
auto old_rel = rel_map[old_rel_id]; old_rel.source(), old_rel.target(), old_rel.target_mode()));
auto new_rel = relationship(rel_id, old_rel.type(),
old_rel.source(), old_rel.target(), old_rel.target_mode());
manifest().register_relationship(new_rel);
} }
} }