finish implementing comment positioning, closes #68

This commit is contained in:
Thomas Fussell 2016-11-12 17:38:29 -05:00
parent b06f3901f6
commit cee933a1b8
8 changed files with 230 additions and 99 deletions

View File

@ -446,7 +446,12 @@ public:
class comment comment();
/// <summary>
/// Apply the comment provided as the ony argument to the cell.
/// Create a new comment with the given text and optional author and apply it to the cell.
/// </summary>
void comment(const std::string &text, const std::string &author = "Microsoft Office User");
/// <summary>
/// Apply the comment provided as the only argument to the cell.
/// </summary>
void comment(const class comment &new_comment);

View File

@ -65,6 +65,24 @@ public:
/// </summary>
std::string author() const;
void hide();
void show();
bool visible() const;
void position(int left, int top);
int left() const;
int top() const;
void size(int width, int height);
int width() const;
int height() const;
/// <summary>
/// Return true if both comments are equivalent.
/// </summary>
@ -73,6 +91,15 @@ public:
private:
formatted_text text_;
std::string author_;
bool visible_ = false;
std::string fill_;
int left_ = 0;
int top_ = 0;
int width_ = 0;
int height_ = 0;
};
} // namespace xlnt

View File

@ -1020,9 +1020,41 @@ class comment cell::comment()
return d_->comment_.get();
}
void cell::comment(const std::string &text, const std::string &author)
{
comment(xlnt::comment(text, author));
}
void cell::comment(const class comment &new_comment)
{
d_->comment_.set(new_comment);
// offset comment 5 pixels down and 5 pixels right of the top right corner of the cell
auto cell_position = get_anchor();
// todo: make this cell_position.first += get_width() instead
if (get_worksheet().has_column_properties(get_column()))
{
cell_position.first += get_worksheet().get_column_properties(get_column()).width;
}
else
{
static const auto DefaultColumnWidth = 51.85L;
auto points_to_pixels = [](long double value, long double dpi)
{
return static_cast<int>(std::ceil(value * dpi / 72));
};
cell_position.first += points_to_pixels(DefaultColumnWidth, 96.0L);
}
cell_position.first += 5;
cell_position.second += 5;
d_->comment_.get().position(cell_position.first, cell_position.second);
d_->comment_.get().size(200, 100);
get_worksheet().register_comments_in_manifest();
}

View File

