Skip to content

Commit

Permalink
Merge pull request #1 from musshorn/issue_685
Browse files Browse the repository at this point in the history
Fix XLNT issue tfussell#685
  • Loading branch information
Laurențiu Leahu-Vlăducu authored May 22, 2024
2 parents 297b331 + 6dda4a2 commit 418857a
Show file tree
Hide file tree
Showing 13 changed files with 536 additions and 19 deletions.
33 changes: 33 additions & 0 deletions include/xlnt/workbook/workbook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct stylesheet;
struct workbook_impl;
class xlsx_consumer;
class xlsx_producer;
struct defined_name;

} // namespace detail

Expand Down Expand Up @@ -751,6 +752,38 @@ class XLNT_API workbook
/// </summary>
bool known_fonts_enabled() const;

// Defined Names

/// <summary>
/// Add a defined name to a sheet
/// </summary
void add_defined_name(detail::defined_name name);

/// <summary>
/// Returns a copy of workbook defined names
/// </summary>
std::vector<detail::defined_name> get_defined_names() const;

/// <summary>
/// Returns a reference to a single defined name
/// </summary>
detail::defined_name &get_defined_name(const std::size_t index);

/// <summary>
/// Returns a reference to a single defined name. Finds the first name that matches, there may be multiple with the same name
/// </summary>
detail::defined_name &get_defined_name(const std::string &name);

/// <summary>
/// Removes the defined name at the given index
/// </summary>
void remove_defined_name(const std::size_t index);

/// <summary>
/// Removes the defined name that first matches the passed name.
/// </summary>
void remove_defined_name(const std::string &name);

// Manifest

/// <summary>
Expand Down
34 changes: 33 additions & 1 deletion include/xlnt/worksheet/worksheet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ namespace detail {

class xlsx_consumer;
class xlsx_producer;

struct defined_name;
struct worksheet_impl;

} // namespace detail
Expand Down Expand Up @@ -729,6 +729,38 @@ class XLNT_API worksheet
/// </summary>
cell_reference active_cell() const;

// Defined Names

/// <summary>
/// Add a defined name to a sheet
/// </summary
void add_defined_name(detail::defined_name name);

/// <summary>
/// Returns a copy of workbook defined names
/// </summary>
std::vector<detail::defined_name> get_defined_names() const;

/// <summary>
/// Returns a reference to a single defined name
/// </summary>
detail::defined_name &get_defined_name(const std::size_t index);

/// <summary>
/// Returns a reference to a single defined name. Finds the first name that matches, there may be multiple with the same name
/// </summary>
detail::defined_name &get_defined_name(const std::string &name);

/// <summary>
/// Removes the defined name at the given index
/// </summary>
void remove_defined_name(const std::size_t index);

/// <summary>
/// Removes the defined name that first matches the passed name.
/// </summary>
void remove_defined_name(const std::string &name);

// page breaks

/// <summary>
Expand Down
13 changes: 12 additions & 1 deletion source/detail/implementations/workbook_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <detail/implementations/stylesheet.hpp>
#include <detail/implementations/worksheet_impl.hpp>
#include <detail/serialization/defined_name.hpp>
#include <xlnt/packaging/ext_list.hpp>
#include <xlnt/packaging/manifest.hpp>
#include <xlnt/utils/datetime.hpp>
Expand Down Expand Up @@ -88,11 +89,20 @@ struct workbook_impl
extended_properties_ = other.extended_properties_;
custom_properties_ = other.custom_properties_;

defined_names_ = other.defined_names_;

return *this;
}

