fix styles -- Excel wanted to repair saved workbooks

pull/46/head
Thomas Fussell 2016-05-15 15:03:02 -04:00
parent 365e2f93fc
commit 135af6e6c9
19 changed files with 176 additions and 88 deletions

View File

@ -64,7 +64,8 @@ public:
extended_properties,
core_properties,
office_document,
custom_xml
custom_xml,
thumbnail
};
static type type_from_string(const std::string &type_string);

View File

@ -67,11 +67,11 @@ protected:
private:
std::experimental::optional<side> start_;
std::experimental::optional<side> end_;
std::experimental::optional<side> left_;
std::experimental::optional<side> right_;
std::experimental::optional<side> top_;
std::experimental::optional<side> bottom_;
std::experimental::optional<side> diagonal_;
std::experimental::optional<side> left_ = side();
std::experimental::optional<side> right_ = side();
std::experimental::optional<side> top_ = side();
std::experimental::optional<side> bottom_ = side();
std::experimental::optional<side> diagonal_ = side();
std::experimental::optional<side> vertical_;
std::experimental::optional<side> horizontal_;

View File

@ -122,8 +122,8 @@ protected:
std::string to_hash_string() const override;
private:
type type_ = type::none;
pattern_type pattern_type_;
type type_ = type::pattern;
pattern_type pattern_type_ = pattern_type::none;
gradient_type gradient_type_;
double rotation_ = 0;

View File

@ -95,18 +95,18 @@ private:
friend class style;
std::string name_ = "Calibri";
std::size_t size_ = 11;
std::size_t size_ = 12;
bool bold_ = false;
bool italic_ = false;
bool superscript_ = false;
bool subscript_ = false;
underline_style underline_ = underline_style::none;
bool strikethrough_ = false;
color color_;
bool has_family_ = false;
std::size_t family_;
bool has_scheme_ = false;
std::string scheme_;
color color_ = color(xlnt::color::type::theme, 1);
bool has_family_ = true;
std::size_t family_ = 2;
bool has_scheme_ = true;
std::string scheme_ = "minor";
};
} // namespace xlnt

View File

@ -54,6 +54,7 @@ class range;
class range_reference;
class relationship;
class style;
class style_serializer;
class text;
class theme;
class worksheet;
@ -196,11 +197,13 @@ public:
style &get_style(const std::string &name);
const style &get_style(const std::string &name) const;
style &create_style(const std::string &name);
void clear_styles();
const std::vector<style> &get_styles() const;
manifest &get_manifest();
const manifest &get_manifest() const;
void create_root_relationship(const std::string &id, const std::string &target, relationship::type type);
const std::vector<relationship> &get_root_relationships() const;
void add_shared_string(const text &shared, bool allow_duplicates=false);
@ -212,6 +215,10 @@ public:
private:
friend class worksheet;
friend class style_serializer;
std::string next_relationship_id() const;
std::unique_ptr<detail::workbook_impl> d_;
};

View File

