mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
change some things
This commit is contained in:
parent
cbab7a36cb
commit
21b3d366d6
|
@ -37,6 +37,7 @@ project "xlnt"
|
|||
targetdir "../../lib/"
|
||||
includedirs {
|
||||
"../../include",
|
||||
"../../source",
|
||||
"../../third-party/miniz",
|
||||
"../../third-party/pugixml/src"
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ struct time;
|
|||
struct timedelta;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct cell_impl;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -7,7 +7,9 @@ namespace xlnt {
|
|||
class cell;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct comment_impl;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// <summary>
|
||||
|
|
7
include/xlnt/formula/tokenizer.hpp
Normal file
7
include/xlnt/formula/tokenizer.hpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace xlnt {
|
||||
|
||||
class tokenizer {
|
||||
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
33
include/xlnt/formula/translate.hpp
Normal file
33
include/xlnt/formula/translate.hpp
Normal 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
|
|
@ -23,11 +23,16 @@
|
|||
// @author: see AUTHORS file
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class excel_reader
|
||||
{
|
||||
class workbook;
|
||||
|
||||
};
|
||||
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
|
||||
|
|
|
@ -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
|
36
include/xlnt/reader/shared_strings_reader.hpp
Normal file
36
include/xlnt/reader/shared_strings_reader.hpp
Normal 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
|
|
@ -31,12 +31,17 @@
|
|||
|
||||
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
|
||||
|
|
|
@ -24,13 +24,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class document_properties;
|
||||
class relationship;
|
||||
class workbook;
|
||||
class zip_file;
|
||||
|
||||
xlnt::workbook load_workbook(const std::vector<std::uint8_t> &bytes);
|
||||
xlnt::workbook load_workbook(const std::string &filename);
|
||||
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
|
||||
|
|
|
@ -23,11 +23,19 @@
|
|||
// @author: see AUTHORS file
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class worksheet_reader
|
||||
{
|
||||
class workbook;
|
||||
class worksheet;
|
||||
|
||||
};
|
||||
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
|
||||
|
|
|
@ -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:
|
|
@ -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
|
||||
|
|
39
include/xlnt/styles/gradient_fill.hpp
Normal file
39
include/xlnt/styles/gradient_fill.hpp
Normal 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
|
90
include/xlnt/styles/named_style.hpp
Normal file
90
include/xlnt/styles/named_style.hpp
Normal 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
|
43
include/xlnt/styles/pattern_fill.hpp
Normal file
43
include/xlnt/styles/pattern_fill.hpp
Normal 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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
9
include/xlnt/workbook/external_book.hpp
Normal file
9
include/xlnt/workbook/external_book.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class external_book {
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
@ -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 drawing_reader
|
||||
class sheet_view
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
|
@ -25,9 +25,7 @@
|
|||
|
||||
namespace xlnt {
|
||||
|
||||
class drawing_writer
|
||||
{
|
||||
|
||||
class worksheet_properties {
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
|
@ -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"
|
||||
|
|
|
@ -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
|
67
source/reader/excel_reader.cpp
Normal file
67
source/reader/excel_reader.cpp
Normal 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
|
29
source/reader/shared_strings_reader.cpp
Normal file
29
source/reader/shared_strings_reader.cpp
Normal 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
|
238
source/reader/workbook_reader.cpp
Normal file
238
source/reader/workbook_reader.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include <xlnt/styles/borders.hpp>
|
||||
#include <xlnt/styles/border.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
186
source/worksheet/worksheet_reader.cpp
Normal file
186
source/worksheet/worksheet_reader.cpp
Normal 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
|
|
@ -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());
|
|
@ -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>
|
|
@ -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();
|
||||
|
|
|
@ -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++));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user