change some things

pull/16/head
Thomas Fussell 2015-10-20 23:30:10 -04:00
parent cbab7a36cb
commit 21b3d366d6
64 changed files with 961 additions and 760 deletions

View File

@ -37,6 +37,7 @@ project "xlnt"
targetdir "../../lib/"
includedirs {
"../../include",
"../../source",
"../../third-party/miniz",
"../../third-party/pugixml/src"
}

View File

@ -44,8 +44,10 @@ struct datetime;
struct time;
struct timedelta;
namespace detail {
namespace detail {
struct cell_impl;
} // namespace detail
/// <summary>

View File

@ -7,7 +7,9 @@ namespace xlnt {
class cell;
namespace detail {
struct comment_impl;
} // namespace detail
/// <summary>

View File

@ -0,0 +1,7 @@
namespace xlnt {
class tokenizer {
};
} // namespace xlnt

View File

@ -0,0 +1,33 @@
#include <string>
#include <vector>
namespace xlnt {
class cell_reference;
class tokenizer;
class translator
{
translator(const std::string &formula, const cell_reference &ref);
std::vector<std::string> get_tokens();
static std::string translate_row(const std::string &row_str, int row_delta);
static std::string translate_col(const std::string &col_str, col_delta);
std::pair<std::string, std::string> strip_ws_name(const std::string &range_str);
void translate_range(const range_reference &range_ref);
void translate_formula(const cell_reference &dest);
private:
const std::string ROW_RANGE_RE;
const std::string COL_RANGE_RE;
const std::string CELL_REF_RE;
std::string formula_;
cell_reference reference_;
tokenizer tokenizer_;
};
} // namespace xlnt

View File

@ -23,11 +23,16 @@
// @author: see AUTHORS file
#pragma once
#include <string>
#include <vector>
namespace xlnt {
class workbook;
class excel_reader
{
};
std::string CentralDirectorySignature();
std::string repair_central_directory(const std::string &original);
workbook load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
workbook load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types = false, bool data_only = false);
} // namespace xlnt

View File

@ -1,59 +0,0 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
namespace xlnt {
class document_properties;
class relationship;
class style;
class workbook;
class worksheet;
class zip_file;
class reader
{
public:
static const std::string CentralDirectorySignature;
static std::string repair_central_directory(const std::string &original);
static void fast_parse(worksheet ws, std::istream &xml_source, const std::vector<std::string> &shared_string, const std::vector<style> &style_table, std::size_t color_index);
static std::vector<relationship> read_relationships(zip_file &content, const std::string &filename);
static std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive);
static std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types);
static worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats);
static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats);
static std::vector<std::string> read_shared_string(const std::string &xml_string);
static std::string read_dimension(const std::string &xml_string);
static document_properties read_properties_core(const std::string &xml_string);
static std::vector<std::pair<std::string,std::string>> read_sheets(zip_file &archive);
static workbook load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
static std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive);
};
} // namespace xlnt

View File

@ -0,0 +1,36 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <vector>
namespace xlnt {
std::vector<std::string> read_shared_strings(const std::string &xml_string);
void read_string_table(const std::string &xml_source);
std::string get_string();
std::string get_text();
} // namespace xlnt

View File

@ -30,13 +30,18 @@
#include "style.h"
namespace xlnt {
class workbook;
class style_reader
{
public:
style_reader(workbook &wb);
};
void read_styles();
void read_custom_num_formats();
void read_color_index();
void read_dxfs();
void read_fonts();
void read_fills();
void read_borders();
void read_named_styles();
void read_style_names();
void read_cell_styles();
void read_xfs();
void read_style_table();
} // namespace xlnt

View File

@ -24,13 +24,22 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace xlnt {
class workbook;
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes);
xlnt::workbook load_workbook(const std::string &filename);
class document_properties;
class relationship;
class workbook;
class zip_file;
std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive);
std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types);
document_properties read_properties_core(const std::string &xml_string);
std::vector<std::pair<std::string,std::string>> read_sheets(zip_file &archive);
std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive);
std::vector<relationship> read_relationships(zip_file &content, const std::string &filename);
} // namespace xlnt

View File

@ -23,11 +23,19 @@
// @author: see AUTHORS file
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
namespace xlnt {
class workbook;
class worksheet;
class worksheet_reader
{
};
worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats);
void read_worksheet(worksheet ws, std::istream &stream, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats);
void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats);
std::string read_dimension(const std::string &xml_string);
} // namespace xlnt

View File

@ -25,7 +25,7 @@
#include <cstddef>
#include <xlnt/styles/color.hpp>
#include "side.hpp"
namespace xlnt {
@ -36,24 +36,6 @@ struct optional
bool initialized;
};
enum class border_style
{
none,
dashdot,
dashdotdot,
dashed,
dotted,
double_,
hair,
medium,
mediumdashdot,
mediumdashdotdot,
mediumdashed,
slantdashdot,
thick,
thin
};
enum class diagonal_direction
{
none,
@ -62,21 +44,6 @@ enum class diagonal_direction
both
};
class side
{
public:
side(border_style style = border_style::none, color = color::black);
bool operator==(const side &other) const
{
return other.style_ == style_;
}
private:
border_style style_;
color color_;
};
class border
{
public:

View File

@ -68,17 +68,4 @@ public:
virtual std::size_t hash() const { return 0; }
};
class pattern_fill : public fill
{
public:
void set_pattern_type(const std::string &type) { type_ = type; }
void set_foreground_color(const std::string &hex) { foreground_color_ = hex; }
std::size_t hash() const override { return 0; }
private:
std::string type_;
std::string foreground_color_;
};
} // namespace xlnt

View File

@ -0,0 +1,39 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include "fill.hpp"
namespace xlnt {
class gradient_fill : public fill
{
public:
std::size_t hash() const override { return 0; }
private:
};
} // namespace xlnt

View File