@ -57,6 +57,53 @@ std::string comment::author() const
return author_;
}
void comment::hide()
{
visible_ = false;
}
void comment::show()
{
visible_ = true;
}
void comment::position(int left, int top)
{
left_ = left;
top_ = top;
}
void comment::size(int width, int height)
{
width_ = width;
height_ = height;
}
bool comment::visible() const
{
return visible_;
}
int comment::left() const
{
return left_;
}
int comment::top() const
{
return top_;
}
int comment::width() const
{
return width_;
}
int comment::height() const
{
return height_;
}
XLNT_API bool operator==(const comment &left, const comment &right)
{
return left.text_ == right.text_ && left.author_ == right.author_;

View File

@ -2513,118 +2513,138 @@ void xlsx_producer::write_comments(const relationship &/*rel*/, worksheet ws,
void xlsx_producer::write_vml_drawings(const relationship &rel, worksheet ws,
const std::vector<cell_reference> &cells)
{
static const auto &xmlns_mv = std::string("http://macVmlSchemaUri");
static const auto &xmlns_o = std::string("urn:schemas-microsoft-com:office:office");
static const auto &xmlns_mv = std::string("http://macVmlSchemaUri");
static const auto &xmlns_o = std::string("urn:schemas-microsoft-com:office:office");
static const auto &xmlns_v = std::string("urn:schemas-microsoft-com:vml");
static const auto &xmlns_x = std::string("urn:schemas-microsoft-com:office:excel");
static const auto &xmlns_x = std::string("urn:schemas-microsoft-com:office:excel");
serializer().start_element("xml");
serializer().namespace_decl(xmlns_v, "v");
serializer().namespace_decl(xmlns_o, "o");
serializer().namespace_decl(xmlns_x, "x");
serializer().namespace_decl(xmlns_mv, "mv");
serializer().start_element("xml");
serializer().namespace_decl(xmlns_v, "v");
serializer().namespace_decl(xmlns_o, "o");
serializer().namespace_decl(xmlns_x, "x");
serializer().namespace_decl(xmlns_mv, "mv");
serializer().start_element(xmlns_o, "shapelayout");
serializer().attribute(xml::qname(xmlns_v, "ext"), "edit");
serializer().start_element(xmlns_o, "idmap");
serializer().attribute(xml::qname(xmlns_v, "ext"), "edit");
auto filename = rel.get_target().get_path().split_extension().first;
auto index_pos = filename.size() - 1;
while (filename[index_pos] >= '0' && filename[index_pos] <= '9')
{
index_pos--;
}
auto file_index = std::stoull(filename.substr(index_pos + 1));
serializer().attribute("data", file_index);
serializer().end_element(xmlns_o, "idmap");
serializer().end_element(xmlns_o, "shapelayout");
serializer().start_element(xmlns_o, "shapelayout");
serializer().attribute(xml::qname(xmlns_v, "ext"), "edit");
serializer().start_element(xmlns_o, "idmap");
serializer().attribute(xml::qname(xmlns_v, "ext"), "edit");
serializer().start_element(xmlns_v, "shapetype");
serializer().attribute("id", "_x0000_t202");
serializer().attribute("coordsize", "21600,21600");
serializer().attribute(xml::qname(xmlns_o, "spt"), "202");
serializer().attribute("path", "m0,0l0,21600,21600,21600,21600,0xe");
serializer().start_element(xmlns_v, "stroke");
serializer().attribute("joinstyle", "miter");
serializer().end_element(xmlns_v, "stroke");
serializer().start_element(xmlns_v, "path");
serializer().attribute("gradientshapeok", "t");
serializer().attribute(xml::qname(xmlns_o, "connecttype"), "rect");
serializer().end_element(xmlns_v, "path");
serializer().end_element(xmlns_v, "shapetype");
auto filename = rel.get_target().get_path().split_extension().first;
auto index_pos = filename.size() - 1;
std::size_t comment_index = 0;
while (filename[index_pos] >= '0' && filename[index_pos] <= '9')
{
index_pos--;
}
for (const auto &cell_ref : cells)
{
auto file_index = std::stoull(filename.substr(index_pos + 1));
serializer().attribute("data", file_index);
serializer().end_element(xmlns_o, "idmap");
serializer().end_element(xmlns_o, "shapelayout");
serializer().start_element(xmlns_v, "shapetype");
serializer().attribute("id", "_x0000_t202");
serializer().attribute("coordsize", "21600,21600");
serializer().attribute(xml::qname(xmlns_o, "spt"), "202");
serializer().attribute("path", "m0,0l0,21600,21600,21600,21600,0xe");
serializer().start_element(xmlns_v, "stroke");
serializer().attribute("joinstyle", "miter");
serializer().end_element(xmlns_v, "stroke");
serializer().start_element(xmlns_v, "path");
serializer().attribute("gradientshapeok", "t");
serializer().attribute(xml::qname(xmlns_o, "connecttype"), "rect");
serializer().end_element(xmlns_v, "path");
serializer().end_element(xmlns_v, "shapetype");
std::size_t comment_index = 0;
for (const auto &cell_ref : cells)
{
auto comment = ws.get_cell(cell_ref).comment();
auto shape_id = 1024 * file_index + 1 + comment_index * 2;
serializer().start_element(xmlns_v, "shape");
serializer().attribute("id", "_x0000_s" + std::to_string(shape_id));
serializer().attribute("type", "#_x0000_t202");
std::string style("position:absolute;margin-left:80pt;margin-top:");
style.append(std::to_string(2 + comment_index * 4));
style.append("pt;width:104pt;height:76pt;z-index:");
style.append(std::to_string(comment_index + 1));
style.append(";visibility:hidden");
serializer().attribute("style", style);
serializer().attribute("fillcolor", "#fbf6d6");
serializer().attribute("strokecolor", "#edeaa1");
serializer().start_element(xmlns_v, "shape");
serializer().attribute("id", "_x0000_s" + std::to_string(shape_id));
serializer().attribute("type", "#_x0000_t202");
serializer().start_element(xmlns_v, "fill");
serializer().attribute("color2", "#fbfe82");
serializer().attribute("angle", -180);
serializer().attribute("type", "gradient");
serializer().start_element(xmlns_o, "fill");
serializer().attribute(xml::qname(xmlns_v, "ext"), "view");
serializer().attribute("type", "gradientUnscaled");
serializer().end_element(xmlns_o, "fill");
serializer().end_element(xmlns_v, "fill");
std::vector<std::pair<std::string, std::string>> style;
serializer().start_element(xmlns_v, "shadow");
serializer().attribute("on", "t");
serializer().attribute("obscured", "t");
serializer().end_element(xmlns_v, "shadow");
style.push_back({"position", "absolute"});
style.push_back({"margin-left", std::to_string(comment.left())});
style.push_back({"margin-top", std::to_string(comment.top())});
style.push_back({"width", std::to_string(comment.width())});
style.push_back({"height", std::to_string(comment.height())});
style.push_back({"z-index", std::to_string(comment_index + 1)});
style.push_back({"visibility", comment.visible() ? "visible" : "hidden"});
serializer().start_element(xmlns_v, "path");
serializer().attribute(xml::qname(xmlns_o, "connecttype"), "none");
serializer().end_element(xmlns_v, "path");
std::string style_string;
serializer().start_element(xmlns_v, "textbox");
serializer().attribute("style", "mso-direction-alt:auto");
serializer().start_element("div");
serializer().attribute("style", "text-align:left");
serializer().characters("");
serializer().end_element("div");
serializer().end_element(xmlns_v, "textbox");
for (auto part : style)
{
style_string.append(part.first);
style_string.append(":");
style_string.append(part.second);
style_string.append(":");
}
serializer().start_element(xmlns_x, "ClientData");
serializer().attribute("ObjectType", "Note");
serializer().start_element(xmlns_x, "MoveWithCells");
serializer().end_element(xmlns_x, "MoveWithCells");
serializer().start_element(xmlns_x, "SizeWithCells");
serializer().end_element(xmlns_x, "SizeWithCells");
serializer().start_element(xmlns_x, "Anchor");
serializer().characters("1, 15, 0, " + std::to_string(2 + comment_index * 4) + ", 2, 54, 4, 14");
serializer().end_element(xmlns_x, "Anchor");
serializer().start_element(xmlns_x, "AutoFill");
serializer().characters("False");
serializer().end_element(xmlns_x, "AutoFill");
serializer().start_element(xmlns_x, "Row");
serializer().characters(cell_ref.get_row() - 1);
serializer().end_element(xmlns_x, "Row");
serializer().start_element(xmlns_x, "Column");
serializer().characters(cell_ref.get_column_index().index - 1);
serializer().end_element(xmlns_x, "Column");
serializer().end_element(xmlns_x, "ClientData");
serializer().attribute("style", style_string);
serializer().attribute("fillcolor", "#fbf6d6");
serializer().attribute("strokecolor", "#edeaa1");
serializer().end_element(xmlns_v, "shape");
serializer().start_element(xmlns_v, "fill");
serializer().attribute("color2", "#fbfe82");
serializer().attribute("angle", -180);
serializer().attribute("type", "gradient");
serializer().start_element(xmlns_o, "fill");
serializer().attribute(xml::qname(xmlns_v, "ext"), "view");
serializer().attribute("type", "gradientUnscaled");
serializer().end_element(xmlns_o, "fill");
serializer().end_element(xmlns_v, "fill");
++comment_index;
}
serializer().start_element(xmlns_v, "shadow");
serializer().attribute("on", "t");
serializer().attribute("obscured", "t");
serializer().end_element(xmlns_v, "shadow");
serializer().end_element("xml");
serializer().start_element(xmlns_v, "path");
serializer().attribute(xml::qname(xmlns_o, "connecttype"), "none");
serializer().end_element(xmlns_v, "path");
serializer().start_element(xmlns_v, "textbox");
serializer().attribute("style", "mso-direction-alt:auto");
serializer().start_element("div");
serializer().attribute("style", "text-align:left");
serializer().characters("");
serializer().end_element("div");
serializer().end_element(xmlns_v, "textbox");
serializer().start_element(xmlns_x, "ClientData");
serializer().attribute("ObjectType", "Note");
serializer().start_element(xmlns_x, "MoveWithCells");
serializer().end_element(xmlns_x, "MoveWithCells");
serializer().start_element(xmlns_x, "SizeWithCells");
serializer().end_element(xmlns_x, "SizeWithCells");
serializer().start_element(xmlns_x, "Anchor");
serializer().characters("1, 15, 0, " + std::to_string(2 + comment_index * 4) + ", 2, 54, 4, 14");
serializer().end_element(xmlns_x, "Anchor");
serializer().start_element(xmlns_x, "AutoFill");
serializer().characters("False");
serializer().end_element(xmlns_x, "AutoFill");
serializer().start_element(xmlns_x, "Row");
serializer().characters(cell_ref.get_row() - 1);
serializer().end_element(xmlns_x, "Row");
serializer().start_element(xmlns_x, "Column");
serializer().characters(cell_ref.get_column_index().index - 1);
serializer().end_element(xmlns_x, "Column");
serializer().end_element(xmlns_x, "ClientData");
serializer().end_element(xmlns_v, "shape");
++comment_index;
}
serializer().end_element("xml");
}
// Other Parts

2
third-party/botan vendored

@ -1 +1 @@
Subproject commit e6bf87b6b0d4d9c3877b79de53de58dc34a3acca
Subproject commit 923a95d546df5b6d31f39b0af900d0361fb2e6a6

2
third-party/pugixml vendored

@ -1 +1 @@
Subproject commit e4c43a0aa2d5cbc158dd279cb2bf6d21f2d27c83
Subproject commit a832e8a5eff11f58a00ca41ec51ff3895b0da165

2
third-party/utfcpp vendored

@ -1 +1 @@
Subproject commit 0232ab8188b16ae6f2293a5817f1d9b0030879a3
Subproject commit a5ad5ec9d936d63e9c010d1054f8b11fed0fabbc