@ -25,9 +25,20 @@
#include <iterator>
#include <vector>
#include <detail/worksheet_impl.hpp>
#include <xlnt/packaging/app_properties.hpp>
#include <xlnt/packaging/document_properties.hpp>
#include <xlnt/packaging/manifest.hpp>
#include <xlnt/workbook/theme.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/sheet_view.hpp>
namespace xlnt {
namespace detail {
struct worksheet_impl;
struct workbook_impl
{
workbook_impl();

View File

@ -20,14 +20,20 @@
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <xlnt/workbook/named_range.hpp>
#include <xlnt/worksheet/range.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/sheet_view.hpp>
#include <xlnt/worksheet/column_properties.hpp>
#include <xlnt/worksheet/row_properties.hpp>
#include "cell_impl.hpp"
#include <detail/cell_impl.hpp>
namespace xlnt {

View File

@ -26,7 +26,9 @@
namespace xlnt {
document_properties::document_properties()
: created(1900, 1, 1), modified(1900, 1, 1), excel_base_date(calendar::windows_1900)
: created(1900, 1, 1),
modified(1900, 1, 1),
excel_base_date(calendar::windows_1900)
{
}

View File

@ -81,6 +81,10 @@ relationship::type relationship::type_from_string(const std::string &type_string
{
return type::custom_xml;
}
else if (type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
{
return type::thumbnail;
}
return type::invalid;
}
@ -109,6 +113,8 @@ std::string relationship::type_to_string(type t)
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet";
case type::custom_xml:
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";
case type::thumbnail:
return "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail";
default:
return "??";
}

View File

@ -106,6 +106,13 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl
}
xlnt::relationship_serializer relationship_serializer_(archive);
auto root_relationships = relationship_serializer_.read_relationships("");
for (const auto &relationship : root_relationships)
{
wb.create_root_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type());
}
auto workbook_relationships = relationship_serializer_.read_relationships(xlnt::constants::ArcWorkbook());
for (const auto &relationship : workbook_relationships)
@ -247,9 +254,13 @@ void excel_serializer::write_data(bool /*as_template*/)
theme_serializer theme_serializer_;
archive_.writestr(constants::ArcTheme(), theme_serializer_.write_theme(workbook_.get_loaded_theme()).to_string());
archive_.writestr(
constants::ArcSharedString(),
xml_serializer::serialize(xlnt::shared_strings_serializer::write_shared_strings(workbook_.get_shared_strings())));
if (!workbook_.get_shared_strings().empty())
{
const auto &strings = workbook_.get_shared_strings();
auto shared_strings_xml = xlnt::shared_strings_serializer::write_shared_strings(strings);
archive_.writestr(constants::ArcSharedString(), xml_serializer::serialize(shared_strings_xml));
}
archive_.writestr(constants::ArcWorkbook(), xml_serializer::serialize(workbook_serializer_.write_workbook()));

View File

@ -95,8 +95,8 @@ bool relationship_serializer::write_relationships(const std::vector<relationship
auto relationship_node = root_node.add_child("Relationship");
relationship_node.add_attribute("Id", relationship.get_id());
relationship_node.add_attribute("Target", relationship.get_target_uri());
relationship_node.add_attribute("Type", relationship.get_type_string());
relationship_node.add_attribute("Target", relationship.get_target_uri());
if (relationship.get_target_mode() == target_mode::external)
{

View File

@ -25,6 +25,7 @@
#include <unordered_set>
#include <xlnt/serialization/style_serializer.hpp>
#include <detail/workbook_impl.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/serialization/xml_document.hpp>
#include <xlnt/serialization/xml_node.hpp>
@ -395,6 +396,11 @@ bool style_serializer::read_stylesheet(const xml_document &xml)
read_styles(stylesheet_node.get_child("cellStyles"), stylesheet_node.get_child("cellStyleXfs"));
read_formats(stylesheet_node.get_child("cellXfs"));
if (!styles_.empty())
{
workbook_.clear_styles();
}
for (const auto &ns : styles_)
{
workbook_.create_style(ns.get_name()) = ns;
@ -407,7 +413,7 @@ bool style_serializer::read_stylesheet(const xml_document &xml)
for (const auto &s : formats_)
{
workbook_.add_format(s);
workbook_.d_->formats_.push_back(s);
}
return true;
@ -758,27 +764,7 @@ color style_serializer::read_color(const xml_node &color_node)
void style_serializer::initialize_vectors()
{
bool any_style = false;
for (auto ws : workbook_)
{
for (auto row : ws)
{
for (auto cell : row)
{
if (cell.has_format())
{
any_style = true;
}
}
}
}
if (any_style)
{
formats_.assign(workbook_.get_formats().begin(), workbook_.get_formats().end());
}
formats_.assign(workbook_.get_formats().begin(), workbook_.get_formats().end());
colors_.clear();
borders_.clear();
fills_.clear();
@ -1123,24 +1109,18 @@ bool style_serializer::write_formattable(const formattable &xf, xml_node xf_node
auto font_id = std::distance(fonts_.begin(), std::find(fonts_.begin(), fonts_.end(), xf.get_font()));
xf_node.add_attribute("fontId", std::to_string(font_id));
if (xf.fill_applied())
{
auto fill_id = std::distance(fills_.begin(), std::find(fills_.begin(), fills_.end(), xf.get_fill()));
xf_node.add_attribute("fillId", std::to_string(fill_id));
}
auto fill_id = std::distance(fills_.begin(), std::find(fills_.begin(), fills_.end(), xf.get_fill()));
xf_node.add_attribute("fillId", std::to_string(fill_id));
if (xf.border_applied())
{
auto border_id = std::distance(borders_.begin(), std::find(borders_.begin(), borders_.end(), xf.get_border()));
xf_node.add_attribute("borderId", std::to_string(border_id));
}
auto border_id = std::distance(borders_.begin(), std::find(borders_.begin(), borders_.end(), xf.get_border()));
xf_node.add_attribute("borderId", std::to_string(border_id));
xf_node.add_attribute("applyNumberFormat", xf.number_format_applied() ? "1" : "0");
xf_node.add_attribute("applyFill", xf.fill_applied() ? "1" : "0");
xf_node.add_attribute("applyFont", xf.font_applied() ? "1" : "0");
xf_node.add_attribute("applyBorder", xf.border_applied() ? "1" : "0");
xf_node.add_attribute("applyAlignment", xf.alignment_applied() ? "1" : "0");
xf_node.add_attribute("applyProtection", xf.protection_applied() ? "1" : "0");
if(xf.number_format_applied()) xf_node.add_attribute("applyNumberFormat", "1");
if(xf.fill_applied()) xf_node.add_attribute("applyFill", "1");
if(xf.font_applied()) xf_node.add_attribute("applyFont", "1");
if(xf.border_applied()) xf_node.add_attribute("applyBorder", "1");
if(xf.alignment_applied()) xf_node.add_attribute("applyAlignment", "1");
if(xf.protection_applied()) xf_node.add_attribute("applyProtection", "1");
if (xf.alignment_applied())
{
@ -1205,6 +1185,8 @@ bool style_serializer::write_formattable(const formattable &xf, xml_node xf_node
}
}
//TODO protection
return true;
}
@ -1270,8 +1252,8 @@ bool write_dxfs(xlnt::xml_node &dxfs_node)
bool write_table_styles(xlnt::xml_node &table_styles_node)
{
table_styles_node.add_attribute("count", "0");
table_styles_node.add_attribute("defaultTableStyle", "TableStyleMedium2");
table_styles_node.add_attribute("defaultPivotStyle", "PivotStyleMedium9");
table_styles_node.add_attribute("defaultTableStyle", "TableStyleMedium9");
table_styles_node.add_attribute("defaultPivotStyle", "PivotStyleMedium7");
return true;
}
@ -1309,8 +1291,11 @@ xml_document style_serializer::write_stylesheet()
root_node.add_attribute("mc:Ignorable", "x14ac");
doc.add_namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
auto number_formats_node = root_node.add_child("numFmts");
write_number_formats(number_formats_node);
if (number_formats_.size() > 1 || !(number_formats_.front() == number_format::general()))
{
auto number_formats_node = root_node.add_child("numFmts");
write_number_formats(number_formats_node);
}
auto fonts_node = root_node.add_child("fonts");
write_fonts(fonts_node);

View File

@ -29,6 +29,19 @@ public:
return wb;
}
void test_temp()
{
xlnt::workbook wb;
wb.load("/Users/thomas/Desktop/Workbook1.xlsx");
//xlnt::worksheet ws = wb.get_active_sheet();
//ws.get_cell("A1").set_value(5);
//ws.get_cell("B2").set_value("string data");
//ws.get_cell("C3").set_formula("=RAND()");
//ws.merge_cells("C3:C4");
//ws.freeze_panes("B2");
wb.save("/Users/thomas/Desktop/Workbook1-rt.xlsx");
}
void test_read_standard_workbook()
{

View File

@ -46,6 +46,7 @@ public:
void test_write_workbook_rels()
{
xlnt::workbook wb;
wb.add_shared_string(xlnt::text());
xlnt::zip_file archive;
xlnt::relationship_serializer serializer(archive);
serializer.write_relationships(wb.get_relationships(), "xl/workbook.xml");

View File

@ -220,8 +220,8 @@ xml_document workbook_serializer::write_properties_app() const
auto heading_pairs_node = root_node.add_child("HeadingPairs");
auto heading_pairs_vector_node = heading_pairs_node.add_child("vt:vector");
heading_pairs_vector_node.add_attribute("baseType", "variant");
heading_pairs_vector_node.add_attribute("size", "2");
heading_pairs_vector_node.add_attribute("baseType", "variant");
heading_pairs_vector_node.add_child("vt:variant").add_child("vt:lpstr").set_text("Worksheets");
heading_pairs_vector_node.add_child("vt:variant")
.add_child("vt:i4")
@ -229,8 +229,8 @@ xml_document workbook_serializer::write_properties_app() const
auto titles_of_parts_node = root_node.add_child("TitlesOfParts");
auto titles_of_parts_vector_node = titles_of_parts_node.add_child("vt:vector");
titles_of_parts_vector_node.add_attribute("baseType", "lpstr");
titles_of_parts_vector_node.add_attribute("size", std::to_string(workbook_.get_sheet_names().size()));
titles_of_parts_vector_node.add_attribute("baseType", "lpstr");
for (auto ws : workbook_)
{

View File

@ -35,7 +35,7 @@ namespace xlnt {
std::string xml_serializer::serialize(const xml_document &xml)
{
std::ostringstream ss;
xml.d_->doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8);
xml.d_->doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8);
return ss.str();
}

View File

@ -20,27 +20,44 @@ public:
auto stylesheet_xml = style_serializer.write_stylesheet().to_string();
const std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\">"
" <numFmts count=\"1\">"
" <numFmt numFmtId=\"0\" formatCode=\"General\" />"
" </numFmts>"
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"11\" />"
" <color indexed=\"0\" />"
" <name val=\"Calibri\" />"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"0\" />"
" <borders count=\"0\" />"
" <cellStyleXfs count=\"0\" />"
" <cellXfs count=\"0\" />"
" <cellStyles count=\"0\" />"
" <dxfs count=\"0\" />"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium2\" defaultPivotStyle=\"PivotStyleMedium9\" />"
" <fills count=\"1\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
" <extLst>"
" <ext uri=\"{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}\" xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\">"
" <x14:slicerStyles defaultSlicerStyle=\"SlicerStyleLight1\" />"
" <ext xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" uri=\"{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}\">"
" <x14:slicerStyles defaultSlicerStyle=\"SlicerStyleLight1\"/>"
" </ext>"
" </extLst>"
"</styleSheet>";

View File

@ -70,9 +70,8 @@ workbook::workbook() : d_(new detail::workbook_impl())
{
create_sheet("Sheet");
create_relationship("rId2", "sharedStrings.xml", relationship::type::shared_strings);
create_relationship("rId3", "styles.xml", relationship::type::styles);
create_relationship("rId4", "theme/theme1.xml", relationship::type::theme);
create_relationship("rId2", "styles.xml", relationship::type::styles);
create_relationship("rId3", "theme/theme1.xml", relationship::type::theme);
d_->encoding_ = encoding::utf8;
@ -82,11 +81,11 @@ workbook::workbook() : d_(new detail::workbook_impl())
d_->manifest_.add_override_type("/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
d_->manifest_.add_override_type("/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml");
d_->manifest_.add_override_type("/xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
d_->manifest_.add_override_type("/xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
d_->manifest_.add_override_type("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml");
d_->manifest_.add_override_type("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml");
add_format(format());
create_style("Normal");
}
workbook::workbook(encoding e) : workbook()
@ -268,6 +267,11 @@ void workbook::create_relationship(const std::string &id, const std::string &tar
d_->relationships_.push_back(relationship(type, id, target));
}
void workbook::create_root_relationship(const std::string &id, const std::string &target, relationship::type type)
{
d_->root_relationships_.push_back(relationship(type, id, target));
}
relationship workbook::get_relationship(const std::string &id) const
{
for (auto &rel : d_->relationships_)
@ -646,6 +650,11 @@ std::size_t workbook::add_format(const format &style)
return d_->formats_.size() - 1;
}
void workbook::clear_styles()
{
d_->styles_.clear();
}
void workbook::clear_formats()
{
d_->formats_.clear();
@ -697,6 +706,12 @@ const std::vector<text> &workbook::get_shared_strings() const
void workbook::add_shared_string(const text &shared, bool allow_duplicates)
{
if (d_->shared_strings_.empty())
{
create_relationship(next_relationship_id(), "sharedStrings.xml", relationship::type::shared_strings);
d_->manifest_.add_override_type("/xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
}
if (!allow_duplicates)
{
//TODO: inefficient, use a set or something?
@ -761,4 +776,17 @@ const style &workbook::get_style(const std::string &name) const
[&name](const style &s) { return s.get_name() == name; });
}
std::string workbook::next_relationship_id() const
{
std::size_t i = 1;
while (std::find_if(d_->relationships_.begin(), d_->relationships_.end(),
[i](const relationship &r) { return r.get_id() == "rId" + std::to_string(i); }) != d_->relationships_.end())
{
i++;
}
return "rId" + std::to_string(i);
}
} // namespace xlnt

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Target="worksheets/sheet1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"/>
<Relationship Id="rId2" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
<Relationship Id="rId3" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
<Relationship Id="rId4" Target="theme/theme1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"/>
<Relationship Id="rId2" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
<Relationship Id="rId3" Target="theme/theme1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"/>
<Relationship Id="rId4" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
</Relationships>