@ -0,0 +1,90 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include "alignment.hpp"
#include "borders.hpp"
#include "fill.hpp"
#include "font.hpp"
#include "number_format.hpp"
#include "protection.hpp"
namespace xlnt {
class workbook;
class style
{
public:
style();
std::size_t hash() const;
const alignment get_alignment() const;
const border get_border() const;
const fill get_fill() const;
const font get_font() const;
const number_format get_number_format() const;
const protection get_protection() const;
bool pivot_button() const;
bool quote_prefix() const;
std::size_t get_fill_index() const { return fill_index_; }
std::size_t get_font_index() const { return font_index_; }
std::size_t get_border_index() const { return border_index_; }
std::size_t get_number_format_index() const { return number_format_index_; }
bool operator==(const style &other) const
{
return hash() == other.hash();
}
private:
friend class workbook;
std::size_t style_index_;
std::size_t alignment_index_;
alignment alignment_;
std::size_t border_index_;
border border_;
std::size_t fill_index_;
fill fill_;
std::size_t font_index_;
font font_;
std::size_t number_format_index_;
number_format number_format_;
std::size_t protection_index_;
protection protection_;
bool pivot_button_;
bool quote_prefix_;
};
} // namespace xlnt

View File

@ -0,0 +1,43 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include "color.hpp"
namespace xlnt {
class pattern_fill : public fill
{
public:
void set_pattern_type(const std::string &type) { type_ = type; }
void set_foreground_color(const std::string &hex) { foreground_color_ = hex; }
std::size_t hash() const override { return 0; }
private:
std::string type_;
std::string foreground_color_;
};
} // namespace xlnt

View File

@ -23,43 +23,43 @@
// @author: see AUTHORS file
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <cstddef>
#include "charts/chart.h"
#include <xlnt/styles/color.hpp>
namespace xlnt {
class chart_reader
enum class border_style
{
none,
dashdot,
dashdotdot,
dashed,
dotted,
double_,
hair,
medium,
mediumdashdot,
mediumdashdotdot,
mediumdashed,
slantdashdot,
thick,
thin
};
class pie_chart_reader : public chart_reader
{
};
class pie_chart_reader : public chart_reader
{
};
class pie_chart_reader : public chart_reader
{
};
class pie_chart_reader : public chart_reader
{
};
class chart_reader_factory
class side
{
public:
static std::unique_ptr<chart_reader> create_reader(chart c);
side(border_style style = border_style::none, color = color::black);
bool operator==(const side &other) const
{
return other.style_ == style_;
}
private:
border_style style_;
color color_;
};
} // namespace xlnt

View File

@ -24,7 +24,7 @@
#pragma once
#include "alignment.hpp"
#include "borders.hpp"
#include "border.hpp"
#include "fill.hpp"
#include "font.hpp"
#include "number_format.hpp"

View File

@ -0,0 +1,9 @@
#pragma once
namespace xlnt {
class external_book {
};
}

View File

@ -27,14 +27,14 @@ namespace xlnt {
class column_properties
{
public:
void set_height(int height) { this->height = height; }
int row_index;
int height;
bool visible;
int outline_level;
bool collapsed;
int style_index;
public:
void set_height(int height) { this->height = height; }
int row_index;
int height;
bool visible;
int outline_level;
bool collapsed;
int style_index;
};
} // namespace xlnt

View File

@ -1,5 +1,4 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -25,9 +24,8 @@
namespace xlnt {
class theme_reader
class pane
{
};
} // namespace xlnt

View File

@ -27,14 +27,14 @@ namespace xlnt {
class row_properties
{
public:
void set_height(int height) { this->height = height; }
int row_index;
int height;
bool visible;
int outline_level;
bool collapsed;
int style_index;
public:
void set_height(int height) { this->height = height; }
int row_index;
int height;
bool visible;
int outline_level;
bool collapsed;
int style_index;
};
} // namespace xlnt

View File

