#include #include #include #include #include #include #include #include #include #include #include namespace { bool is_true(const std::string &bool_string) { return bool_string == "1" || bool_string == "true"; } bool is_false(const std::string &bool_string) { return bool_string == "0" || bool_string == "false"; } std::size_t string_to_size_t(const std::string &s) { #if ULLONG_MAX == SIZE_MAX return std::stoull(s); #else return std::stoul(s); #endif } 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; } struct EnumClassHash { template std::size_t operator()(T t) const { return static_cast(t); } }; std::string string_lower(std::string str) { for (std::size_t i = 0; i < str.size(); i++) { str[i] = std::tolower(str[i]); } return str; } template T from_string(const std::string &string); template<> xlnt::font::underline_style from_string(const std::string &underline_string) { if (underline_string == "double") return xlnt::font::underline_style::double_; if (underline_string == "doubleAccounting") return xlnt::font::underline_style::double_accounting; if (underline_string == "single") return xlnt::font::underline_style::single; if (underline_string == "singleAccounting") return xlnt::font::underline_style::single_accounting; return xlnt::font::underline_style::none; } template<> xlnt::pattern_fill::type from_string(const std::string &fill_type) { if (fill_type == "darkdown") return xlnt::pattern_fill::type::darkdown; if (fill_type == "darkgray") return xlnt::pattern_fill::type::darkgray; if (fill_type == "darkgrid") return xlnt::pattern_fill::type::darkgrid; if (fill_type == "darkhorizontal") return xlnt::pattern_fill::type::darkhorizontal; if (fill_type == "darktrellis") return xlnt::pattern_fill::type::darktrellis; if (fill_type == "darkup") return xlnt::pattern_fill::type::darkup; if (fill_type == "darkvertical") return xlnt::pattern_fill::type::darkvertical; if (fill_type == "gray0625") return xlnt::pattern_fill::type::gray0625; if (fill_type == "gray125") return xlnt::pattern_fill::type::gray125; if (fill_type == "lightdown") return xlnt::pattern_fill::type::lightdown; if (fill_type == "lightgray") return xlnt::pattern_fill::type::lightgray; if (fill_type == "lighthorizontal") return xlnt::pattern_fill::type::lighthorizontal; if (fill_type == "lighttrellis") return xlnt::pattern_fill::type::lighttrellis; if (fill_type == "lightup") return xlnt::pattern_fill::type::lightup; if (fill_type == "lightvertical") return xlnt::pattern_fill::type::lightvertical; if (fill_type == "mediumgray") return xlnt::pattern_fill::type::mediumgray; if (fill_type == "solid") return xlnt::pattern_fill::type::solid; return xlnt::pattern_fill::type::none; }; template<> xlnt::gradient_fill::type from_string(const std::string &fill_type) { //TODO what's the default? if (fill_type == "linear") return xlnt::gradient_fill::type::linear; return xlnt::gradient_fill::type::path; }; template<> xlnt::border_style from_string(const std::string &border_style_string) { if (border_style_string == "dashdot") return xlnt::border_style::dashdot; if (border_style_string == "dashdotdot") return xlnt::border_style::dashdotdot; if (border_style_string == "dashed") return xlnt::border_style::dashed; if (border_style_string == "dotted") return xlnt::border_style::dotted; if (border_style_string == "double") return xlnt::border_style::double_; if (border_style_string == "hair") return xlnt::border_style::hair; if (border_style_string == "medium") return xlnt::border_style::medium; if (border_style_string == "mediumdashdot") return xlnt::border_style::mediumdashdot; if (border_style_string == "mediumdashdotdot") return xlnt::border_style::mediumdashdotdot; if (border_style_string == "mediumdashed") return xlnt::border_style::mediumdashed; if (border_style_string == "slantdashdot") return xlnt::border_style::slantdashdot; if (border_style_string == "thick") return xlnt::border_style::thick; if (border_style_string == "thin") return xlnt::border_style::thin; return xlnt::border_style::none; } template<> xlnt::vertical_alignment from_string(const std::string &vertical_alignment_string) { if (vertical_alignment_string == "bottom") return xlnt::vertical_alignment::bottom; if (vertical_alignment_string == "center") return xlnt::vertical_alignment::center; if (vertical_alignment_string == "justify") return xlnt::vertical_alignment::justify; if (vertical_alignment_string == "top") return xlnt::vertical_alignment::top; return xlnt::vertical_alignment::none; } template<> xlnt::horizontal_alignment from_string(const std::string &horizontal_alignment_string) { if (horizontal_alignment_string == "center") return xlnt::horizontal_alignment::center; if (horizontal_alignment_string == "center-continous") return xlnt::horizontal_alignment::center_continuous; if (horizontal_alignment_string == "general") return xlnt::horizontal_alignment::general; if (horizontal_alignment_string == "justify") return xlnt::horizontal_alignment::justify; if (horizontal_alignment_string == "left") return xlnt::horizontal_alignment::left; if (horizontal_alignment_string == "right") return xlnt::horizontal_alignment::right; return xlnt::horizontal_alignment::none; } template<> xlnt::relationship::type from_string(const std::string &type_string) { if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties") { return xlnt::relationship::type::extended_properties; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties") { return xlnt::relationship::type::custom_properties; } else if (type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties") { return xlnt::relationship::type::core_properties; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") { return xlnt::relationship::type::office_document; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") { return xlnt::relationship::type::worksheet; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings") { return xlnt::relationship::type::shared_string_table; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles") { return xlnt::relationship::type::styles; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") { return xlnt::relationship::type::theme; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") { return xlnt::relationship::type::hyperlink; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet") { return xlnt::relationship::type::chartsheet; } else if (type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail") { return xlnt::relationship::type::thumbnail; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain") { return xlnt::relationship::type::calculation_chain; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings") { return xlnt::relationship::type::printer_settings; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing") { return xlnt::relationship::type::drawings; } else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") { return xlnt::relationship::type::image; } return xlnt::relationship::type::unknown; } xlnt::protection read_protection(const pugi::xml_node protection_node) { xlnt::protection prot; prot.set_locked(is_true(protection_node.attribute("locked").value())); prot.set_hidden(is_true(protection_node.attribute("hidden").value())); return prot; } xlnt::alignment read_alignment(const pugi::xml_node alignment_node) { xlnt::alignment align; align.set_wrap_text(is_true(alignment_node.attribute("wrapText").value())); align.set_shrink_to_fit(is_true(alignment_node.attribute("shrinkToFit").value())); if (alignment_node.attribute("vertical")) { std::string vertical = alignment_node.attribute("vertical").value(); align.set_vertical(from_string(vertical)); } if (alignment_node.attribute("horizontal")) { std::string horizontal = alignment_node.attribute("horizontal").value(); align.set_horizontal(from_string(horizontal)); } return align; } void read_number_formats(const pugi::xml_node number_formats_node, std::vector &number_formats) { number_formats.clear(); for (auto num_fmt_node : number_formats_node.children("numFmt")) { std::string format_string(num_fmt_node.attribute("formatCode").value()); if (format_string == "GENERAL") { format_string = "General"; } xlnt::number_format nf; nf.set_format_string(format_string); nf.set_id(string_to_size_t(num_fmt_node.attribute("numFmtId").value())); number_formats.push_back(nf); } } xlnt::color read_color(const pugi::xml_node &color_node) { xlnt::color result; if (color_node.attribute("auto")) { return result; } if (color_node.attribute("rgb")) { result = xlnt::rgb_color(color_node.attribute("rgb").value()); } else if (color_node.attribute("theme")) { result = xlnt::theme_color(string_to_size_t(color_node.attribute("theme").value())); } else if (color_node.attribute("indexed")) { result = xlnt::indexed_color(string_to_size_t(color_node.attribute("indexed").value())); } if (color_node.attribute("tint")) { result.set_tint(color_node.attribute("tint").as_double()); } return result; } xlnt::font read_font(const pugi::xml_node font_node) { xlnt::font new_font; new_font.set_size(string_to_size_t(font_node.child("sz").attribute("val").value())); new_font.set_name(font_node.child("name").attribute("val").value()); if (font_node.child("color")) { new_font.set_color(read_color(font_node.child("color"))); } if (font_node.child("family")) { new_font.set_family(string_to_size_t(font_node.child("family").attribute("val").value())); } if (font_node.child("scheme")) { new_font.set_scheme(font_node.child("scheme").attribute("val").value()); } if (font_node.child("b")) { if (font_node.child("b").attribute("val")) { new_font.set_bold(is_true(font_node.child("b").attribute("val").value())); } else { new_font.set_bold(true); } } if (font_node.child("strike")) { if (font_node.child("strike").attribute("val")) { new_font.set_strikethrough(is_true(font_node.child("strike").attribute("val").value())); } else { new_font.set_strikethrough(true); } } if (font_node.child("i")) { if (font_node.child("i").attribute("val")) { new_font.set_italic(is_true(font_node.child("i").attribute("val").value())); } else { new_font.set_italic(true); } } if (font_node.child("u")) { if (font_node.child("u").attribute("val")) { std::string underline_string = font_node.child("u").attribute("val").value(); new_font.set_underline(from_string(underline_string)); } else { new_font.set_underline(xlnt::font::underline_style::single); } } return new_font; } void read_fonts(const pugi::xml_node &fonts_node, std::vector &fonts) { fonts.clear(); for (auto font_node : fonts_node.children()) { fonts.push_back(read_font(font_node)); } } void read_indexed_colors(const pugi::xml_node &indexed_colors_node, std::vector &colors) { for (auto color_node : indexed_colors_node.children()) { colors.push_back(read_color(color_node)); } } void read_colors(const pugi::xml_node &colors_node, std::vector &colors) { colors.clear(); if (colors_node.child("indexedColors")) { read_indexed_colors(colors_node.child("indexedColors"), colors); } } xlnt::fill read_fill(const pugi::xml_node &fill_node) { xlnt::fill new_fill; if (fill_node.child("patternFill")) { auto pattern_fill_node = fill_node.child("patternFill"); std::string pattern_fill_type_string = pattern_fill_node.attribute("patternType").value(); if (!pattern_fill_type_string.empty()) { new_fill = xlnt::fill::pattern(from_string(pattern_fill_type_string)); if (pattern_fill_node.child("bgColor")) { new_fill.get_pattern_fill().get_background_color() = read_color(pattern_fill_node.child("bgColor")); } if (pattern_fill_node.child("fgColor")) { new_fill.get_pattern_fill().get_foreground_color() = read_color(pattern_fill_node.child("fgColor")); } } else { new_fill = xlnt::fill::pattern(xlnt::pattern_fill::type::none); } } else if (fill_node.child("gradientFill")) { auto gradient_fill_node = fill_node.child("gradientFill"); std::string gradient_fill_type_string = gradient_fill_node.attribute("type").value(); if (!gradient_fill_type_string.empty()) { new_fill = xlnt::fill::gradient(from_string(gradient_fill_type_string)); } else { new_fill = xlnt::fill::gradient(xlnt::gradient_fill::type::linear); } for (auto stop_node : gradient_fill_node.children("stop")) { auto position = stop_node.attribute("position").as_double(); auto color = read_color(stop_node.child("color")); new_fill.get_gradient_fill().add_stop(position, color); } } return new_fill; } void read_fills(const pugi::xml_node &fills_node, std::vector &fills) { fills.clear(); for (auto fill_node : fills_node.children()) { fills.emplace_back(); fills.back() = read_fill(fill_node); } } xlnt::border::border_property read_side(const pugi::xml_node &side_node) { xlnt::border::border_property new_side; if (side_node.attribute("style")) { new_side.set_style(from_string(side_node.attribute("style").value())); } if (side_node.child("color")) { new_side.set_color(read_color(side_node.child("color"))); } return new_side; } xlnt::border read_border(const pugi::xml_node &border_node) { xlnt::border new_border; for (const auto &side_name : xlnt::border::get_side_names()) { if (border_node.child(side_name.second.c_str())) { auto side = read_side(border_node.child(side_name.second.c_str())); new_border.set_side(side_name.first, side); } } return new_border; } void read_borders(const pugi::xml_node &borders_node, std::vector &borders) { borders.clear(); for (auto border_node : borders_node.children()) { borders.push_back(read_border(border_node)); } } bool read_base_format(const pugi::xml_node &format_node, const xlnt::detail::stylesheet &stylesheet, xlnt::base_format &f) { // Alignment f.alignment_applied(format_node.child("alignment") || is_true(format_node.attribute("applyAlignment").value())); if (f.alignment_applied()) { auto inline_alignment = read_alignment(format_node.child("alignment")); f.set_alignment(inline_alignment); } // Border auto border_index = format_node.attribute("borderId") ? string_to_size_t(format_node.attribute("borderId").value()) : 0; f.set_border(stylesheet.borders.at(border_index)); f.border_applied(is_true(format_node.attribute("applyBorder").value())); // Fill auto fill_index = format_node.attribute("fillId") ? string_to_size_t(format_node.attribute("fillId").value()) : 0; f.set_fill(stylesheet.fills.at(fill_index)); f.fill_applied(is_true(format_node.attribute("applyFill").value())); // Font auto font_index = format_node.attribute("fontId") ? string_to_size_t(format_node.attribute("fontId").value()) : 0; f.set_font(stylesheet.fonts.at(font_index)); f.font_applied(is_true(format_node.attribute("applyFont").value())); // Number Format auto number_format_id = string_to_size_t(format_node.attribute("numFmtId").value()); bool builtin_format = true; for (const auto &num_fmt : stylesheet.number_formats) { if (static_cast(num_fmt.get_id()) == number_format_id) { f.set_number_format(num_fmt); builtin_format = false; break; } } if (builtin_format) { try { f.set_number_format(xlnt::number_format::from_builtin_id(number_format_id)); } catch (std::runtime_error) { f.set_number_format(xlnt::number_format::general()); } } f.number_format_applied(is_true(format_node.attribute("applyNumberFormat").value())); // Protection f.protection_applied(format_node.attribute("protection") || is_true(format_node.attribute("applyProtection").value())); if (f.protection_applied()) { auto inline_protection = read_protection(format_node.child("protection")); f.set_protection(inline_protection); } return true; } void read_formats(const pugi::xml_node &formats_node, const xlnt::detail::stylesheet &stylesheet, std::vector &formats, std::vector &format_styles) { for (auto format_node : formats_node.children("xf")) { xlnt::format format; read_base_format(format_node, stylesheet, format); auto style_index = string_to_size_t(format_node.attribute("xfId").value()); auto style_name = stylesheet.style_name_map.at(style_index); format_styles.push_back(style_name); formats.push_back(format); } } xlnt::style read_style(const pugi::xml_node &style_node, const pugi::xml_node &style_format_node, const xlnt::detail::stylesheet &stylesheet) { xlnt::style s; read_base_format(style_format_node, stylesheet, s); s.set_name(style_node.attribute("name").value()); s.set_hidden(style_node.attribute("hidden") && is_true(style_node.attribute("hidden").value())); s.set_builtin_id(string_to_size_t(style_node.attribute("builtinId").value())); return s; } void read_styles(const pugi::xml_node &styles_node, const pugi::xml_node &style_formats_node, const xlnt::detail::stylesheet stylesheet, std::vector &styles, std::unordered_map &style_names) { std::size_t style_index = 0; for (auto cell_style_format_node : style_formats_node.children()) { bool match = false; for (auto cell_style_node : styles_node.children()) { auto cell_style_format_index = std::stoull(cell_style_node.attribute("xfId").value()); if (cell_style_format_index == style_index) { styles.push_back(read_style(cell_style_node, cell_style_format_node, stylesheet)); style_names[style_index] = styles.back().get_name(); match = true; break; } } style_index++; } } std::vector read_relationships(const xlnt::path &part, xlnt::zip_file &archive) { std::vector relationships; if (!archive.has_file(part)) return relationships; pugi::xml_document document; document.load_string(archive.read(part).c_str()); auto root_node = document.child("Relationships"); for (auto relationship_node : root_node.children("Relationship")) { std::string id(relationship_node.attribute("Id").value()); std::string type_string(relationship_node.attribute("Type").value()); auto type = from_string(type_string); xlnt::path rel_target(relationship_node.attribute("Target").value()); relationships.push_back(xlnt::relationship(id, type, rel_target, xlnt::target_mode::internal)); } return relationships; } 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; } /// /// Returns true if d is exactly equal to an integer. /// bool is_integral(long double d) { return d == static_cast(d); } void check_document_type(const std::string &document_content_type) { if (document_content_type != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" && document_content_type != "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml ") { throw xlnt::invalid_file(document_content_type); } } } // namespace namespace xlnt { namespace detail { xlsx_consumer::xlsx_consumer(workbook &destination) : destination_(destination) { } void xlsx_consumer::read(const path &source) { source_.load(source); populate_workbook(); } void xlsx_consumer::read(std::istream &source) { source_.load(source); populate_workbook(); } void xlsx_consumer::read(const std::vector &source) { source_.load(source); populate_workbook(); } // Part Writing Methods void xlsx_consumer::populate_workbook() { auto &manifest = destination_.get_manifest(); manifest.clear(); destination_.d_->worksheets_.clear(); read_manifest(); for (const auto &rel : manifest.get_package_relationships()) { pugi::xml_document document; document.load_string(source_.read(path(rel.get_target_uri())).c_str()); switch (rel.get_type()) { case relationship::type::core_properties: read_core_properties(document.root()); break; case relationship::type::extended_properties: read_extended_properties(document.root()); break; case relationship::type::custom_properties: read_custom_property(document.root()); break; case relationship::type::office_document: check_document_type(manifest.get_part_content_type_string(rel.get_target_uri())); read_workbook(document.root()); break; } } for (const auto &rel : manifest.get_part_relationships(constants::part_workbook())) { pugi::xml_document document; document.load_string(source_.read(path(rel.get_target_uri())).c_str()); switch (rel.get_type()) { case relationship::type::calculation_chain: read_calculation_chain(document.root()); break; case relationship::type::chartsheet: read_chartsheet(document.root(), rel.get_id()); break; case relationship::type::connections: read_connections(document.root()); break; case relationship::type::custom_xml_mappings: read_custom_xml_mappings(document.root()); break; case relationship::type::dialogsheet: read_dialogsheet(document.root(), rel.get_id()); break; case relationship::type::external_workbook_references: read_external_workbook_references(document.root()); break; case relationship::type::metadata: read_metadata(document.root()); break; case relationship::type::pivot_table: read_pivot_table(document.root()); break; case relationship::type::shared_string_table: read_shared_string_table(document.root()); break; case relationship::type::shared_workbook_revision_headers: read_shared_workbook_revision_headers(document.root()); break; case relationship::type::styles: read_styles(document.root()); break; case relationship::type::theme: read_theme(document.root()); break; case relationship::type::volatile_dependencies: read_volatile_dependencies(document.root()); break; case relationship::type::worksheet: read_worksheet(document.root(), rel.get_id()); break; } } // Sheet Relationship Target Parts void read_comments(); void read_drawings(); // Unknown Parts void read_unknown_parts(); void read_unknown_relationships(); auto current_iter = destination_.d_->worksheets_.begin(); for (std::size_t current_index = 0; current_index < destination_.get_sheet_titles().size(); ++current_index) { auto current_id = current_iter->id_; for (const auto j : worksheet_rel_title_id_index_map_) { if (std::get<1>(j.second) == current_id) { if (std::get<2>(j.second) == current_index) break; auto switch_iter = destination_.d_->worksheets_.begin(); for (std::size_t k = 0; k < std::get<2>(j.second); ++k) { ++switch_iter; } std::swap(*switch_iter, *current_iter); } } ++current_iter; } } // Package Parts void xlsx_consumer::read_manifest() { auto package_rels = read_relationships(path("_rels/.rels"), source_); pugi::xml_document document; document.load_string(source_.read(path("[Content_Types].xml")).c_str()); const auto root_node = document.child("Types"); auto &manifest = destination_.get_manifest(); std::unordered_map> part_types; auto make_relative = [](const path &p) { path copy; for (const auto &part : std::vector(p.begin() + 1, p.end())) { copy.append(part); } return copy; }; for (const auto child : root_node.children()) { if (child.name() == std::string("Default")) { manifest.register_default_type(child.attribute("Extension").value(), child.attribute("ContentType").value()); } else if (child.name() == std::string("Override")) { path part(child.attribute("PartName").value()); part_types[make_relative(part)] = child.attribute("ContentType").value(); } } for (const auto &package_rel : package_rels) { manifest.register_package_part(package_rel.get_target_uri(), part_types.at(package_rel.get_target_uri()), package_rel.get_type()); } for (const auto &relationship_source : part_types) { auto rels_name = relationship_source.first.parent() .append("_rels") .append(relationship_source.first.basename() + ".rels"); auto part_rels = read_relationships(rels_name, source_); if (part_rels.empty()) continue; for (const auto part_rel : part_rels) { auto part = relationship_source.first.parent().append(part_rel.get_target_uri()); relationship new_part_rel(part_rel.get_id(), part_rel.get_type(), part, target_mode::internal); manifest.register_part(relationship_source.first, new_part_rel, part_types.at(part)); } } } void xlsx_consumer::read_extended_properties(const pugi::xml_node root) { for (auto property_node : root.child("Properties")) { std::string name(property_node.name()); std::string value(property_node.text().get()); if (name == "Application") destination_.set_application(value); else if (name == "DocSecurity") destination_.set_doc_security(std::stoi(value)); else if (name == "ScaleCrop") destination_.set_scale_crop(is_true(value)); else if (name == "Company") destination_.set_company(value); else if (name == "SharedDoc") destination_.set_shared_doc(is_true(value)); else if (name == "HyperlinksChanged") destination_.set_hyperlinks_changed(is_true(value)); else if (name == "AppVersion") destination_.set_app_version(value); else if (name == "Application") destination_.set_application(value); } } void xlsx_consumer::read_core_properties(const pugi::xml_node root) { for (auto property_node : root.child("cp:coreProperties")) { std::string name(property_node.name()); std::string value(property_node.text().get()); if (name == "dc:creator") destination_.set_creator(value); else if (name == "cp:lastModifiedBy") destination_.set_last_modified_by(value); else if (name == "dcterms:created") destination_.set_created(w3cdtf_to_datetime(value)); else if (name == "dcterms:modified") destination_.set_modified(w3cdtf_to_datetime(value)); } } void xlsx_consumer::read_custom_file_properties(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("customFileProperties"); } // Write SpreadsheetML-Specific Package Parts void xlsx_consumer::read_workbook(const pugi::xml_node root) { auto workbook_node = root.child("workbook"); auto workbook_pr_node = workbook_node.child("workbookPr"); if (workbook_pr_node.attribute("date1904")) { std::string value = workbook_pr_node.attribute("date1904").value(); if (value == "1" || value == "true") { destination_.set_base_date(xlnt::calendar::mac_1904); } } for (auto sheet_node : workbook_node.child("sheets").children("sheet")) { std::string rel_id(sheet_node.attribute("r:id").value()); std::string title(sheet_node.attribute("name").value()); auto id = string_to_size_t(sheet_node.attribute("sheetId").value()); worksheet_rel_title_id_index_map_[rel_id] = { title, id, worksheet_rel_title_id_index_map_.size() }; } } // Write Workbook Relationship Target Parts void xlsx_consumer::read_calculation_chain(const pugi::xml_node root) { } void xlsx_consumer::read_chartsheet(const pugi::xml_node root, const std::string &title) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("chartsheet"); } void xlsx_consumer::read_connections(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("connections"); } void xlsx_consumer::read_custom_property(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("customProperty"); } void xlsx_consumer::read_custom_xml_mappings(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("connections"); } void xlsx_consumer::read_dialogsheet(const pugi::xml_node root, const std::string &title) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("dialogsheet"); } void xlsx_consumer::read_external_workbook_references(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("externalLink"); } void xlsx_consumer::read_metadata(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("metadata"); } void xlsx_consumer::read_pivot_table(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("pivotTableDefinition"); } void xlsx_consumer::read_shared_string_table(const pugi::xml_node root) { auto sst_node = root.child("sst"); std::size_t unique_count = 0; if (sst_node.attribute("uniqueCount")) { unique_count = string_to_size_t(sst_node.attribute("uniqueCount").value()); } auto &strings = destination_.get_shared_strings(); for (const auto string_item_node : sst_node.children("si")) { if (string_item_node.child("t")) { text t; t.set_plain_string(string_item_node.child("t").text().get()); strings.push_back(t); } else if (string_item_node.child("r")) // possible multiple text entities. { text t; for (const auto& rich_text_run_node : string_item_node.children("r")) { if (rich_text_run_node.child("t")) { text_run run; run.set_string(rich_text_run_node.child("t").text().get()); if (rich_text_run_node.child("rPr")) { auto run_properties_node = rich_text_run_node.child("rPr"); if (run_properties_node.child("sz")) { run.set_size(string_to_size_t(run_properties_node.child("sz").attribute("val").value())); } if (run_properties_node.child("rFont")) { run.set_font(run_properties_node.child("rFont").attribute("val").value()); } if (run_properties_node.child("color")) { run.set_color(run_properties_node.child("color").attribute("rgb").value()); } if (run_properties_node.child("family")) { run.set_family(string_to_size_t(run_properties_node.child("family").attribute("val").value())); } if (run_properties_node.child("scheme")) { run.set_scheme(run_properties_node.child("scheme").attribute("val").value()); } } t.add_run(run); } } strings.push_back(t); } } if (unique_count != strings.size()) { throw std::runtime_error("counts don't match"); } } void xlsx_consumer::read_shared_workbook_revision_headers(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("headers"); } void xlsx_consumer::read_shared_workbook(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("revisions"); } void xlsx_consumer::read_shared_workbook_user_data(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("users"); } void xlsx_consumer::read_styles(const pugi::xml_node root) { pugi::xml_document document; auto stylesheet_node = document.child("styleSheet"); auto &stylesheet = destination_.impl().stylesheet_; ::read_borders(stylesheet_node.child("borders"), stylesheet.borders); ::read_fills(stylesheet_node.child("fills"), stylesheet.fills); ::read_fonts(stylesheet_node.child("fonts"), stylesheet.fonts); ::read_number_formats(stylesheet_node.child("numFmts"), stylesheet.number_formats); ::read_colors(stylesheet_node.child("colors"), stylesheet.colors); ::read_styles(stylesheet_node.child("cellStyles"), stylesheet_node.child("cellStyleXfs"), stylesheet, stylesheet.styles, stylesheet.style_name_map); ::read_formats(stylesheet_node.child("cellXfs"), stylesheet, stylesheet.formats, stylesheet.format_styles); } void xlsx_consumer::read_theme(const pugi::xml_node root) { root.child("theme"); destination_.set_theme(theme()); } void xlsx_consumer::read_volatile_dependencies(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("volTypes"); } void xlsx_consumer::read_worksheet(const pugi::xml_node root, const std::string &rel_id) { auto title = std::get<0>(worksheet_rel_title_id_index_map_[rel_id]); auto id = std::get<1>(worksheet_rel_title_id_index_map_[rel_id]); destination_.d_->worksheets_.emplace_back(&destination_, id, title); auto ws = destination_.get_sheet_by_id(id); auto worksheet_node = root.child("worksheet"); auto dimension_node = worksheet_node.child("dimension"); std::string dimension(dimension_node.attribute("ref").value()); auto full_range = xlnt::range_reference(dimension); auto sheet_data_node = worksheet_node.child("sheetData"); if (worksheet_node.child("mergeCells")) { auto merge_cells_node = worksheet_node.child("mergeCells"); auto count = std::stoull(merge_cells_node.attribute("count").value()); for (auto merge_cell_node : merge_cells_node.children("mergeCell")) { ws.merge_cells(range_reference(merge_cell_node.attribute("ref").value())); count--; } if (count != 0) { throw std::runtime_error("mismatch between count and actual number of merged cells"); } } auto &shared_strings = destination_.get_shared_strings(); for (auto row_node : sheet_data_node.children("row")) { auto row_index = static_cast(std::stoull(row_node.attribute("r").value())); if (row_node.attribute("ht")) { ws.get_row_properties(row_index).height = std::stold(row_node.attribute("ht").value()); } std::string span_string = row_node.attribute("spans").value(); 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(std::stoll(span_string.substr(0, colon_index))); max_column = static_cast(std::stoll(span_string.substr(colon_index + 1))); } else { min_column = full_range.get_top_left().get_column_index(); max_column = full_range.get_bottom_right().get_column_index(); } for (column_t i = min_column; i <= max_column; i++) { std::string address = i.column_string() + std::to_string(row_index); pugi::xml_node cell_node; bool cell_found = false; for (auto &check_cell_node : row_node.children("c")) { if (check_cell_node.attribute("r") && check_cell_node.attribute("r").value() == address) { cell_node = check_cell_node; cell_found = true; break; } } if (cell_found) { bool has_value = cell_node.child("v") != nullptr; std::string value_string = has_value ? cell_node.child("v").text().get() : ""; bool has_type = cell_node.attribute("t") != nullptr; std::string type = has_type ? cell_node.attribute("t").value() : ""; bool has_format = cell_node.attribute("s") != nullptr; auto format_id = static_cast(has_format ? std::stoull(cell_node.attribute("s").value()) : 0LL); bool has_formula = cell_node.child("f") != nullptr; bool has_shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && cell_node.child("f").attribute("t").value() == std::string("shared"); auto cell = ws.get_cell(address); if (has_formula && !has_shared_formula && !ws.get_workbook().get_data_only()) { std::string formula = cell_node.child("f").text().get(); cell.set_formula(formula); } if (has_type && type == "inlineStr") // inline string { std::string inline_string = cell_node.child("is").child("t").text().get(); cell.set_value(inline_string); } else if (has_type && type == "s" && !has_formula) // shared string { auto shared_string_index = static_cast(std::stoull(value_string)); auto shared_string = shared_strings.at(shared_string_index); cell.set_value(shared_string); } else if (has_type && type == "b") // boolean { cell.set_value(value_string != "0"); } else if (has_type && type == "str") { cell.set_value(value_string); } else if (has_value && !value_string.empty()) { if (!value_string.empty() && value_string[0] == '#') { cell.set_error(value_string); } else { cell.set_value(std::stold(value_string)); } } if (has_format) { cell.set_format_id(format_id); } } } } auto cols_node = worksheet_node.child("cols"); for (auto col_node : cols_node.children("col")) { auto min = static_cast(std::stoull(col_node.attribute("min").value())); auto max = static_cast(std::stoull(col_node.attribute("max").value())); auto width = std::stold(col_node.attribute("width").value()); bool custom = col_node.attribute("customWidth").value() == std::string("1"); auto column_style = static_cast(col_node.attribute("style") ? std::stoull(col_node.attribute("style").value()) : 0); for (auto column = min; column <= max; column++) { if (!ws.has_column_properties(column)) { ws.add_column_properties(column, column_properties()); } ws.get_column_properties(min).width = width; ws.get_column_properties(min).style = column_style; ws.get_column_properties(min).custom = custom; } } if (worksheet_node.child("autoFilter")) { auto auto_filter_node = worksheet_node.child("autoFilter"); xlnt::range_reference ref(auto_filter_node.attribute("ref").value()); ws.auto_filter(ref); } } // Sheet Relationship Target Parts void xlsx_consumer::read_comments(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("comments"); } void xlsx_consumer::read_drawings(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("wsDr"); } // Unknown Parts void xlsx_consumer::read_unknown_parts(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("Hmm"); } void xlsx_consumer::read_unknown_relationships(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); auto root_node = document.append_child("Relationships"); } } // namespace detail } // namepsace xlnt