mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
minimum support for xlsm
This commit is contained in:
parent
e66e417b0c
commit
7f44dc2274
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,3 +18,4 @@ python/record.txt
|
|||
python/xlntpyarrow.egg-info/
|
||||
/x64/
|
||||
.envrc
|
||||
.vscode
|
||||
|
|
|
@ -88,6 +88,7 @@ enum class XLNT_API relationship_type
|
|||
vml_drawing,
|
||||
volatile_dependencies,
|
||||
worksheet,
|
||||
vbaproject,
|
||||
|
||||
// Worksheet parts
|
||||
hyperlink,
|
||||
|
|
|
@ -798,6 +798,11 @@ public:
|
|||
/// </summary>
|
||||
const std::vector<std::uint8_t> &thumbnail() const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns stored binary data.
|
||||
/// </summary>
|
||||
const std::unordered_map<std::string, std::vector<std::uint8_t>>& binaries() const;
|
||||
|
||||
// Calculation properties
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -102,6 +102,7 @@ struct workbook_impl
|
|||
&& manifest_ == other.manifest_
|
||||
&& theme_ == other.theme_
|
||||
&& images_ == other.images_
|
||||
&& binaries_ == other.binaries_
|
||||
&& core_properties_ == other.core_properties_
|
||||
&& extended_properties_ == other.extended_properties_
|
||||
&& custom_properties_ == other.custom_properties_
|
||||
|
@ -130,6 +131,7 @@ struct workbook_impl
|
|||
manifest manifest_;
|
||||
optional<theme> theme_;
|
||||
std::unordered_map<std::string, std::vector<std::uint8_t>> images_;
|
||||
std::unordered_map<std::string, std::vector<std::uint8_t>> binaries_;
|
||||
|
||||
std::vector<std::pair<xlnt::core_property, variant>> core_properties_;
|
||||
std::vector<std::pair<xlnt::extended_property, variant>> extended_properties_;
|
||||
|
|
|
@ -119,6 +119,8 @@ std::string to_string(relationship_type t)
|
|||
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table";
|
||||
case relationship_type::volatile_dependencies:
|
||||
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/volatileDependencies";
|
||||
case relationship_type::vbaproject:
|
||||
return "http://schemas.microsoft.com/office/2006/relationships/vbaProject";
|
||||
case relationship_type::image:
|
||||
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
||||
case relationship_type::unknown:
|
||||
|
|
|
@ -162,6 +162,8 @@ relationship_type from_string(const std::string &string)
|
|||
return relationship_type::table_definition;
|
||||
else if (string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/volatileDependencies")
|
||||
return relationship_type::volatile_dependencies;
|
||||
else if (string == "http://schemas.microsoft.com/office/2006/relationships/vbaProject")
|
||||
return relationship_type::vbaproject;
|
||||
else if (string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
|
||||
return relationship_type::image;
|
||||
|
||||
|
|
|
@ -1240,6 +1240,13 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
|
|||
read_drawings(ws, drawings_part);
|
||||
}
|
||||
|
||||
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::printer_settings))
|
||||
{
|
||||
read_part({workbook_rel, sheet_rel,
|
||||
manifest.relationship(sheet_path,
|
||||
relationship_type::printer_settings)});
|
||||
}
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
|
@ -1557,6 +1564,7 @@ void xlsx_consumer::read_part(const std::vector<relationship> &rel_chain)
|
|||
break;
|
||||
|
||||
case relationship_type::printer_settings:
|
||||
read_binary(part_path);
|
||||
break;
|
||||
|
||||
case relationship_type::custom_property:
|
||||
|
@ -1589,6 +1597,10 @@ void xlsx_consumer::read_part(const std::vector<relationship> &rel_chain)
|
|||
case relationship_type::table_definition:
|
||||
break;
|
||||
|
||||
case relationship_type::vbaproject:
|
||||
read_binary(part_path);
|
||||
break;
|
||||
|
||||
case relationship_type::image:
|
||||
read_image(part_path);
|
||||
break;
|
||||
|
@ -1733,7 +1745,10 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
|
|||
"openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
|
||||
&& content_type !=
|
||||
"application/vnd."
|
||||
"openxmlformats-officedocument.spreadsheetml.template.main+xml")
|
||||
"openxmlformats-officedocument.spreadsheetml.template.main+xml"
|
||||
&& content_type !=
|
||||
"application/vnd."
|
||||
"ms-excel.sheet.macroEnabled.main+xml")
|
||||
{
|
||||
throw xlnt::invalid_file(content_type);
|
||||
}
|
||||
|
@ -1995,25 +2010,20 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_
|
|||
auto workbook_rel = manifest().relationship(path("/"), relationship_type::office_document);
|
||||
auto workbook_path = workbook_rel.target().path();
|
||||
|
||||
if (manifest().has_relationship(workbook_path, relationship_type::shared_string_table))
|
||||
{
|
||||
read_part({workbook_rel,
|
||||
manifest().relationship(workbook_path,
|
||||
relationship_type::shared_string_table)});
|
||||
}
|
||||
const auto rel_types = {
|
||||
relationship_type::shared_string_table,
|
||||
relationship_type::stylesheet,
|
||||
relationship_type::theme,
|
||||
relationship_type::vbaproject,
|
||||
};
|
||||
|
||||
if (manifest().has_relationship(workbook_path, relationship_type::stylesheet))
|
||||
for (auto rel_type : rel_types)
|
||||
{
|
||||
read_part({workbook_rel,
|
||||
manifest().relationship(workbook_path,
|
||||
relationship_type::stylesheet)});
|
||||
}
|
||||
|
||||
if (manifest().has_relationship(workbook_path, relationship_type::theme))
|
||||
{
|
||||
read_part({workbook_rel,
|
||||
manifest().relationship(workbook_path,
|
||||
relationship_type::theme)});
|
||||
if (manifest().has_relationship(workbook_path, rel_type))
|
||||
{
|
||||
read_part({workbook_rel,
|
||||
manifest().relationship(workbook_path, rel_type)});
|
||||
}
|
||||
}
|
||||
|
||||
for (auto worksheet_rel : manifest().relationships(workbook_path, relationship_type::worksheet))
|
||||
|
@ -2907,6 +2917,14 @@ void xlsx_consumer::read_image(const xlnt::path &image_path)
|
|||
out_stream << image_streambuf.get();
|
||||
}
|
||||
|
||||
void xlsx_consumer::read_binary(const xlnt::path &binary_path)
|
||||
{
|
||||
auto binary_streambuf = archive_->open(binary_path);
|
||||
vector_ostreambuf buffer(target_.d_->binaries_[binary_path.string()]);
|
||||
std::ostream out_stream(&buffer);
|
||||
out_stream << binary_streambuf.get();
|
||||
}
|
||||
|
||||
std::string xlsx_consumer::read_text()
|
||||
{
|
||||
auto text = std::string();
|
||||
|
|
|
@ -252,6 +252,11 @@ private:
|
|||
/// </summary>
|
||||
void read_image(const path &part);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
void read_binary(const path &part);
|
||||
|
||||
// Common Section Readers
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -182,7 +182,10 @@ void xlsx_producer::begin_part(const path &part)
|
|||
end_part();
|
||||
current_part_streambuf_ = archive_->open(part);
|
||||
current_part_stream_.rdbuf(current_part_streambuf_.get());
|
||||
current_part_serializer_.reset(new xml::serializer(current_part_stream_, part.string()));
|
||||
|
||||
auto xml_serializer = new xml::serializer(current_part_stream_, part.string(), 0);
|
||||
xml_serializer->xml_decl("1.0", "UTF-8", "yes");
|
||||
current_part_serializer_.reset(xml_serializer);
|
||||
}
|
||||
|
||||
// Package Parts
|
||||
|
@ -672,6 +675,16 @@ void xlsx_producer::write_workbook(const relationship &rel)
|
|||
if (child_rel.type() == relationship_type::calculation_chain) continue;
|
||||
|
||||
path archive_path(child_rel.source().path().parent().append(child_rel.target().path()));
|
||||
|
||||
// write binary
|
||||
switch (child_rel.type())
|
||||
{
|
||||
case relationship_type::vbaproject:
|
||||
write_binary(archive_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
// write xml
|
||||
begin_part(archive_path);
|
||||
|
||||
switch (child_rel.type())
|
||||
|
@ -766,6 +779,8 @@ void xlsx_producer::write_workbook(const relationship &rel)
|
|||
break;
|
||||
case relationship_type::table_definition:
|
||||
break;
|
||||
case relationship_type::vbaproject:
|
||||
break;
|
||||
case relationship_type::image:
|
||||
break;
|
||||
}
|
||||
|
@ -3033,19 +3048,26 @@ void xlsx_producer::write_worksheet(const relationship &rel)
|
|||
archive_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""),
|
||||
[](const path &a, const std::string &b) { return a.append(b); });
|
||||
|
||||
begin_part(archive_path);
|
||||
if (child_rel.type() == relationship_type::printer_settings)
|
||||
{
|
||||
write_binary(archive_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
begin_part(archive_path);
|
||||
|
||||
if (child_rel.type() == relationship_type::comments)
|
||||
{
|
||||
write_comments(child_rel, ws, cells_with_comments);
|
||||
}
|
||||
else if (child_rel.type() == relationship_type::vml_drawing)
|
||||
{
|
||||
write_vml_drawings(child_rel, ws, cells_with_comments);
|
||||
}
|
||||
else if (child_rel.type() == relationship_type::drawings)
|
||||
{
|
||||
write_drawings(child_rel, ws);
|
||||
if (child_rel.type() == relationship_type::comments)
|
||||
{
|
||||
write_comments(child_rel, ws, cells_with_comments);
|
||||
}
|
||||
else if (child_rel.type() == relationship_type::vml_drawing)
|
||||
{
|
||||
write_vml_drawings(child_rel, ws, cells_with_comments);
|
||||
}
|
||||
else if (child_rel.type() == relationship_type::drawings)
|
||||
{
|
||||
write_drawings(child_rel, ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3303,6 +3325,15 @@ void xlsx_producer::write_image(const path &image_path)
|
|||
std::ostream(image_streambuf.get()) << &buffer;
|
||||
}
|
||||
|
||||
void xlsx_producer::write_binary(const path &binary_path)
|
||||
{
|
||||
end_part();
|
||||
|
||||
vector_istreambuf buffer(source_.d_->binaries_.at(binary_path.string()));
|
||||
auto image_streambuf = archive_->open(binary_path);
|
||||
std::ostream(image_streambuf.get()) << &buffer;
|
||||
}
|
||||
|
||||
std::string xlsx_producer::write_bool(bool boolean) const
|
||||
{
|
||||
return boolean ? "1" : "0";
|
||||
|
|
|
@ -99,6 +99,7 @@ private:
|
|||
void write_extended_properties(const relationship &rel);
|
||||
void write_custom_properties(const relationship &rel);
|
||||
void write_image(const path &image_path);
|
||||
void write_binary(const path &binary_path);
|
||||
|
||||
// SpreadsheetML-Specific Package Parts
|
||||
|
||||
|
|
|
@ -167,6 +167,8 @@ xlnt::path default_path(xlnt::relationship_type type, std::size_t index = 0)
|
|||
return path("/xl/vmlDrawing.xml");
|
||||
case relationship_type::volatile_dependencies:
|
||||
return path("/xl/volatileDependencies.xml");
|
||||
case relationship_type::vbaproject:
|
||||
return path("/xl/vbaProject.bin");
|
||||
case relationship_type::worksheet:
|
||||
return path("/xl/worksheets/sheet" + std::to_string(index) + ".xml");
|
||||
}
|
||||
|
@ -246,6 +248,8 @@ std::string content_type(xlnt::relationship_type type)
|
|||
return "application/vnd.openxmlformats-officedocument.vmlDrawing";
|
||||
case relationship_type::volatile_dependencies:
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml";
|
||||
case relationship_type::vbaproject:
|
||||
return "application/vnd.ms-office.vbaProject";
|
||||
case relationship_type::worksheet:
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
|
||||
}
|
||||
|
@ -1439,6 +1443,11 @@ const std::vector<std::uint8_t> &workbook::thumbnail() const
|
|||
return d_->images_.at(thumbnail_rel.target().to_string());
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::vector<std::uint8_t>> &workbook::binaries() const
|
||||
{
|
||||
return d_->binaries_;
|
||||
}
|
||||
|
||||
style workbook::create_style(const std::string &name)
|
||||
{
|
||||
return d_->stylesheet_.get().create_style(name);
|
||||
|
|
BIN
tests/data/17_xlsm.xlsm
Normal file
BIN
tests/data/17_xlsm.xlsm
Normal file
Binary file not shown.
|
@ -113,6 +113,7 @@ public:
|
|||
register_test(test_insert_too_many);
|
||||
register_test(test_insert_delete_moves_merges);
|
||||
register_test(test_hidden_sheet);
|
||||
register_test(test_xlsm_read_write);
|
||||
}
|
||||
|
||||
void test_new_worksheet()
|
||||
|
@ -1590,5 +1591,53 @@ public:
|
|||
wb.load(path_helper::test_file("16_hidden_sheet.xlsx"));
|
||||
xlnt_assert_equals(wb.sheet_hidden_by_index(1), true);
|
||||
}
|
||||
|
||||
void test_xlsm_read_write()
|
||||
{
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::test_file("17_xlsm.xlsm"));
|
||||
|
||||
auto ws = wb.sheet_by_title("Sheet1");
|
||||
auto rows = ws.rows();
|
||||
|
||||
xlnt_assert_equals(rows[0][0].value<std::string>(), "Values");
|
||||
xlnt_assert_equals(rows[1][0].value<int>(), 100);
|
||||
xlnt_assert_equals(rows[2][0].value<int>(), 200);
|
||||
xlnt_assert_equals(rows[3][0].value<int>(), 300);
|
||||
xlnt_assert_equals(rows[4][0].value<int>(), 400);
|
||||
xlnt_assert_equals(rows[5][0].value<int>(), 500);
|
||||
|
||||
xlnt_assert_equals(rows[0][1].value<std::string>(), "Sum");
|
||||
xlnt_assert_equals(rows[1][1].formula(), "SumVBA(A2:A6)");
|
||||
xlnt_assert_equals(rows[1][1].value<int>(), 1500);
|
||||
|
||||
// change sheet value
|
||||
ws.cell("A6").value(1000);
|
||||
|
||||
wb.save("17_xlsm_modified.xlsm");
|
||||
}
|
||||
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load("17_xlsm_modified.xlsm");
|
||||
|
||||
auto ws = wb.sheet_by_title("Sheet1");
|
||||
auto rows = ws.rows();
|
||||
|
||||
xlnt_assert_equals(rows[0][0].value<std::string>(), "Values");
|
||||
xlnt_assert_equals(rows[1][0].value<int>(), 100);
|
||||
xlnt_assert_equals(rows[2][0].value<int>(), 200);
|
||||
xlnt_assert_equals(rows[3][0].value<int>(), 300);
|
||||
xlnt_assert_equals(rows[4][0].value<int>(), 400);
|
||||
// sheet value changed (500 -> 1000)
|
||||
xlnt_assert_equals(rows[5][0].value<int>(), 1000);
|
||||
|
||||
xlnt_assert_equals(rows[0][1].value<std::string>(), "Sum");
|
||||
xlnt_assert_equals(rows[1][1].formula(), "SumVBA(A2:A6)");
|
||||
// formula value not changed (we can't execute vba)
|
||||
xlnt_assert_equals(rows[1][1].value<int>(), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
static worksheet_test_suite x;
|
||||
|
|
Loading…
Reference in New Issue
Block a user