bool operator==(const workbook_impl &other)
bool operator==(const workbook_impl &other) const
{
if (defined_names_.size() != other.defined_names_.size()) return false;

for (std::size_t i = 0; i < defined_names_.size(); i++)
{
if (defined_names_[i] != other.defined_names_[i]) return false;
}

return active_sheet_index_ == other.active_sheet_index_
&& worksheets_ == other.worksheets_
&& shared_strings_ids_ == other.shared_strings_ids_
Expand Down Expand Up @@ -168,6 +178,7 @@ struct workbook_impl
optional<std::string> abs_path_;
optional<std::size_t> arch_id_flags_;
optional<ext_list> extensions_;
std::vector<defined_name> defined_names_;
};

} // namespace detail
Expand Down
15 changes: 14 additions & 1 deletion source/detail/implementations/worksheet_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <xlnt/worksheet/print_options.hpp>
#include <xlnt/worksheet/sheet_pr.hpp>
#include <detail/implementations/cell_impl.hpp>
#include <detail/serialization/defined_name.hpp>

namespace xlnt {

Expand Down Expand Up @@ -88,6 +89,8 @@ struct worksheet_impl
extension_list_ = other.extension_list_;
sheet_properties_ = other.sheet_properties_;
print_options_ = other.print_options_;
defined_names_ = other.defined_names_;


for (auto &cell : cell_map_)
{
Expand All @@ -97,8 +100,15 @@ struct worksheet_impl

workbook *parent_;

bool operator==(const worksheet_impl& rhs) const
bool operator==(const worksheet_impl &rhs) const
{
if (defined_names_.size() != rhs.defined_names_.size()) return false;

for (std::size_t i = 0; i < defined_names_.size(); i++)
{
if (defined_names_[i] != rhs.defined_names_[i]) return false;
}

return id_ == rhs.id_
&& title_ == rhs.title_
&& format_properties_ == rhs.format_properties_
Expand All @@ -122,6 +132,7 @@ struct worksheet_impl
&& print_options_ == rhs.print_options_
&& sheet_properties_ == rhs.sheet_properties_
&& extension_list_ == rhs.extension_list_;

}

std::size_t id_;
Expand Down Expand Up @@ -160,6 +171,8 @@ struct worksheet_impl

std::string drawing_rel_id_;
optional<drawing::spreadsheet_drawing> drawing_;

std::vector<defined_name> defined_names_;
};

} // namespace detail
Expand Down
61 changes: 57 additions & 4 deletions source/detail/serialization/defined_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,63 @@ namespace detail {

struct defined_name
{
std::string name;
std::size_t sheet_id;
bool hidden;
std::string value;

defined_name &operator=(const defined_name &other)
{
name = other.name;
comment = other.comment;
custom_menu = other.custom_menu;
description = other.description;
help = other.help;

status_bar = other.status_bar;
sheet_id = other.sheet_id;
hidden = other.hidden;
function = other.function;
function_group_id = other.function_group_id;

shortcut_key = other.shortcut_key;
value = other.value;

return *this;
}

bool operator==(const defined_name &other) const
{
return name == other.name
&& comment == other.comment
&& custom_menu == other.custom_menu
&& description == other.description
&& help == other.help
&& status_bar == other.status_bar
&& sheet_id == other.sheet_id
&& hidden == other.hidden
&& function == other.function
&& function_group_id == other.function_group_id
&& shortcut_key == other.shortcut_key
&& value == other.value;
}

bool operator!=(const defined_name &other) const
{
return !(*this == other);
}

// Implements (most of) CT_RevisionDefinedName, there's several "old" members in the spec but they're also ignored in other librarie
std::string name; // A string representing the name for this defined name.
optional<std::string> comment; // A string representing a comment about the defined name.
optional<std::string> custom_menu; // A string representing the new custom menu text
optional<std::string> description; // A string representing the new description text for the defined name.
optional<std::string> help; // A string representing the new help topic text.
optional<std::string> status_bar; // A string representing the new status bar text.
optional<std::size_t> sheet_id; // An integer representing the id of the sheet to which this defined name belongs. This shall be used local defined names only. 0 indexed indexed.
optional<bool> hidden; // A Boolean value indicating whether the named range is now hidden.
optional<bool> function; // A Boolean value indicating that the defined name refers to a function. True if the defined name is a function, false otherwise.
optional<std::size_t> function_group_id;// Represents the new function group id.
optional<std::string> shortcut_key; // Represents the new keyboard shortcut. This is unsigned byte in the spec, but openpyxl uses string so let's try that
std::string value; // The actual value of the name, ie "='Sheet1'!A1"


};

} // namespace detail
Expand Down
81 changes: 75 additions & 6 deletions source/detail/serialization/xlsx_consumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet
skip_attributes({qn("mc", "Ignorable")});