@ -1,5 +1,4 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -25,8 +24,9 @@
namespace xlnt {
class string_reader
{
class selection {
};
};

View File

@ -1,5 +1,4 @@
// Copyright (c) 2015 Thomas Fussell
// Copyright (c) 2010-2015 openpyxl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -24,10 +23,9 @@
#pragma once
namespace xlnt {
class drawing_reader
class sheet_view
{
};
} // namespace xlnt

View File

@ -25,9 +25,7 @@
namespace xlnt {
class drawing_writer
{
class worksheet_properties {
};
} // namespace xlnt

View File

@ -33,21 +33,20 @@ const std::string author_email = "thomas.fussell@gmail.com";
const std::string url = "https://github.com/tfussell/xlnt";
const std::string download_url = "https://github.com/tfussell/xlnt/archive/master.zip";
#include "workbook/workbook.hpp"
#include "worksheet/worksheet.hpp"
#include "cell/cell_reference.hpp"
#include "cell/cell.hpp"
#include "common/relationship.hpp"
#include "cell/cell_reference.hpp"
#include "cell/comment.hpp"
#include "common/datetime.hpp"
#include "writer/writer.hpp"
#include "writer/style_writer.hpp"
#include "worksheet/range_reference.hpp"
#include "worksheet/range.hpp"
#include "common/encoding.hpp"
#include "common/exceptions.hpp"
#include "reader/reader.hpp"
#include "common/relationship.hpp"
#include "common/string_table.hpp"
#include "common/zip_file.hpp"
#include "reader/excel_reader.hpp"
#include "workbook/document_properties.hpp"
#include "cell/comment.hpp"
#include "common/encoding.hpp"
#include "workbook/named_range.hpp"
#include "workbook/workbook.hpp"
#include "worksheet/range.hpp"
#include "worksheet/range_reference.hpp"
#include "worksheet/worksheet.hpp"
#include "writer/workbook_writer.hpp"

View File

@ -1,476 +0,0 @@
#include <algorithm>
#include <pugixml.hpp>
#include <xlnt/cell/cell.hpp>
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/relationship.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/reader.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include <xlnt/styles/number_format.hpp>
namespace {
std::string::size_type find_string_in_string(const std::string &string, const std::string &substring)
{
std::string::size_type possible_match_index = string.find(substring.at(0));
while(possible_match_index != std::string::npos)
{
if(string.substr(possible_match_index, substring.size()) == substring)
{
return possible_match_index;
}
possible_match_index = string.find(substring.at(0), possible_match_index + 1);
}
return possible_match_index;
}
xlnt::datetime w3cdtf_to_datetime(const std::string &string)
{
xlnt::datetime result(1900, 1, 1);
auto separator_index = string.find('-');
result.year = std::stoi(string.substr(0, separator_index));
result.month = std::stoi(string.substr(separator_index + 1, string.find('-', separator_index + 1)));
separator_index = string.find('-', separator_index + 1);
result.day = std::stoi(string.substr(separator_index + 1, string.find('T', separator_index + 1)));
separator_index = string.find('T', separator_index + 1);
result.hour = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.minute = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.second = std::stoi(string.substr(separator_index + 1, string.find('Z', separator_index + 1)));
return result;
}
void read_worksheet_common(xlnt::worksheet ws, const pugi::xml_node &root_node, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
auto full_range = xlnt::range_reference(dimension);
auto sheet_data_node = root_node.child("sheetData");
auto merge_cells_node = root_node.child("mergeCells");
if(merge_cells_node != nullptr)
{
int count = merge_cells_node.attribute("count").as_int();
for(auto merge_cell_node : merge_cells_node.children("mergeCell"))
{
ws.merge_cells(merge_cell_node.attribute("ref").as_string());
count--;
}
if(count != 0)
{
throw std::runtime_error("mismatch between count and actual number of merged cells");
}
}
for(auto row_node : sheet_data_node.children("row"))
{
int row_index = row_node.attribute("r").as_int();
std::string span_string = row_node.attribute("spans").as_string();
auto colon_index = span_string.find(':');
column_t min_column = 0;
column_t max_column = 0;
if(colon_index != std::string::npos)
{
min_column = static_cast<column_t>(std::stoll(span_string.substr(0, colon_index)));
max_column = static_cast<column_t>(std::stoll(span_string.substr(colon_index + 1)));
}
else
{
min_column = static_cast<column_t>(full_range.get_top_left().get_column_index());
max_column = static_cast<column_t>(full_range.get_bottom_right().get_column_index());
}
for(column_t i = min_column; i <= max_column; i++)
{
std::string address = xlnt::cell_reference::column_string_from_index(i) + std::to_string(row_index);
auto cell_node = row_node.find_child_by_attribute("c", "r", address.c_str());
if(cell_node != nullptr)
{
bool has_value = cell_node.child("v") != nullptr;
std::string value_string = cell_node.child("v").text().as_string();
bool has_type = cell_node.attribute("t") != nullptr;
std::string type = cell_node.attribute("t").as_string();
bool has_style = cell_node.attribute("s") != nullptr;
std::string style = cell_node.attribute("s").as_string();
bool has_formula = cell_node.child("f") != nullptr;
bool shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && std::string(cell_node.child("f").attribute("t").as_string()) == "shared";
if(has_formula && !shared_formula && !ws.get_parent().get_data_only())
{
std::string formula = cell_node.child("f").text().as_string();
ws.get_cell(address).set_formula(formula);
}
if(has_type && type == "inlineStr") // inline string
{
std::string inline_string = cell_node.child("is").child("t").text().as_string();
ws.get_cell(address).set_value(inline_string);
}
else if(has_type && type == "s" && !has_formula) // shared string
{
auto shared_string_index = std::stoll(value_string);
auto shared_string = string_table.at(static_cast<std::size_t>(shared_string_index));
ws.get_cell(address).set_value(shared_string);
}
else if(has_type && type == "b") // boolean
{
ws.get_cell(address).set_value(value_string != "0");
}
else if(has_type && type == "str")
{
ws.get_cell(address).set_value(value_string);
}
else if(has_value)
{
try
{
ws.get_cell(address).set_value(std::stold(value_string));
}
catch(std::invalid_argument)
{
ws.get_cell(address).set_value(value_string);
}
}
if(has_style)
{
auto style_index = static_cast<std::size_t>(std::stoll(style));
if(number_format_ids.size() > style_index)
{
auto number_format_id = number_format_ids.at(style_index);
auto format = xlnt::number_format::lookup_format(number_format_id);
if(format == xlnt::number_format::format::unknown)
{
auto match = custom_number_formats.find(number_format_id);
if(match != custom_number_formats.end())
{
xlnt::number_format nf(match->second);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
else
{
xlnt::number_format nf(format);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
}
else
{
ws.get_cell(address).set_number_format(xlnt::number_format(xlnt::number_format::format::general));
}
}
}
}
auto auto_filter_node = root_node.child("autoFilter");
if(auto_filter_node != nullptr)
{
xlnt::range_reference ref(auto_filter_node.attribute("ref").as_string());
ws.auto_filter(ref);
}
}
}
namespace xlnt {
const std::string reader::CentralDirectorySignature = "\x50\x4b\x05\x06";
std::string reader::repair_central_directory(const std::string &original)
{
auto pos = find_string_in_string(original, CentralDirectorySignature);
if(pos != std::string::npos)
{
return original.substr(0, pos + 22);
}
return original;
}
std::vector<std::pair<std::string, std::string>> reader::read_sheets(zip_file &archive)
{
auto xml_source = archive.read("xl/workbook.xml");
pugi::xml_document doc;
doc.load(xml_source.c_str());
std::string ns;
for(auto child : doc.children())
{
std::string name = child.name();
if(name.find(':') != std::string::npos)
{
auto colon_index = name.find(':');
ns = name.substr(0, colon_index);
break;
}
}
auto with_ns = [&](const std::string &base) { return ns.empty() ? base : ns + ":" + base; };
auto root_node = doc.child(with_ns("workbook").c_str());
auto sheets_node = root_node.child(with_ns("sheets").c_str());
std::vector<std::pair<std::string, std::string>> sheets;
// store temp because pugixml iteration uses the internal char array multiple times
auto sheet_element_name = with_ns("sheet");
for(auto sheet_node : sheets_node.children(sheet_element_name.c_str()))
{
std::string id = sheet_node.attribute("r:id").as_string();
std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name));
}
return sheets;
}
document_properties reader::read_properties_core(const std::string &xml_string)
{
document_properties props;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("cp:coreProperties");
props.excel_base_date = calendar::windows_1900;
if(root_node.child("dc:creator") != nullptr)
{
props.creator = root_node.child("dc:creator").text().as_string();
}
if(root_node.child("cp:lastModifiedBy") != nullptr)
{
props.last_modified_by = root_node.child("cp:lastModifiedBy").text().as_string();
}
if(root_node.child("dcterms:created") != nullptr)
{
std::string created_string = root_node.child("dcterms:created").text().as_string();
props.created = w3cdtf_to_datetime(created_string);
}
if(root_node.child("dcterms:modified") != nullptr)
{
std::string modified_string = root_node.child("dcterms:modified").text().as_string();
props.modified = w3cdtf_to_datetime(modified_string);
}
return props;
}
std::string reader::read_dimension(const std::string &xml_string)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
return dimension;
}
std::vector<relationship> reader::read_relationships(zip_file &archive, const std::string &filename)
{
auto filename_separator_index = filename.find_last_of('/');
auto basename = filename.substr(filename_separator_index + 1);
auto dirname = filename.substr(0, filename_separator_index);
auto rels_filename = dirname + "/_rels/" + basename + ".rels";
pugi::xml_document doc;
auto content = archive.read(rels_filename);
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
std::vector<relationship> relationships;
for(auto relationship : root_node.children("Relationship"))
{
std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").as_string();
if(target[0] != '/' && target.substr(0, 2) != "..")
{
target = dirname + "/" + target;
}
if(target[0] == '/')
{
target = target.substr(1);
}
relationships.push_back(xlnt::relationship(type, id, target));
}
return relationships;
}
std::vector<std::pair<std::string, std::string>> reader::read_content_types(zip_file &archive)
{
pugi::xml_document doc;
try
{
auto content_types_string = archive.read("[Content_Types].xml");
doc.load(content_types_string.c_str());
}
catch(std::exception e)
{
throw invalid_file_exception(archive.get_filename());
}
auto root_node = doc.child("Types");
std::vector<std::pair<std::string, std::string>> override_types;
for(auto child : root_node.children("Override"))
{
std::string part_name = child.attribute("PartName").as_string();
std::string content_type = child.attribute("ContentType").as_string();
override_types.push_back({part_name, content_type});
}
return override_types;
}
std::string reader::determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types)
{
auto match = std::find_if(override_types.begin(), override_types.end(), [](const std::pair<std::string, std::string> &p) { return p.first == "/xl/workbook.xml"; });
if(match == override_types.end())
{
return "unsupported";
}
std::string type = match->second;
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
{
return "excel";
}
else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
{
return "powerpoint";
}
else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
{
return "word";
}
return "unsupported";
}
void reader::fast_parse(worksheet ws, std::istream &xml_source, const std::vector<std::string> &shared_string, const std::vector<style> &/*style_table*/, std::size_t /*color_index*/)
{
pugi::xml_document doc;
doc.load(xml_source);
read_worksheet_common(ws, doc.child("worksheet"), shared_string, {}, {});
}
void reader::read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
read_worksheet_common(ws, doc.child("worksheet"), string_table, number_format_ids, custom_number_formats);
}
worksheet xlnt::reader::read_worksheet(std::istream &handle, xlnt::workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats)
{
auto ws = wb.create_sheet();
ws.set_title(title);
pugi::xml_document doc;
doc.load(handle);
read_worksheet_common(ws, doc.child("worksheet"), string_table, {}, custom_number_formats);
return ws;
}
std::vector<std::string> reader::read_shared_string(const std::string &xml_string)
{
std::vector<std::string> shared_strings;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("sst");
//int count = root_node.attribute("count").as_int();
int unique_count = root_node.attribute("uniqueCount").as_int();
for(auto si_node : root_node)
{
shared_strings.push_back(si_node.child("t").text().as_string());
}
if(unique_count != (int)shared_strings.size())
{
throw std::runtime_error("counts don't match");
}
return shared_strings;
}
workbook reader::load_workbook(const std::string &filename, bool guess_types, bool data_only)
{
workbook wb;
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
wb.load(filename);
return wb;
}
std::vector<std::pair<std::string, std::string>> reader::detect_worksheets(zip_file &archive)
{
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
auto content_types = read_content_types(archive);
std::vector<std::string> valid_sheets;
for(const auto &content_type : content_types)
{
if(content_type.second == ValidWorksheet)
{
valid_sheets.push_back(content_type.first);
}
}
auto workbook_relationships = reader::read_relationships(archive, "xl/workbook.xml");
std::vector<std::pair<std::string, std::string>> result;
for(const auto &ws : read_sheets(archive))
{
auto rel = *std::find_if(workbook_relationships.begin(), workbook_relationships.end(), [&](const relationship &r) { return r.get_id() == ws.first; });
auto target = rel.get_target_uri();
if(std::find(valid_sheets.begin(), valid_sheets.end(), "/" + target) != valid_sheets.end())
{
result.push_back({target, ws.second});
}
}
return result;
}
} // namespace xlnt

View File

@ -0,0 +1,67 @@
#include <xlnt/reader/excel_reader.hpp>
#include <xlnt/workbook/workbook.hpp>
namespace {
std::string::size_type find_string_in_string(const std::string &string, const std::string &substring)
{
std::string::size_type possible_match_index = string.find(substring.at(0));
while(possible_match_index != std::string::npos)
{
if(string.substr(possible_match_index, substring.size()) == substring)
{
return possible_match_index;
}
possible_match_index = string.find(substring.at(0), possible_match_index + 1);
}
return possible_match_index;
}
} // namespace
namespace xlnt {
std::string CentralDirectorySignature()
{
return "\x50\x4b\x05\x06";
}
std::string repair_central_directory(const std::string &original)
{
auto pos = find_string_in_string(original, CentralDirectorySignature());
if(pos != std::string::npos)
{
return original.substr(0, pos + 22);
}
return original;
}
workbook load_workbook(const std::string &filename, bool guess_types, bool data_only)
{
workbook wb;
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
wb.load(filename);
return wb;
}
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes, bool guess_types, bool data_only)
{
xlnt::workbook wb;
wb.set_guess_types(guess_types);
wb.set_data_only(data_only);
wb.load(bytes);
return wb;
}
} // namespace xlnt

