mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
add failing tests and begin restructuring worksheet reading [ci skip]
This commit is contained in:
parent
2305eae8b6
commit
d7e7526beb
|
@ -58,12 +58,16 @@ public:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
bool has_cell();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the next cell in the current worksheet and optionally returns it if
|
/// Reads the next cell in the current worksheet and optionally returns it if
|
||||||
/// the last cell in the sheet has not yet been read.
|
/// the last cell in the sheet has not yet been read.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
cell read_cell();
|
cell read_cell();
|
||||||
|
|
||||||
|
bool has_worksheet();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Beings reading of the next worksheet in the workbook and optionally
|
/// Beings reading of the next worksheet in the workbook and optionally
|
||||||
/// returns its title if the last worksheet has not yet been read.
|
/// returns its title if the last worksheet has not yet been read.
|
||||||
|
|
|
@ -129,7 +129,8 @@ namespace detail {
|
||||||
|
|
||||||
xlsx_consumer::xlsx_consumer(workbook &target)
|
xlsx_consumer::xlsx_consumer(workbook &target)
|
||||||
: target_(target),
|
: target_(target),
|
||||||
parser_(nullptr)
|
parser_(nullptr),
|
||||||
|
stream_cell_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +151,252 @@ cell xlsx_consumer::read_cell()
|
||||||
return cell(nullptr);
|
return cell(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string xlsx_consumer::begin_worksheet()
|
std::string xlsx_consumer::read_worksheet_begin()
|
||||||
{
|
{
|
||||||
return "";
|
if (worksheet_queue_.empty())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto back = worksheet_queue_.back();
|
||||||
|
const auto rel_id = back.id();
|
||||||
|
worksheet_queue_.pop_back();
|
||||||
|
|
||||||
|
auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
|
||||||
|
target_.d_->sheet_title_rel_id_map_.end(),
|
||||||
|
[&](const std::pair<std::string, std::string> &p) {
|
||||||
|
return p.second == rel_id;
|
||||||
|
})->first;
|
||||||
|
|
||||||
|
auto id = sheet_title_id_map_[title];
|
||||||
|
auto index = sheet_title_index_map_[title];
|
||||||
|
|
||||||
|
auto insertion_iter = target_.d_->worksheets_.begin();
|
||||||
|
while (insertion_iter != target_.d_->worksheets_.end() && sheet_title_index_map_[insertion_iter->title_] < index)
|
||||||
|
{
|
||||||
|
++insertion_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title);
|
||||||
|
|
||||||
|
auto ws = target_.sheet_by_id(id);
|
||||||
|
|
||||||
|
expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet
|
||||||
|
skip_attributes({ qn("mc", "Ignorable") });
|
||||||
|
read_namespaces();
|
||||||
|
|
||||||
|
xlnt::range_reference full_range;
|
||||||
|
auto &manifest = target_.manifest();
|
||||||
|
|
||||||
|
const auto workbook_rel = manifest.relationship(path("/"), relationship_type::office_document);
|
||||||
|
const auto sheet_rel = manifest.relationship(workbook_rel.target().path(), rel_id);
|
||||||
|
path sheet_path(sheet_rel.source().path().parent().append(sheet_rel.target().path()));
|
||||||
|
auto hyperlinks = manifest.relationships(sheet_path, xlnt::relationship_type::hyperlink);
|
||||||
|
|
||||||
|
while (in_element(qn("spreadsheetml", "worksheet")))
|
||||||
|
{
|
||||||
|
auto current_worksheet_element = expect_start_element(xml::content::complex);
|
||||||
|
|
||||||
|
if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1
|
||||||
|
{
|
||||||
|
while (in_element(current_worksheet_element))
|
||||||
|
{
|
||||||
|
auto sheet_pr_child_element = expect_start_element(xml::content::simple);
|
||||||
|
|
||||||
|
if (sheet_pr_child_element == qn("spreadsheetml", "tabColor")) // CT_Color 0-1
|
||||||
|
{
|
||||||
|
read_color();
|
||||||
|
}
|
||||||
|
else if (sheet_pr_child_element == qn("spreadsheetml", "outlinePr")) // CT_OutlinePr 0-1
|
||||||
|
{
|
||||||
|
skip_attribute("applyStyles"); // optional, boolean, false
|
||||||
|
skip_attribute("summaryBelow"); // optional, boolean, true
|
||||||
|
skip_attribute("summaryRight"); // optional, boolean, true
|
||||||
|
skip_attribute("showOutlineSymbols"); // optional, boolean, true
|
||||||
|
}
|
||||||
|
else if (sheet_pr_child_element == qn("spreadsheetml", "pageSetUpPr")) // CT_PageSetUpPr 0-1
|
||||||
|
{
|
||||||
|
skip_attribute("autoPageBreaks"); // optional, boolean, true
|
||||||
|
skip_attribute("fitToPage"); // optional, boolean, false
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unexpected_element(sheet_pr_child_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_end_element(sheet_pr_child_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_attribute("syncHorizontal"); // optional, boolean, false
|
||||||
|
skip_attribute("syncVertical"); // optional, boolean, false
|
||||||
|
skip_attribute("syncRef"); // optional, ST_Ref, false
|
||||||
|
skip_attribute("transitionEvaluation"); // optional, boolean, false
|
||||||
|
skip_attribute("transitionEntry"); // optional, boolean, false
|
||||||
|
skip_attribute("published"); // optional, boolean, true
|
||||||
|
skip_attribute("codeName"); // optional, string
|
||||||
|
skip_attribute("filterMode"); // optional, boolean, false
|
||||||
|
skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true
|
||||||
|
}
|
||||||
|
else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1
|
||||||
|
{
|
||||||
|
full_range = xlnt::range_reference(parser().attribute("ref"));
|
||||||
|
}
|
||||||
|
else if (current_worksheet_element == qn("spreadsheetml", "sheetViews")) // CT_SheetViews 0-1
|
||||||
|
{
|
||||||
|
while (in_element(current_worksheet_element))
|
||||||
|
{
|
||||||
|
expect_start_element(qn("spreadsheetml", "sheetView"), xml::content::complex); // CT_SheetView 1+
|
||||||
|
|
||||||
|
sheet_view new_view;
|
||||||
|
new_view.id(parser().attribute<std::size_t>("workbookViewId"));
|
||||||
|
|
||||||
|
if (parser().attribute_present("showGridLines")) // default="true"
|
||||||
|
{
|
||||||
|
new_view.show_grid_lines(is_true(parser().attribute("showGridLines")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("defaultGridColor")) // default="true"
|
||||||
|
{
|
||||||
|
new_view.default_grid_color(is_true(parser().attribute("defaultGridColor")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("view") && parser().attribute("view") != "normal")
|
||||||
|
{
|
||||||
|
new_view.type(parser().attribute("view") == "pageBreakPreview" ? sheet_view_type::page_break_preview
|
||||||
|
: sheet_view_type::page_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_attributes({ "windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft",
|
||||||
|
"tabSelected", "showRuler", "showOutlineSymbols", "showWhiteSpace", "view", "topLeftCell",
|
||||||
|
"colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", "zoomScalePageLayoutView" });
|
||||||
|
|
||||||
|
while (in_element(qn("spreadsheetml", "sheetView")))
|
||||||
|
{
|
||||||
|
auto sheet_view_child_element = expect_start_element(xml::content::simple);
|
||||||
|
|
||||||
|
if (sheet_view_child_element == qn("spreadsheetml", "pane")) // CT_Pane 0-1
|
||||||
|
{
|
||||||
|
pane new_pane;
|
||||||
|
|
||||||
|
if (parser().attribute_present("topLeftCell"))
|
||||||
|
{
|
||||||
|
new_pane.top_left_cell = cell_reference(parser().attribute("topLeftCell"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("xSplit"))
|
||||||
|
{
|
||||||
|
new_pane.x_split = parser().attribute<column_t::index_t>("xSplit");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("ySplit"))
|
||||||
|
{
|
||||||
|
new_pane.y_split = parser().attribute<row_t>("ySplit");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("activePane"))
|
||||||
|
{
|
||||||
|
new_pane.active_pane = parser().attribute<pane_corner>("activePane");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser().attribute_present("state"))
|
||||||
|
{
|
||||||
|
new_pane.state = parser().attribute<pane_state>("state");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_view.pane(new_pane);
|
||||||
|
}
|
||||||
|
else if (sheet_view_child_element == qn("spreadsheetml", "selection")) // CT_Selection 0-4
|
||||||
|
{
|
||||||
|
skip_remaining_content(sheet_view_child_element);
|
||||||
|
}
|
||||||
|
else if (sheet_view_child_element == qn("spreadsheetml", "pivotSelection")) // CT_PivotSelection 0-4
|
||||||
|
{
|
||||||
|
skip_remaining_content(sheet_view_child_element);
|
||||||
|
}
|
||||||
|
else if (sheet_view_child_element == qn("spreadsheetml", "extLst")) // CT_ExtensionList 0-1
|
||||||
|
{
|
||||||
|
skip_remaining_content(sheet_view_child_element);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unexpected_element(sheet_view_child_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_end_element(sheet_view_child_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_end_element(qn("spreadsheetml", "sheetView"));
|
||||||
|
|
||||||
|
ws.d_->views_.push_back(new_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1
|
||||||
|
{
|
||||||
|
skip_remaining_content(current_worksheet_element);
|
||||||
|
}
|
||||||
|
else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+
|
||||||
|
{
|
||||||
|
while (in_element(qn("spreadsheetml", "cols")))
|
||||||
|
{
|
||||||
|
expect_start_element(qn("spreadsheetml", "col"), xml::content::simple);
|
||||||
|
|
||||||
|
skip_attributes({ "bestFit", "collapsed", "outlineLevel" });
|
||||||
|
|
||||||
|
auto min = static_cast<column_t::index_t>(std::stoull(parser().attribute("min")));
|
||||||
|
auto max = static_cast<column_t::index_t>(std::stoull(parser().attribute("max")));
|
||||||
|
|
||||||
|
optional<double> width;
|
||||||
|
|
||||||
|
if (parser().attribute_present("width"))
|
||||||
|
{
|
||||||
|
width = parser().attribute<double>("width");
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<std::size_t> column_style;
|
||||||
|
|
||||||
|
if (parser().attribute_present("style"))
|
||||||
|
{
|
||||||
|
column_style = parser().attribute<std::size_t>("style");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto custom =
|
||||||
|
parser().attribute_present("customWidth") ? is_true(parser().attribute("customWidth")) : false;
|
||||||
|
auto hidden = parser().attribute_present("hidden") ? is_true(parser().attribute("hidden")) : false;
|
||||||
|
|
||||||
|
expect_end_element(qn("spreadsheetml", "col"));
|
||||||
|
|
||||||
|
for (auto column = min; column <= max; column++)
|
||||||
|
{
|
||||||
|
column_properties props;
|
||||||
|
|
||||||
|
if (width.is_set())
|
||||||
|
{
|
||||||
|
props.width = width.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column_style.is_set())
|
||||||
|
{
|
||||||
|
props.style = column_style.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
props.hidden = hidden;
|
||||||
|
props.custom_width = custom;
|
||||||
|
ws.add_column_properties(column, props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1
|
||||||
|
{
|
||||||
|
in_sheet_data_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_sheet_data_)
|
||||||
|
{
|
||||||
|
read_worksheet_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
worksheet xlsx_consumer::end_worksheet()
|
worksheet xlsx_consumer::end_worksheet()
|
||||||
|
@ -165,6 +409,16 @@ xml::parser &xlsx_consumer::parser()
|
||||||
return *parser_;
|
return *parser_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool xlsx_consumer::has_cell()
|
||||||
|
{
|
||||||
|
return stream_cell_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xlsx_consumer::has_worksheet()
|
||||||
|
{
|
||||||
|
return !worksheet_queue_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<relationship> xlsx_consumer::read_relationships(const path &part)
|
std::vector<relationship> xlsx_consumer::read_relationships(const path &part)
|
||||||
{
|
{
|
||||||
const auto part_rels_path = part.parent().append("_rels")
|
const auto part_rels_path = part.parent().append("_rels")
|
||||||
|
@ -1434,238 +1688,17 @@ void xlsx_consumer::read_volatile_dependencies()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CT_Worksheet
|
// CT_Worksheet
|
||||||
void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
void xlsx_consumer::read_worksheet_end()
|
||||||
{
|
{
|
||||||
/*
|
in_sheet_data_ = false;
|
||||||
static const auto &xmlns = constants::namespace_("spreadsheetml");
|
auto ws = worksheet(stream_worksheet_);
|
||||||
static const auto &xmlns_mc = constants::namespace_("mc");
|
std::vector<relationship> hyperlinks;
|
||||||
static const auto &xmlns_x14ac = constants::namespace_("x14ac");
|
|
||||||
static const auto &xmlns_r = constants::namespace_("r");
|
|
||||||
*/
|
|
||||||
auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
|
|
||||||
target_.d_->sheet_title_rel_id_map_.end(),
|
|
||||||
[&](const std::pair<std::string, std::string> &p) {
|
|
||||||
return p.second == rel_id;
|
|
||||||
})->first;
|
|
||||||
|
|
||||||
auto id = sheet_title_id_map_[title];
|
|
||||||
auto index = sheet_title_index_map_[title];
|
|
||||||
|
|
||||||
auto insertion_iter = target_.d_->worksheets_.begin();
|
|
||||||
while (insertion_iter != target_.d_->worksheets_.end() && sheet_title_index_map_[insertion_iter->title_] < index)
|
|
||||||
{
|
|
||||||
++insertion_iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title);
|
|
||||||
|
|
||||||
auto ws = target_.sheet_by_id(id);
|
|
||||||
|
|
||||||
expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet
|
|
||||||
skip_attributes({qn("mc", "Ignorable")});
|
|
||||||
read_namespaces();
|
|
||||||
|
|
||||||
xlnt::range_reference full_range;
|
|
||||||
auto &manifest = target_.manifest();
|
|
||||||
|
|
||||||
const auto workbook_rel = manifest.relationship(path("/"), relationship_type::office_document);
|
|
||||||
const auto sheet_rel = manifest.relationship(workbook_rel.target().path(), rel_id);
|
|
||||||
path sheet_path(sheet_rel.source().path().parent().append(sheet_rel.target().path()));
|
|
||||||
auto hyperlinks = manifest.relationships(sheet_path, xlnt::relationship_type::hyperlink);
|
|
||||||
|
|
||||||
while (in_element(qn("spreadsheetml", "worksheet")))
|
while (in_element(qn("spreadsheetml", "worksheet")))
|
||||||
{
|
{
|
||||||
auto current_worksheet_element = expect_start_element(xml::content::complex);
|
auto current_worksheet_element = expect_start_element(xml::content::complex);
|
||||||
|
|
||||||
if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1
|
if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1
|
||||||
{
|
|
||||||
while (in_element(current_worksheet_element))
|
|
||||||
{
|
|
||||||
auto sheet_pr_child_element = expect_start_element(xml::content::simple);
|
|
||||||
|
|
||||||
if (sheet_pr_child_element == qn("spreadsheetml", "tabColor")) // CT_Color 0-1
|
|
||||||
{
|
|
||||||
read_color();
|
|
||||||
}
|
|
||||||
else if (sheet_pr_child_element == qn("spreadsheetml", "outlinePr")) // CT_OutlinePr 0-1
|
|
||||||
{
|
|
||||||
skip_attribute("applyStyles"); // optional, boolean, false
|
|
||||||
skip_attribute("summaryBelow"); // optional, boolean, true
|
|
||||||
skip_attribute("summaryRight"); // optional, boolean, true
|
|
||||||
skip_attribute("showOutlineSymbols"); // optional, boolean, true
|
|
||||||
}
|
|
||||||
else if (sheet_pr_child_element == qn("spreadsheetml", "pageSetUpPr")) // CT_PageSetUpPr 0-1
|
|
||||||
{
|
|
||||||
skip_attribute("autoPageBreaks"); // optional, boolean, true
|
|
||||||
skip_attribute("fitToPage"); // optional, boolean, false
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unexpected_element(sheet_pr_child_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect_end_element(sheet_pr_child_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_attribute("syncHorizontal"); // optional, boolean, false
|
|
||||||
skip_attribute("syncVertical"); // optional, boolean, false
|
|
||||||
skip_attribute("syncRef"); // optional, ST_Ref, false
|
|
||||||
skip_attribute("transitionEvaluation"); // optional, boolean, false
|
|
||||||
skip_attribute("transitionEntry"); // optional, boolean, false
|
|
||||||
skip_attribute("published"); // optional, boolean, true
|
|
||||||
skip_attribute("codeName"); // optional, string
|
|
||||||
skip_attribute("filterMode"); // optional, boolean, false
|
|
||||||
skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true
|
|
||||||
}
|
|
||||||
else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1
|
|
||||||
{
|
|
||||||
full_range = xlnt::range_reference(parser().attribute("ref"));
|
|
||||||
}
|
|
||||||
else if (current_worksheet_element == qn("spreadsheetml", "sheetViews")) // CT_SheetViews 0-1
|
|
||||||
{
|
|
||||||
while (in_element(current_worksheet_element))
|
|
||||||
{
|
|
||||||
expect_start_element(qn("spreadsheetml", "sheetView"), xml::content::complex); // CT_SheetView 1+
|
|
||||||
|
|
||||||
sheet_view new_view;
|
|
||||||
new_view.id(parser().attribute<std::size_t>("workbookViewId"));
|
|
||||||
|
|
||||||
if (parser().attribute_present("showGridLines")) // default="true"
|
|
||||||
{
|
|
||||||
new_view.show_grid_lines(is_true(parser().attribute("showGridLines")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("defaultGridColor")) // default="true"
|
|
||||||
{
|
|
||||||
new_view.default_grid_color(is_true(parser().attribute("defaultGridColor")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("view") && parser().attribute("view") != "normal")
|
|
||||||
{
|
|
||||||
new_view.type(parser().attribute("view") == "pageBreakPreview" ? sheet_view_type::page_break_preview
|
|
||||||
: sheet_view_type::page_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_attributes({"windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft",
|
|
||||||
"tabSelected", "showRuler", "showOutlineSymbols", "showWhiteSpace", "view", "topLeftCell",
|
|
||||||
"colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", "zoomScalePageLayoutView"});
|
|
||||||
|
|
||||||
while (in_element(qn("spreadsheetml", "sheetView")))
|
|
||||||
{
|
|
||||||
auto sheet_view_child_element = expect_start_element(xml::content::simple);
|
|
||||||
|
|
||||||
if (sheet_view_child_element == qn("spreadsheetml", "pane")) // CT_Pane 0-1
|
|
||||||
{
|
|
||||||
pane new_pane;
|
|
||||||
|
|
||||||
if (parser().attribute_present("topLeftCell"))
|
|
||||||
{
|
|
||||||
new_pane.top_left_cell = cell_reference(parser().attribute("topLeftCell"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("xSplit"))
|
|
||||||
{
|
|
||||||
new_pane.x_split = parser().attribute<column_t::index_t>("xSplit");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("ySplit"))
|
|
||||||
{
|
|
||||||
new_pane.y_split = parser().attribute<row_t>("ySplit");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("activePane"))
|
|
||||||
{
|
|
||||||
new_pane.active_pane = parser().attribute<pane_corner>("activePane");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser().attribute_present("state"))
|
|
||||||
{
|
|
||||||
new_pane.state = parser().attribute<pane_state>("state");
|
|
||||||
}
|
|
||||||
|
|
||||||
new_view.pane(new_pane);
|
|
||||||
}
|
|
||||||
else if (sheet_view_child_element == qn("spreadsheetml", "selection")) // CT_Selection 0-4
|
|
||||||
{
|
|
||||||
skip_remaining_content(sheet_view_child_element);
|
|
||||||
}
|
|
||||||
else if (sheet_view_child_element == qn("spreadsheetml", "pivotSelection")) // CT_PivotSelection 0-4
|
|
||||||
{
|
|
||||||
skip_remaining_content(sheet_view_child_element);
|
|
||||||
}
|
|
||||||
else if (sheet_view_child_element == qn("spreadsheetml", "extLst")) // CT_ExtensionList 0-1
|
|
||||||
{
|
|
||||||
skip_remaining_content(sheet_view_child_element);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unexpected_element(sheet_view_child_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect_end_element(sheet_view_child_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect_end_element(qn("spreadsheetml", "sheetView"));
|
|
||||||
|
|
||||||
ws.d_->views_.push_back(new_view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1
|
|
||||||
{
|
|
||||||
skip_remaining_content(current_worksheet_element);
|
|
||||||
}
|
|
||||||
else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+
|
|
||||||
{
|
|
||||||
while (in_element(qn("spreadsheetml", "cols")))
|
|
||||||
{
|
|
||||||
expect_start_element(qn("spreadsheetml", "col"), xml::content::simple);
|
|
||||||
|
|
||||||
skip_attributes({"bestFit", "collapsed", "outlineLevel"});
|
|
||||||
|
|
||||||
auto min = static_cast<column_t::index_t>(std::stoull(parser().attribute("min")));
|
|
||||||
auto max = static_cast<column_t::index_t>(std::stoull(parser().attribute("max")));
|
|
||||||
|
|
||||||
optional<double> width;
|
|
||||||
|
|
||||||
if (parser().attribute_present("width"))
|
|
||||||
{
|
|
||||||
width = parser().attribute<double>("width");
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<std::size_t> column_style;
|
|
||||||
|
|
||||||
if (parser().attribute_present("style"))
|
|
||||||
{
|
|
||||||
column_style = parser().attribute<std::size_t>("style");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto custom =
|
|
||||||
parser().attribute_present("customWidth") ? is_true(parser().attribute("customWidth")) : false;
|
|
||||||
auto hidden = parser().attribute_present("hidden") ? is_true(parser().attribute("hidden")) : false;
|
|
||||||
|
|
||||||
expect_end_element(qn("spreadsheetml", "col"));
|
|
||||||
|
|
||||||
for (auto column = min; column <= max; column++)
|
|
||||||
{
|
|
||||||
column_properties props;
|
|
||||||
|
|
||||||
if (width.is_set())
|
|
||||||
{
|
|
||||||
props.width = width.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column_style.is_set())
|
|
||||||
{
|
|
||||||
props.style = column_style.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
props.hidden = hidden;
|
|
||||||
props.custom_width = custom;
|
|
||||||
ws.add_column_properties(column, props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1
|
|
||||||
{
|
{
|
||||||
while (in_element(qn("spreadsheetml", "sheetData")))
|
while (in_element(qn("spreadsheetml", "sheetData")))
|
||||||
{
|
{
|
||||||
|
@ -2089,10 +2122,15 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
||||||
|
|
||||||
expect_end_element(qn("spreadsheetml", "worksheet"));
|
expect_end_element(qn("spreadsheetml", "worksheet"));
|
||||||
|
|
||||||
|
auto &manifest = target_.manifest();
|
||||||
|
|
||||||
|
xlnt::path sheet_path;
|
||||||
|
relationship workbook_rel, sheet_rel;
|
||||||
|
|
||||||
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments))
|
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments))
|
||||||
{
|
{
|
||||||
auto comments_part = manifest.canonicalize(
|
auto comments_part = manifest.canonicalize({workbook_rel, sheet_rel,
|
||||||
{workbook_rel, sheet_rel, manifest.relationship(sheet_path, xlnt::relationship_type::comments)});
|
manifest.relationship(sheet_path, xlnt::relationship_type::comments)});
|
||||||
|
|
||||||
auto receive = xml::parser::receive_default;
|
auto receive = xml::parser::receive_default;
|
||||||
auto comments_part_streambuf = archive_->open(comments_part);
|
auto comments_part_streambuf = archive_->open(comments_part);
|
||||||
|
@ -2104,8 +2142,8 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
||||||
|
|
||||||
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing))
|
if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing))
|
||||||
{
|
{
|
||||||
auto vml_drawings_part = manifest.canonicalize(
|
auto vml_drawings_part = manifest.canonicalize({workbook_rel, sheet_rel,
|
||||||
{workbook_rel, sheet_rel, manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)});
|
manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)});
|
||||||
|
|
||||||
auto vml_drawings_part_streambuf = archive_->open(comments_part);
|
auto vml_drawings_part_streambuf = archive_->open(comments_part);
|
||||||
std::istream vml_drawings_part_stream(comments_part_streambuf.get());
|
std::istream vml_drawings_part_stream(comments_part_streambuf.get());
|
||||||
|
|
|
@ -45,6 +45,7 @@ template<typename T>
|
||||||
class optional;
|
class optional;
|
||||||
class path;
|
class path;
|
||||||
class relationship;
|
class relationship;
|
||||||
|
class streaming_workbook_reader;
|
||||||
class variant;
|
class variant;
|
||||||
class workbook;
|
class workbook;
|
||||||
class worksheet;
|
class worksheet;
|
||||||
|
@ -52,6 +53,8 @@ class worksheet;
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class izstream;
|
class izstream;
|
||||||
|
struct cell_impl;
|
||||||
|
struct worksheet_impl;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles writing a workbook into an XLSX file.
|
/// Handles writing a workbook into an XLSX file.
|
||||||
|
@ -61,8 +64,18 @@ class xlsx_consumer
|
||||||
public:
|
public:
|
||||||
xlsx_consumer(workbook &destination);
|
xlsx_consumer(workbook &destination);
|
||||||
|
|
||||||
|
void read(std::istream &source);
|
||||||
|
|
||||||
|
void read(std::istream &source, const std::string &password);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class streaming_workbook_reader;
|
||||||
|
|
||||||
|
|
||||||
void open(std::istream &source);
|
void open(std::istream &source);
|
||||||
|
|
||||||
|
bool has_cell();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the next cell in the current worksheet and optionally returns it if
|
/// Reads the next cell in the current worksheet and optionally returns it if
|
||||||
/// the last cell in the sheet has not yet been read. An exception will be thrown
|
/// the last cell in the sheet has not yet been read. An exception will be thrown
|
||||||
|
@ -70,13 +83,6 @@ public:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
cell read_cell();
|
cell read_cell();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Beings reading of the next worksheet in the workbook and optionally
|
|
||||||
/// returns its title if the last worksheet has not yet been read. An
|
|
||||||
/// exception will be thrown if this is not open as a streaming consumer.
|
|
||||||
/// </summary>
|
|
||||||
std::string begin_worksheet();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ends reading of the current worksheet in the workbook and optionally
|
/// Ends reading of the current worksheet in the workbook and optionally
|
||||||
/// returns a worksheet object corresponding to the worksheet with the title
|
/// returns a worksheet object corresponding to the worksheet with the title
|
||||||
|
@ -85,11 +91,8 @@ public:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
worksheet end_worksheet();
|
worksheet end_worksheet();
|
||||||
|
|
||||||
void read(std::istream &source);
|
bool has_worksheet();
|
||||||
|
|
||||||
void read(std::istream &source, const std::string &password);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read all the files needed from the XLSX archive and initialize all of
|
/// Read all the files needed from the XLSX archive and initialize all of
|
||||||
/// the data in the workbook to match.
|
/// the data in the workbook to match.
|
||||||
|
@ -208,6 +211,16 @@ private:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void read_worksheet(const std::string &title);
|
void read_worksheet(const std::string &title);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// xl/sheets/*.xml
|
||||||
|
/// </summary>
|
||||||
|
std::string read_worksheet_begin();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// xl/sheets/*.xml
|
||||||
|
/// </summary>
|
||||||
|
void read_worksheet_end();
|
||||||
|
|
||||||
// Sheet Relationship Target Parts
|
// Sheet Relationship Target Parts
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -398,6 +411,14 @@ private:
|
||||||
std::vector<xml::qname> stack_;
|
std::vector<xml::qname> stack_;
|
||||||
|
|
||||||
bool preserve_space_ = false;
|
bool preserve_space_ = false;
|
||||||
|
|
||||||
|
std::vector<relationship> worksheet_queue_;
|
||||||
|
|
||||||
|
detail::cell_impl *stream_cell_;
|
||||||
|
|
||||||
|
detail::worksheet_impl *stream_worksheet_;
|
||||||
|
|
||||||
|
bool in_sheet_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
|
@ -32,6 +32,45 @@
|
||||||
#include <xlnt/workbook/workbook.hpp>
|
#include <xlnt/workbook/workbook.hpp>
|
||||||
#include <xlnt/worksheet/worksheet.hpp>
|
#include <xlnt/worksheet/worksheet.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
//TODO: (important) this is duplicated from workbook.cpp, find a common place to keep it
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
void open_stream(std::ifstream &stream, const std::wstring &path)
|
||||||
|
{
|
||||||
|
stream.open(path, std::ios::binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_stream(std::ofstream &stream, const std::wstring &path)
|
||||||
|
{
|
||||||
|
stream.open(path, std::ios::binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_stream(std::ifstream &stream, const std::string &path)
|
||||||
|
{
|
||||||
|
open_stream(stream, xlnt::path(path).wstring());
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_stream(std::ofstream &stream, const std::string &path)
|
||||||
|
{
|
||||||
|
open_stream(stream, xlnt::path(path).wstring());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void open_stream(std::ifstream &stream, const std::string &path)
|
||||||
|
{
|
||||||
|
stream.open(path, std::ios::binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_stream(std::ofstream &stream, const std::string &path)
|
||||||
|
{
|
||||||
|
stream.open(path, std::ios::binary);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
namespace xlnt {
|
namespace xlnt {
|
||||||
|
|
||||||
streaming_workbook_reader::~streaming_workbook_reader()
|
streaming_workbook_reader::~streaming_workbook_reader()
|
||||||
|
@ -41,10 +80,15 @@ streaming_workbook_reader::~streaming_workbook_reader()
|
||||||
|
|
||||||
void streaming_workbook_reader::close()
|
void streaming_workbook_reader::close()
|
||||||
{
|
{
|
||||||
if (consumer_)
|
if (consumer_)
|
||||||
{
|
{
|
||||||
consumer_.reset(nullptr);
|
consumer_.reset(nullptr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool streaming_workbook_reader::has_cell()
|
||||||
|
{
|
||||||
|
return consumer_->has_cell();
|
||||||
}
|
}
|
||||||
|
|
||||||
cell streaming_workbook_reader::read_cell()
|
cell streaming_workbook_reader::read_cell()
|
||||||
|
@ -52,9 +96,14 @@ cell streaming_workbook_reader::read_cell()
|
||||||
return consumer_->read_cell();
|
return consumer_->read_cell();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool streaming_workbook_reader::has_worksheet()
|
||||||
|
{
|
||||||
|
return consumer_->has_worksheet();
|
||||||
|
}
|
||||||
|
|
||||||
std::string streaming_workbook_reader::begin_worksheet()
|
std::string streaming_workbook_reader::begin_worksheet()
|
||||||
{
|
{
|
||||||
return consumer_->begin_worksheet();
|
return consumer_->read_worksheet_begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
worksheet streaming_workbook_reader::end_worksheet()
|
worksheet streaming_workbook_reader::end_worksheet()
|
||||||
|
@ -71,21 +120,22 @@ void streaming_workbook_reader::open(const std::vector<std::uint8_t> &data)
|
||||||
|
|
||||||
void streaming_workbook_reader::open(const std::string &filename)
|
void streaming_workbook_reader::open(const std::string &filename)
|
||||||
{
|
{
|
||||||
std::ifstream file_stream(filename, std::ios::binary);
|
std::ifstream file_stream;
|
||||||
open(file_stream);
|
open_stream(file_stream, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
void streaming_workbook_reader::open(const std::wstring &filename)
|
void streaming_workbook_reader::open(const std::wstring &filename)
|
||||||
{
|
{
|
||||||
std::ifstream file_stream(filename, std::ios::binary);
|
std::ifstream file_stream;
|
||||||
open(file_stream);
|
open_stream(file_stream, filename);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void streaming_workbook_reader::open(const xlnt::path &filename)
|
void streaming_workbook_reader::open(const xlnt::path &filename)
|
||||||
{
|
{
|
||||||
open(filename.string());
|
std::ifstream file_stream;
|
||||||
|
open_stream(file_stream, filename.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
void streaming_workbook_reader::open(std::istream &stream)
|
void streaming_workbook_reader::open(std::istream &stream)
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <helpers/test_suite.hpp>
|
#include <helpers/test_suite.hpp>
|
||||||
#include <helpers/path_helper.hpp>
|
#include <helpers/path_helper.hpp>
|
||||||
#include <helpers/xml_helper.hpp>
|
#include <helpers/xml_helper.hpp>
|
||||||
|
#include <xlnt/workbook/streaming_workbook_reader.hpp>
|
||||||
|
#include <xlnt/workbook/streaming_workbook_writer.hpp>
|
||||||
#include <xlnt/workbook/workbook.hpp>
|
#include <xlnt/workbook/workbook.hpp>
|
||||||
|
|
||||||
class serialization_test_suite : public test_suite
|
class serialization_test_suite : public test_suite
|
||||||
|
@ -461,4 +463,42 @@ public:
|
||||||
xlnt_assert(round_trip_matches_rw(path, password));
|
xlnt_assert(round_trip_matches_rw(path, password));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_streaming_read()
|
||||||
|
{
|
||||||
|
const auto path = path_helper::test_file("4_every_style.xlsx");
|
||||||
|
xlnt::streaming_workbook_reader reader;
|
||||||
|
|
||||||
|
reader.open(path);
|
||||||
|
|
||||||
|
while (reader.has_worksheet())
|
||||||
|
{
|
||||||
|
reader.begin_worksheet();
|
||||||
|
|
||||||
|
while (reader.has_cell())
|
||||||
|
{
|
||||||
|
const auto cell = reader.read_cell();
|
||||||
|
std::cout << cell.reference().to_string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ws = reader.end_worksheet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_streaming_write()
|
||||||
|
{
|
||||||
|
const auto path = std::string("stream-out.xlsx");
|
||||||
|
xlnt::streaming_workbook_writer writer;
|
||||||
|
|
||||||
|
writer.open(path);
|
||||||
|
|
||||||
|
writer.add_sheet("stream");
|
||||||
|
|
||||||
|
auto b2 = writer.add_cell("B2");
|
||||||
|
b2.value("B2!");
|
||||||
|
|
||||||
|
auto c3 = writer.add_cell("C3");
|
||||||
|
b2.value("should not change");
|
||||||
|
c3.value("C3!");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user