read_defined_names(ws, defined_names_);
read_defined_names(ws, ws.d_->defined_names_);

while (in_element(qn("spreadsheetml", "worksheet")))
{
Expand Down Expand Up @@ -2082,16 +2082,61 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_

defined_name name;
name.name = parser().attribute("name");
name.sheet_id = parser().attribute<std::size_t>("localSheetId");
name.hidden = false;

if (parser().attribute_present("comment"))
{
name.comment = parser().attribute<std::string>("comment");
}

if (parser().attribute_present("customMenu"))
{
name.custom_menu = parser().attribute<std::string>("customMenu");
}

if (parser().attribute_present("description"))
{
name.description = parser().attribute<std::string>("description");
}

if (parser().attribute_present("help"))
{
name.help = parser().attribute<std::string>("help");
}

if (parser().attribute_present("statusBar"))
{
name.status_bar = parser().attribute<std::string>("statusBar");
}

if (parser().attribute_present("localSheetId"))
{
name.sheet_id = parser().attribute<std::size_t>("localSheetId");
}

if (parser().attribute_present("hidden"))
{
name.hidden = is_true(parser().attribute("hidden"));
}

if (parser().attribute_present("function"))
{
name.function = is_true(parser().attribute("function"));
}

if (parser().attribute_present("functionGroupId"))
{
name.function_group_id = parser().attribute<std::size_t>("functionGroupId");
}

if (parser().attribute_present("shortcutKey"))
{
name.shortcut_key = parser().attribute<std::string>("shortcutKey");
}

parser().attribute_map(); // skip remaining attributes
name.value = read_text();
defined_names_.push_back(name);
target_.d_->defined_names_.push_back(name);

expect_end_element(qn("spreadsheetml", "definedName"));
}
}
Expand Down Expand Up @@ -2195,6 +2240,7 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
}
}

std::vector<defined_name> workbook_names;
for (auto worksheet_rel : manifest().relationships(workbook_path, relationship_type::worksheet))
{
auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
Expand All @@ -2203,7 +2249,7 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
return p.second == worksheet_rel.id();
})->first;

auto id = sheet_title_id_map_[title];
auto id = sheet_title_id_map_[title]; // 1-indexed
auto index = sheet_title_index_map_[title];

auto insertion_iter = target_.d_->worksheets_.begin();
Expand All @@ -2215,11 +2261,34 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_

current_worksheet_ = &*target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title);

// If there's any defined names that are worksheet specific, move them here.
for (std::size_t i = 0; i < target_.d_->defined_names_.size(); i++)
{
const auto &name = target_.d_->defined_names_[i];
if (name.sheet_id.is_set())
{
const auto target_id = name.sheet_id.get();
if (target_id == index)
{
// It's a match, remove it from the workbook and add it to the sheet
current_worksheet_->defined_names_.push_back(name);
}
}
else
{
// Name is global and belongs to the workbook, if i'ts not already added
if (std::find(workbook_names.begin(), workbook_names.end(), name) == workbook_names.end())
workbook_names.push_back(name);
}
}

if (!streaming_)
{
read_part({workbook_rel, worksheet_rel});
}
}
// Update the workbook with the new defined names
target_.d_->defined_names_ = workbook_names;
}

// Write Workbook Relationship Target Parts
Expand Down
Loading

0 comments on commit 418857a

Please sign in to comment.