View File

@ -0,0 +1,29 @@
#include <xlnt/reader/shared_strings_reader.hpp>
#include "detail/include_pugixml.hpp"
namespace xlnt {
std::vector<std::string> read_shared_strings(const std::string &xml_string)
{
std::vector<std::string> shared_strings;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("sst");
//int count = root_node.attribute("count").as_int();
int unique_count = root_node.attribute("uniqueCount").as_int();
for(auto si_node : root_node)
{
shared_strings.push_back(si_node.child("t").text().as_string());
}
if(unique_count != (int)shared_strings.size())
{
throw std::runtime_error("counts don't match");
}
return shared_strings;
}
} // namespace xlnt

View File

@ -0,0 +1,238 @@
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/workbook/document_properties.hpp>
#include <xlnt/workbook/workbook.hpp>
#include "detail/include_pugixml.hpp"
namespace {
xlnt::datetime w3cdtf_to_datetime(const std::string &string)
{
xlnt::datetime result(1900, 1, 1);
auto separator_index = string.find('-');
result.year = std::stoi(string.substr(0, separator_index));
result.month = std::stoi(string.substr(separator_index + 1, string.find('-', separator_index + 1)));
separator_index = string.find('-', separator_index + 1);
result.day = std::stoi(string.substr(separator_index + 1, string.find('T', separator_index + 1)));
separator_index = string.find('T', separator_index + 1);
result.hour = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.minute = std::stoi(string.substr(separator_index + 1, string.find(':', separator_index + 1)));
separator_index = string.find(':', separator_index + 1);
result.second = std::stoi(string.substr(separator_index + 1, string.find('Z', separator_index + 1)));
return result;
}
}
namespace xlnt {
std::vector<std::pair<std::string, std::string>> read_sheets(zip_file &archive)
{
auto xml_source = archive.read("xl/workbook.xml");
pugi::xml_document doc;
doc.load(xml_source.c_str());
std::string ns;
for(auto child : doc.children())
{
std::string name = child.name();
if(name.find(':') != std::string::npos)
{
auto colon_index = name.find(':');
ns = name.substr(0, colon_index);
break;
}
}
auto with_ns = [&](const std::string &base) { return ns.empty() ? base : ns + ":" + base; };
auto root_node = doc.child(with_ns("workbook").c_str());
auto sheets_node = root_node.child(with_ns("sheets").c_str());
std::vector<std::pair<std::string, std::string>> sheets;
// store temp because pugixml iteration uses the internal char array multiple times
auto sheet_element_name = with_ns("sheet");
for(auto sheet_node : sheets_node.children(sheet_element_name.c_str()))
{
std::string id = sheet_node.attribute("r:id").as_string();
std::string name = sheet_node.attribute("name").as_string();
sheets.push_back(std::make_pair(id, name));
}
return sheets;
}
document_properties read_properties_core(const std::string &xml_string)
{
document_properties props;
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("cp:coreProperties");
props.excel_base_date = calendar::windows_1900;
if(root_node.child("dc:creator") != nullptr)
{
props.creator = root_node.child("dc:creator").text().as_string();
}
if(root_node.child("cp:lastModifiedBy") != nullptr)
{
props.last_modified_by = root_node.child("cp:lastModifiedBy").text().as_string();
}
if(root_node.child("dcterms:created") != nullptr)
{
std::string created_string = root_node.child("dcterms:created").text().as_string();
props.created = w3cdtf_to_datetime(created_string);
}
if(root_node.child("dcterms:modified") != nullptr)
{
std::string modified_string = root_node.child("dcterms:modified").text().as_string();
props.modified = w3cdtf_to_datetime(modified_string);
}
return props;
}
std::string read_dimension(const std::string &xml_string)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
auto root_node = doc.child("worksheet");
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
return dimension;
}
std::vector<relationship> read_relationships(zip_file &archive, const std::string &filename)
{
auto filename_separator_index = filename.find_last_of('/');
auto basename = filename.substr(filename_separator_index + 1);
auto dirname = filename.substr(0, filename_separator_index);
auto rels_filename = dirname + "/_rels/" + basename + ".rels";
pugi::xml_document doc;
auto content = archive.read(rels_filename);
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
std::vector<relationship> relationships;
for(auto relationship : root_node.children("Relationship"))
{
std::string id = relationship.attribute("Id").as_string();
std::string type = relationship.attribute("Type").as_string();
std::string target = relationship.attribute("Target").as_string();
if(target[0] != '/' && target.substr(0, 2) != "..")
{
target = dirname + "/" + target;
}
if(target[0] == '/')
{
target = target.substr(1);
}
relationships.push_back(xlnt::relationship(type, id, target));
}
return relationships;
}
std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive)
{
pugi::xml_document doc;
try
{
auto content_types_string = archive.read("[Content_Types].xml");
doc.load(content_types_string.c_str());
}
catch(std::exception e)
{
throw invalid_file_exception(archive.get_filename());
}
auto root_node = doc.child("Types");
std::vector<std::pair<std::string, std::string>> override_types;
for(auto child : root_node.children("Override"))
{
std::string part_name = child.attribute("PartName").as_string();
std::string content_type = child.attribute("ContentType").as_string();
override_types.push_back({part_name, content_type});
}
return override_types;
}
std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types)
{
auto match = std::find_if(override_types.begin(), override_types.end(), [](const std::pair<std::string, std::string> &p) { return p.first == "/xl/workbook.xml"; });
if(match == override_types.end())
{
return "unsupported";
}
std::string type = match->second;
if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
{
return "excel";
}
else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
{
return "powerpoint";
}
else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
{
return "word";
}
return "unsupported";
}
std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive)
{
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
auto content_types = read_content_types(archive);
std::vector<std::string> valid_sheets;
for(const auto &content_type : content_types)
{
if(content_type.second == ValidWorksheet)
{
valid_sheets.push_back(content_type.first);
}
}
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
std::vector<std::pair<std::string, std::string>> result;
for(const auto &ws : read_sheets(archive))
{
auto rel = *std::find_if(workbook_relationships.begin(), workbook_relationships.end(), [&](const relationship &r) { return r.get_id() == ws.first; });
auto target = rel.get_target_uri();
if(std::find(valid_sheets.begin(), valid_sheets.end(), "/" + target) != valid_sheets.end())
{
result.push_back({target, ws.second});
}
}
return result;
}
}

View File

@ -1,5 +1,5 @@
#include <xlnt/styles/borders.hpp>
#include <xlnt/styles/border.hpp>
namespace xlnt {

View File

@ -12,9 +12,11 @@
#include <xlnt/common/relationship.hpp>
#include <xlnt/common/zip_file.hpp>
#include <xlnt/drawing/drawing.hpp>
#include <xlnt/reader/reader.hpp>
#include <xlnt/reader/shared_strings_reader.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/borders.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>
@ -28,7 +30,6 @@
#include <xlnt/writer/manifest_writer.hpp>
#include <xlnt/writer/worksheet_writer.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include <xlnt/writer/writer.hpp>
#include "detail/cell_impl.hpp"
#include "detail/include_pugixml.hpp"
@ -318,8 +319,8 @@ bool workbook::load(const std::string &filename)
bool workbook::load(xlnt::zip_file &archive)
{
auto content_types = reader::read_content_types(archive);
auto type = reader::determine_document_type(content_types);
auto content_types = read_content_types(archive);
auto type = determine_document_type(content_types);
if(type != "excel")
{
@ -328,7 +329,7 @@ bool workbook::load(xlnt::zip_file &archive)
clear();
auto workbook_relationships = reader::read_relationships(archive, "xl/workbook.xml");
auto workbook_relationships = read_relationships(archive, "xl/workbook.xml");
for(auto relationship : workbook_relationships)
{
@ -349,7 +350,7 @@ bool workbook::load(xlnt::zip_file &archive)
if(archive.has_file("xl/sharedStrings.xml"))
{
shared_strings = xlnt::reader::read_shared_string(archive.read("xl/sharedStrings.xml"));
shared_strings = read_shared_strings(archive.read("xl/sharedStrings.xml"));
}
std::vector<int> number_format_ids;
@ -389,7 +390,7 @@ bool workbook::load(xlnt::zip_file &archive)
auto ws = create_sheet(sheet_node.attribute("name").as_string(), *rel);
auto sheet_filename = rel->get_target_uri();
xlnt::reader::read_worksheet(ws, archive.read(sheet_filename).c_str(), shared_strings, number_format_ids, custom_number_formats);
read_worksheet(ws, archive.read(sheet_filename).c_str(), shared_strings, number_format_ids, custom_number_formats);
}
return true;

View File

@ -1,22 +0,0 @@
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/workbook/workbook.hpp>
namespace xlnt {
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes)
{
xlnt::workbook wb;
wb.load(bytes);
return wb;
}
xlnt::workbook load_workbook(const std::string &filename)
{
xlnt::workbook wb;
wb.load(filename);
return wb;
}
}

View File

@ -0,0 +1,186 @@
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/workbook/workbook.hpp>
#include <xlnt/worksheet/range_reference.hpp>
#include <xlnt/worksheet/worksheet.hpp>
#include "detail/include_pugixml.hpp"
namespace {
void read_worksheet_common(xlnt::worksheet ws, const pugi::xml_node &root_node, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
auto dimension_node = root_node.child("dimension");
std::string dimension = dimension_node.attribute("ref").as_string();
auto full_range = xlnt::range_reference(dimension);
auto sheet_data_node = root_node.child("sheetData");
auto merge_cells_node = root_node.child("mergeCells");
if(merge_cells_node != nullptr)
{
int count = merge_cells_node.attribute("count").as_int();
for(auto merge_cell_node : merge_cells_node.children("mergeCell"))
{
ws.merge_cells(merge_cell_node.attribute("ref").as_string());
count--;
}
if(count != 0)
{
throw std::runtime_error("mismatch between count and actual number of merged cells");
}
}
for(auto row_node : sheet_data_node.children("row"))
{
int row_index = row_node.attribute("r").as_int();
std::string span_string = row_node.attribute("spans").as_string();
auto colon_index = span_string.find(':');
column_t min_column = 0;
column_t max_column = 0;
if(colon_index != std::string::npos)
{
min_column = static_cast<column_t>(std::stoll(span_string.substr(0, colon_index)));
max_column = static_cast<column_t>(std::stoll(span_string.substr(colon_index + 1)));
}
else
{
min_column = static_cast<column_t>(full_range.get_top_left().get_column_index());
max_column = static_cast<column_t>(full_range.get_bottom_right().get_column_index());
}
for(column_t i = min_column; i <= max_column; i++)
{
std::string address = xlnt::cell_reference::column_string_from_index(i) + std::to_string(row_index);
auto cell_node = row_node.find_child_by_attribute("c", "r", address.c_str());
if(cell_node != nullptr)
{
bool has_value = cell_node.child("v") != nullptr;
std::string value_string = cell_node.child("v").text().as_string();
bool has_type = cell_node.attribute("t") != nullptr;
std::string type = cell_node.attribute("t").as_string();
bool has_style = cell_node.attribute("s") != nullptr;
std::string style = cell_node.attribute("s").as_string();
bool has_formula = cell_node.child("f") != nullptr;
bool shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && std::string(cell_node.child("f").attribute("t").as_string()) == "shared";
if(has_formula && !shared_formula && !ws.get_parent().get_data_only())
{
std::string formula = cell_node.child("f").text().as_string();
ws.get_cell(address).set_formula(formula);
}
if(has_type && type == "inlineStr") // inline string
{
std::string inline_string = cell_node.child("is").child("t").text().as_string();
ws.get_cell(address).set_value(inline_string);
}
else if(has_type && type == "s" && !has_formula) // shared string
{
auto shared_string_index = std::stoll(value_string);
auto shared_string = string_table.at(static_cast<std::size_t>(shared_string_index));
ws.get_cell(address).set_value(shared_string);
}
else if(has_type && type == "b") // boolean
{
ws.get_cell(address).set_value(value_string != "0");
}
else if(has_type && type == "str")
{
ws.get_cell(address).set_value(value_string);
}
else if(has_value)
{
try
{
ws.get_cell(address).set_value(std::stold(value_string));
}
catch(std::invalid_argument)
{
ws.get_cell(address).set_value(value_string);
}
}
if(has_style)
{
auto style_index = static_cast<std::size_t>(std::stoll(style));
if(number_format_ids.size() > style_index)
{
auto number_format_id = number_format_ids.at(style_index);
auto format = xlnt::number_format::lookup_format(number_format_id);
if(format == xlnt::number_format::format::unknown)
{
auto match = custom_number_formats.find(number_format_id);
if(match != custom_number_formats.end())
{
xlnt::number_format nf(match->second);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
else
{
xlnt::number_format nf(format);
auto cell = ws.get_cell(address);
cell.set_number_format(nf);
}
}
}
else
{
ws.get_cell(address).set_number_format(xlnt::number_format(xlnt::number_format::format::general));
}
}
}
}
auto auto_filter_node = root_node.child("autoFilter");
if(auto_filter_node != nullptr)
{
xlnt::range_reference ref(auto_filter_node.attribute("ref").as_string());
ws.auto_filter(ref);
}
}
} // namespace
namespace xlnt {
void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
pugi::xml_document doc;
doc.load(xml_string.c_str());
read_worksheet_common(ws, doc.child("worksheet"), string_table, number_format_ids, custom_number_formats);
}
void read_worksheet(worksheet ws, std::istream &stream, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids, const std::unordered_map<int, std::string> &custom_number_formats)
{
pugi::xml_document doc;
doc.load(stream);
read_worksheet_common(ws, doc.child("worksheet"), string_table, number_format_ids, custom_number_formats);
}
worksheet read_worksheet(std::istream &handle, xlnt::workbook &wb, const std::string &title, const std::vector<std::string> &string_table, const std::unordered_map<int, std::string> &custom_number_formats)
{
auto ws = wb.create_sheet();
ws.set_title(title);
pugi::xml_document doc;
doc.load(handle);
read_worksheet_common(ws, doc.child("worksheet"), string_table, {}, custom_number_formats);
return ws;
}
} // namespace xlnt

View File

@ -2,7 +2,7 @@
#include <pugixml.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/borders.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>
@ -53,6 +53,10 @@ std::string style_writer::write_table() const
auto fonts_node = style_sheet_node.append_child("fonts");
auto fonts = wb_.get_fonts();
if(fonts.empty())
{
fonts.push_back(font());
}
fonts_node.append_attribute("count").set_value(static_cast<int>(fonts.size()));
fonts_node.append_attribute("x14ac:knownFonts").set_value(1);
@ -103,10 +107,19 @@ std::string style_writer::write_table() const
const auto &styles = wb_.get_styles();
cell_xfs_node.append_attribute("count").set_value(static_cast<int>(styles.size()));
custom_index = 164;
for(auto &style : styles)
{
xf_node = cell_xfs_node.append_child("xf");
xf_node.append_attribute("numFmtId").set_value((int)style.get_number_format_index());
if(style.get_number_format().get_format_code() == number_format::format::unknown)
{
xf_node.append_attribute("numFmtId").set_value(custom_index++);
}
else
{
xf_node.append_attribute("numFmtId").set_value(style.get_number_format().get_format_index());
}
xf_node.append_attribute("applyNumberFormat").set_value(1);
xf_node.append_attribute("fontId").set_value((int)style.get_font_index());
xf_node.append_attribute("fillId").set_value((int)style.get_fill_index());

View File

@ -4,7 +4,7 @@
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/cell_reference.hpp>
#include <xlnt/styles/borders.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/number_format.hpp>

View File

@ -11,9 +11,10 @@
#include <xlnt/common/exceptions.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/styles/alignment.hpp>
#include <xlnt/styles/borders.hpp>
#include <xlnt/styles/border.hpp>
#include <xlnt/styles/font.hpp>
#include <xlnt/styles/fill.hpp>
#include <xlnt/styles/pattern_fill.hpp>
#include <xlnt/styles/number_format.hpp>
#include <xlnt/styles/protection.hpp>
#include <xlnt/worksheet/range.hpp>
@ -31,24 +32,6 @@ public:
wb_guess_types.set_guess_types(true);
}
void test_debug()
{
xlnt::workbook wb = xlnt::load_workbook("/Users/thomas/Development/xlnt/samples/sample1.xlsx");
for(auto ws : wb)
{
for(auto row : ws.rows())
{
for(auto cell : row)
{
std::cout << cell << ", ";
}
std::cout << std::endl;
}
}
wb.save("book.xlsx");
}
void test_infer_numeric()
{
auto ws = wb_guess_types.create_sheet();

View File

@ -3,6 +3,7 @@
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/writer/workbook_writer.hpp>
#include "helpers/path_helper.hpp"
@ -15,7 +16,7 @@ public:
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx");
auto content = archive.read("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
auto prop = xlnt::read_properties_core(content);
TS_ASSERT_EQUALS(prop.creator, "*.*");
TS_ASSERT_EQUALS(prop.last_modified_by, "Charlie Clark");
TS_ASSERT_EQUALS(prop.created, xlnt::datetime(2010, 4, 9, 20, 43, 12));
@ -30,7 +31,7 @@ public:
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
std::size_t i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))
for(auto sheet : xlnt::read_sheets(archive))
{
TS_ASSERT_EQUALS(sheet.second, expected_titles.at(i++));
}
@ -40,7 +41,7 @@ public:
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx");
auto content = archive.read("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
auto prop = xlnt::read_properties_core(content);
TS_ASSERT_EQUALS(prop.excel_base_date, xlnt::calendar::windows_1900);
}
@ -52,7 +53,7 @@ public:
const std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
std::size_t i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))
for(auto sheet : xlnt::read_sheets(archive))
{
TS_ASSERT_EQUALS(sheet.second, expected_titles.at(i++));
}

View File

@ -2,9 +2,13 @@
#include <fstream>
#include <iostream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include <xlnt/reader/excel_reader.hpp>
#include <xlnt/reader/workbook_reader.hpp>
#include <xlnt/reader/worksheet_reader.hpp>
#include "helpers/path_helper.hpp"
class test_read : public CxxTest::TestSuite
@ -17,7 +21,7 @@ public:
xlnt::worksheet ws(wb);
{
std::ifstream handle(path);
ws = xlnt::reader::read_worksheet(handle, wb, "Sheet 2", {"hello"}, {});
ws = xlnt::read_worksheet(handle, wb, "Sheet 2", {"hello"}, {});
}
TS_ASSERT_DIFFERS(ws, nullptr);
if(!(ws == nullptr))
@ -31,7 +35,7 @@ public:
xlnt::workbook standard_workbook()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty.xlsx");
return xlnt::reader::load_workbook(path);
return xlnt::load_workbook(path);
}
void test_read_standard_workbook()
@ -43,7 +47,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/genuine/empty.xlsx");
std::ifstream fo(path, std::ios::binary);
auto wb = xlnt::reader::load_workbook(path);
auto wb = xlnt::load_workbook(path);
TS_ASSERT_DIFFERS(standard_workbook(), nullptr);
}
@ -61,32 +65,32 @@ public:
void test_read_nostring_workbook()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty-no-string.xlsx");
auto wb = xlnt::reader::load_workbook(path);
auto wb = xlnt::load_workbook(path);
TS_ASSERT_DIFFERS(standard_workbook(), nullptr);
}
void test_read_empty_file()
{
auto path = PathHelper::GetDataDirectory("/reader/null_file.xlsx");
TS_ASSERT_THROWS(xlnt::reader::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
}
void test_read_empty_archive()
{
auto path = PathHelper::GetDataDirectory("/reader/null_archive.xlsx");
TS_ASSERT_THROWS(xlnt::reader::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
}
void test_read_workbook_with_no_properties()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty_with_no_properties.xlsx");
xlnt::reader::load_workbook(path);
xlnt::load_workbook(path);
}
xlnt::workbook workbook_with_styles()
{
auto path = PathHelper::GetDataDirectory("/genuine/empty-with-styles.xlsx");
return xlnt::reader::load_workbook(path);
return xlnt::load_workbook(path);
}
void test_read_workbook_with_styles_general()
@ -137,13 +141,13 @@ public:
xlnt::workbook date_mac_1904()
{
auto path = PathHelper::GetDataDirectory("/reader/date_1904.xlsx");
return xlnt::reader::load_workbook(path);
return xlnt::load_workbook(path);
}
xlnt::workbook date_std_1900()
{
auto path = PathHelper::GetDataDirectory("/reader/date_1900.xlsx");
return xlnt::reader::load_workbook(path);
return xlnt::load_workbook(path);
}
void test_read_win_base_date()
@ -186,20 +190,20 @@ public:
void test_repair_central_directory()
{
std::string data_a = "foobarbaz" + xlnt::reader::CentralDirectorySignature;
std::string data_a = "foobarbaz" + xlnt::CentralDirectorySignature();
std::string data_b = "bazbarfoo12345678901234567890";
auto f = xlnt::reader::repair_central_directory(data_a + data_b);
auto f = xlnt::repair_central_directory(data_a + data_b);
TS_ASSERT_EQUALS(f, data_a + data_b.substr(0, 18));
f = xlnt::reader::repair_central_directory(data_b);
f = xlnt::repair_central_directory(data_b);
TS_ASSERT_EQUALS(f, data_b);
}
void test_read_no_theme()
{
auto path = PathHelper::GetDataDirectory("/genuine/libreoffice_nrt.xlsx");
auto wb = xlnt::reader::load_workbook(path);
auto wb = xlnt::load_workbook(path);
TS_ASSERT_DIFFERS(wb, nullptr);
}
@ -209,7 +213,7 @@ public:
auto ws = wb.get_active_sheet();
auto path = PathHelper::GetDataDirectory("/reader/worksheet_formula.xml");
std::ifstream ws_stream(path);
xlnt::reader::fast_parse(ws, ws_stream, {"", ""}, {}, 0);
xlnt::read_worksheet(ws, ws_stream, { "string", "string2" }, {}, {});
auto b1 = ws.get_cell("B1");
TS_ASSERT(b1.has_formula());
@ -280,7 +284,7 @@ public:
void test_data_only()
{
auto path = PathHelper::GetDataDirectory("/reader/formulae.xlsx");
auto wb = xlnt::reader::load_workbook(path, false, true);
auto wb = xlnt::load_workbook(path, false, true);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_formula_attributes().empty());
@ -308,7 +312,7 @@ public:
{
{"xl/worksheets/sheet1.xml", "Sheet1"}
};
TS_ASSERT_EQUALS(xlnt::reader::detect_worksheets(archive), expected);
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected);
}
{
@ -321,7 +325,7 @@ public:
{"xl/worksheets/sheet2.xml", "moredata"}
};
TS_ASSERT_EQUALS(xlnt::reader::detect_worksheets(archive), expected);
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected);
}
{
@ -335,7 +339,7 @@ public:
{"xl/worksheets/sheet.xml", "Sheet3"}
};
TS_ASSERT_EQUALS(xlnt::reader::detect_worksheets(archive), expected);
TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected);
}
}
@ -354,7 +358,7 @@ public:
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::reader::read_relationships(archive, "xl/workbook.xml"), expected);
TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected);
}
{
@ -373,7 +377,7 @@ public:
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::reader::read_relationships(archive, "xl/workbook.xml"), expected);
TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected);
}
}
@ -399,7 +403,7 @@ public:
auto path = PathHelper::GetDataDirectory("/reader/contains_chartsheets.xlsx");
xlnt::zip_file f(path);
auto result = xlnt::reader::read_content_types(f);
auto result = xlnt::read_content_types(f);
if(result.size() != expected.size())
{
@ -418,7 +422,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file f(path);
auto sheets = xlnt::reader::read_sheets(f);
auto sheets = xlnt::read_sheets(f);
std::vector<std::pair<std::string, std::string>> expected =
{{"rId1", "Chart1"}, {"rId2", "Sheet1"}};
TS_ASSERT_EQUALS(sheets, expected);
@ -427,7 +431,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file f(path);
auto sheets = xlnt::reader::read_sheets(f);
auto sheets = xlnt::read_sheets(f);
std::vector<std::pair<std::string, std::string>> expected =
{{"rId1", "Sheet1"}, {"rId2", "Sheet2"}, {"rId3", "Sheet3"}};
TS_ASSERT_EQUALS(sheets, expected);
@ -444,7 +448,7 @@ public:
{
std::tie(guess, dtype) = expected;
auto path = PathHelper::GetDataDirectory("/genuine/guess_types.xlsx");
auto wb = xlnt::reader::load_workbook(path, guess);
auto wb = xlnt::load_workbook(path, guess);
auto ws = wb.get_active_sheet();
TS_ASSERT(ws.get_cell("D2").get_data_type() == dtype);
}
@ -453,7 +457,7 @@ public:
void test_read_autofilter()
{
auto path = PathHelper::GetDataDirectory("/reader/bug275.xlsx");
auto wb = xlnt::reader::load_workbook(path);
auto wb = xlnt::load_workbook(path);
auto ws = wb.get_active_sheet();
TS_ASSERT_EQUALS(ws.get_auto_filter().to_string(), "A1:B6");
}
@ -461,18 +465,18 @@ public:
void test_bad_formats_xlsb()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.xlsb");
TS_ASSERT_THROWS(xlnt::reader::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
}
void test_bad_formats_xls()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.xls");
TS_ASSERT_THROWS(xlnt::reader::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
}
void test_bad_formats_no()
{
auto path = PathHelper::GetDataDirectory("/genuine/a.no-format");
TS_ASSERT_THROWS(xlnt::reader::load_workbook(path), xlnt::invalid_file_exception);
TS_ASSERT_THROWS(xlnt::load_workbook(path), xlnt::invalid_file_exception);
}
};