diff --git a/src/odr/internal/ooxml/ooxml_util.hpp b/src/odr/internal/ooxml/ooxml_util.hpp index 265281e8..deabd79b 100644 --- a/src/odr/internal/ooxml/ooxml_util.hpp +++ b/src/odr/internal/ooxml/ooxml_util.hpp @@ -48,6 +48,8 @@ std::optional read_border_node(pugi::xml_node); std::string read_text_property(pugi::xml_node); +using Relations = std::unordered_map; + std::unordered_map parse_relationships(const pugi::xml_document &relations); std::unordered_map diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp index 28838590..960e1284 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -15,41 +14,46 @@ Document::Document(std::shared_ptr filesystem) : common::TemplateDocument(FileType::office_open_xml_workbook, DocumentType::spreadsheet, std::move(filesystem)) { - m_workbook_xml = util::xml::parse(*m_filesystem, "xl/workbook.xml"); - m_styles_xml = util::xml::parse(*m_filesystem, "xl/styles.xml"); + auto workbook_path = common::Path("xl/workbook.xml"); + auto [workbook_xml, workbook_relations] = parse_xml_(workbook_path); + auto [styles_xml, _] = parse_xml_("xl/styles.xml"); - for (const auto &relationships : - parse_relationships(*m_filesystem, "xl/workbook.xml")) { - auto sheet_path = common::Path("xl").join(relationships.second); - auto sheet_xml = util::xml::parse(*m_filesystem, sheet_path); + for (pugi::xml_node sheet_node : + workbook_xml.document_element().child("sheets").children("sheet")) { + const char *id = sheet_node.attribute("r:id").value(); + common::Path sheet_path = + workbook_path.parent().join(workbook_relations.at(id)); + auto [sheet_xml, sheet_relationships] = parse_xml_(sheet_path); if (auto drawing = sheet_xml.document_element().child("drawing")) { - auto sheet_relationships = parse_relationships(*m_filesystem, sheet_path); - auto drawing_path = - common::Path("xl/worksheets") - .join(sheet_relationships.at(drawing.attribute("r:id").value())); - auto drawing_xml = util::xml::parse(*m_filesystem, drawing_path); - - m_sheets[relationships.first].sheet_path = std::move(drawing_path); - m_sheets[relationships.first].drawing_xml = std::move(drawing_xml); + auto drawing_path = sheet_path.parent().join( + sheet_relationships.at(drawing.attribute("r:id").value())); + parse_xml_(drawing_path); } - - m_sheets[relationships.first].sheet_path = std::move(sheet_path); - m_sheets[relationships.first].sheet_xml = std::move(sheet_xml); } if (m_filesystem->exists("xl/sharedStrings.xml")) { - m_shared_strings_xml = - util::xml::parse(*m_filesystem, "xl/sharedStrings.xml"); - } + auto [shared_strings_xml, _] = parse_xml_("xl/sharedStrings.xml"); - for (auto shared_string : m_shared_strings_xml.document_element()) { - m_shared_strings.push_back(shared_string); + for (auto shared_string : shared_strings_xml.document_element()) { + m_shared_strings.push_back(shared_string); + } } - m_style_registry = StyleRegistry(m_styles_xml.document_element()); + m_style_registry = StyleRegistry(styles_xml.document_element()); - m_root_element = parse_tree(*this, m_workbook_xml.document_element()); + m_root_element = parse_tree(*this, workbook_xml.document_element(), + workbook_path, workbook_relations); +} + +std::pair +Document::parse_xml_(const common::Path &path) { + pugi::xml_document document = util::xml::parse(*m_filesystem, path); + Relations relations = parse_relationships(*m_filesystem, path); + + auto result = m_xml.emplace( + path, std::make_pair(std::move(document), std::move(relations))); + return {result.first->second.first, result.first->second.second}; } bool Document::is_editable() const noexcept { return false; } @@ -67,11 +71,9 @@ void Document::save(const common::Path & /*path*/, throw UnsupportedOperation(); } -pugi::xml_node Document::get_sheet_root(const std::string &ref) const { - if (auto it = m_sheets.find(ref); it != std::end(m_sheets)) { - return it->second.sheet_xml.document_element(); - } - return {}; +std::pair +Document::get_xml(const common::Path &path) const { + return m_xml.at(path); } pugi::xml_node Document::get_shared_string(std::size_t index) const { diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.hpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.hpp index 3f17dfd6..999952f2 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.hpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.hpp @@ -12,6 +12,7 @@ #include #include +#include "odr/internal/ooxml/ooxml_util.hpp" #include namespace odr::internal::ooxml::spreadsheet { @@ -26,26 +27,20 @@ class Document final : public common::TemplateDocument { void save(const common::Path &path) const final; void save(const common::Path &path, const char *password) const final; - pugi::xml_node get_sheet_root(const std::string &ref) const; + std::pair + get_xml(const common::Path &) const; pugi::xml_node get_shared_string(std::size_t index) const; private: - struct Sheet final { - common::Path sheet_path; - pugi::xml_document sheet_xml; - std::optional drawing_path; - pugi::xml_document drawing_xml; - }; - - pugi::xml_document m_workbook_xml; - pugi::xml_document m_styles_xml; - std::unordered_map m_sheets; - std::unordered_map m_drawings_xml; - pugi::xml_document m_shared_strings_xml; + std::unordered_map> + m_xml; StyleRegistry m_style_registry; std::vector m_shared_strings; + std::pair + parse_xml_(const common::Path &path); + friend class Element; }; diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.cpp index 33acdf71..a44b09e0 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.cpp @@ -13,7 +13,9 @@ namespace odr::internal::ooxml::spreadsheet { -Element::Element(pugi::xml_node node) : m_node{node} { +Element::Element(pugi::xml_node node, common::Path /*document_path*/, + const Relations & /*document_relations*/) + : m_node{node} { if (!node) { // TODO log error throw std::runtime_error("node not set"); @@ -51,19 +53,28 @@ Element::style_registry_(const abstract::Document *document) { return &document_(document)->m_style_registry; } -pugi::xml_node Element::sheet_(const abstract::Document *document, - const std::string &id) { - return document_(document)->m_sheets.at(id).sheet_xml.document_element(); +const common::Path & +Element::document_path_(const abstract::Document *document) const { + return dynamic_cast(m_parent)->document_path_(document); } -pugi::xml_node Element::drawing_(const abstract::Document *document, - const std::string &id) { - return document_(document)->m_sheets.at(id).drawing_xml.document_element(); +const Relations & +Element::document_relations_(const abstract::Document *document) const { + return dynamic_cast(m_parent)->document_relations_(document); } -const std::vector & -Element::shared_strings_(const abstract::Document *document) { - return document_(document)->m_shared_strings; +Root::Root(pugi::xml_node node, common::Path document_path, + const Relations &document_relations) + : DefaultElement(node, document_path, document_relations), + m_document_path{std::move(document_path)}, m_document_relations{ + document_relations} {} + +const common::Path &Root::document_path_(const abstract::Document *) const { + return m_document_path; +} + +const Relations &Root::document_relations_(const abstract::Document *) const { + return m_document_relations; } void SheetIndex::init_column(std::uint32_t /*min*/, std::uint32_t max, @@ -81,7 +92,7 @@ void SheetIndex::init_cell(std::uint32_t column, std::uint32_t row, } pugi::xml_node SheetIndex::column(std::uint32_t column) const { - if (auto it = util::map::lookup_greater_than(columns, column); + if (auto it = util::map::lookup_greater_or_equals(columns, column); it != std::end(columns)) { return it->second; } @@ -89,7 +100,7 @@ pugi::xml_node SheetIndex::column(std::uint32_t column) const { } pugi::xml_node SheetIndex::row(std::uint32_t row) const { - if (auto it = util::map::lookup_greater_than(rows, row); + if (auto it = util::map::lookup_greater_or_equals(rows, row); it != std::end(rows)) { return it->second.row; } @@ -97,10 +108,10 @@ pugi::xml_node SheetIndex::row(std::uint32_t row) const { } pugi::xml_node SheetIndex::cell(std::uint32_t column, std::uint32_t row) const { - if (auto row_it = util::map::lookup_greater_than(rows, row); + if (auto row_it = util::map::lookup_greater_or_equals(rows, row); row_it != std::end(rows)) { const auto &cells = row_it->second.cells; - if (auto cell_it = util::map::lookup_greater_than(cells, column); + if (auto cell_it = util::map::lookup_greater_or_equals(cells, column); cell_it != std::end(cells)) { return cell_it->second; } @@ -108,6 +119,20 @@ pugi::xml_node SheetIndex::cell(std::uint32_t column, std::uint32_t row) const { return {}; } +Sheet::Sheet(pugi::xml_node node, common::Path document_path, + const Relations &document_relations) + : Element(node, document_path, document_relations), + m_document_path{std::move(document_path)}, m_document_relations{ + document_relations} {} + +const common::Path &Sheet::document_path_(const abstract::Document *) const { + return m_document_path; +} + +const Relations &Sheet::document_relations_(const abstract::Document *) const { + return m_document_relations; +} + std::string Sheet::name(const abstract::Document *) const { return m_node.attribute("name").value(); } @@ -144,7 +169,7 @@ TableColumnStyle Sheet::column_style(const abstract::Document *, std::uint32_t column) const { TableColumnStyle result; pugi::xml_node column_node = m_index.column(column); - if (auto width = column_node.attribute("width")) { + if (pugi::xml_attribute width = column_node.attribute("width")) { result.width = Measure(width.as_float(), DynamicUnit("ch")); } return result; @@ -154,16 +179,24 @@ TableRowStyle Sheet::row_style(const abstract::Document *, std::uint32_t row) const { TableRowStyle result; pugi::xml_node row_node = m_index.row(row); - if (auto height = row_node.attribute("ht")) { + if (pugi::xml_attribute height = row_node.attribute("ht")) { result.height = Measure(height.as_float(), DynamicUnit("pt")); } return result; } -TableCellStyle Sheet::cell_style(const abstract::Document *, - std::uint32_t /*column*/, - std::uint32_t /*row*/) const { - return TableCellStyle(); // TODO +TableCellStyle Sheet::cell_style(const abstract::Document *document, + std::uint32_t column, + std::uint32_t row) const { + TableCellStyle result; + if (pugi::xml_attribute style_attr = m_index.cell(column, row).attribute("s"); + style_attr) { + std::uint32_t style_id = style_attr.as_uint(); + common::ResolvedStyle style = + style_registry_(document)->cell_style(style_id); + result.override(style.table_cell_style); + } + return result; } void Sheet::init_column_(std::uint32_t min, std::uint32_t max, @@ -190,12 +223,15 @@ void Sheet::init_dimensions_(TableDimensions dimensions) { m_index.dimensions = dimensions; } -pugi::xml_node Sheet::sheet_node_(const abstract::Document *document) const { - return sheet_(document, m_node.attribute("r:id").value()); -} - -pugi::xml_node Sheet::drawing_node_(const abstract::Document *document) const { - return drawing_(document, m_node.attribute("r:id").value()); +void Sheet::append_shape_(Element *shape) { + shape->m_previous_sibling = m_last_shape; + shape->m_parent = this; + if (m_last_shape == nullptr) { + m_first_shape = shape; + } else { + m_last_shape->m_next_sibling = shape; + } + m_last_shape = shape; } bool SheetCell::is_covered(const abstract::Document *) const { @@ -226,10 +262,13 @@ TextStyle Span::style(const abstract::Document *document) const { return intermediate_style(document).text_style; } -Text::Text(pugi::xml_node node) : Text(node, node) {} +Text::Text(pugi::xml_node node, common::Path document_path, + const Relations &document_relations) + : Text(node, node, document_path, document_relations) {} -Text::Text(pugi::xml_node first, pugi::xml_node last) - : Element(first), m_last{last} {} +Text::Text(pugi::xml_node first, pugi::xml_node last, + common::Path document_path, const Relations &document_relations) + : Element(first, document_path, document_relations), m_last{last} {} std::string Text::content(const abstract::Document *) const { std::string result; @@ -258,49 +297,65 @@ std::string Text::text_(const pugi::xml_node node) { return ""; } +Frame::Frame(pugi::xml_node node, common::Path document_path, + const Relations &document_relations) + : Element(node, document_path, document_relations), + m_document_path{std::move(document_path)}, m_document_relations{ + document_relations} {} + +const common::Path &Frame::document_path_(const abstract::Document *) const { + return m_document_path; +} + +const Relations &Frame::document_relations_(const abstract::Document *) const { + return m_document_relations; +} + AnchorType Frame::anchor_type(const abstract::Document *) const { return AnchorType::at_page; } std::optional Frame::x(const abstract::Document *) const { - if (auto x = read_emus_attribute(m_node.child("xdr:pic") - .child("xdr:spPr") - .child("a:xfrm") - .child("a:off") - .attribute("x"))) { + if (std::optional x = read_emus_attribute(m_node.child("xdr:pic") + .child("xdr:spPr") + .child("a:xfrm") + .child("a:off") + .attribute("x"))) { return x->to_string(); } return {}; } std::optional Frame::y(const abstract::Document *) const { - if (auto y = read_emus_attribute(m_node.child("xdr:pic") - .child("xdr:spPr") - .child("a:xfrm") - .child("a:off") - .attribute("y"))) { + if (std::optional y = read_emus_attribute(m_node.child("xdr:pic") + .child("xdr:spPr") + .child("a:xfrm") + .child("a:off") + .attribute("y"))) { return y->to_string(); } return {}; } std::optional Frame::width(const abstract::Document *) const { - if (auto width = read_emus_attribute(m_node.child("xdr:pic") - .child("xdr:spPr") - .child("a:xfrm") - .child("a:ext") - .attribute("cx"))) { + if (std::optional width = + read_emus_attribute(m_node.child("xdr:pic") + .child("xdr:spPr") + .child("a:xfrm") + .child("a:ext") + .attribute("cx"))) { return width->to_string(); } return {}; } std::optional Frame::height(const abstract::Document *) const { - if (auto height = read_emus_attribute(m_node.child("xdr:pic") - .child("xdr:spPr") - .child("a:xfrm") - .child("a:ext") - .attribute("cy"))) { + if (std::optional height = + read_emus_attribute(m_node.child("xdr:pic") + .child("xdr:spPr") + .child("a:xfrm") + .child("a:ext") + .attribute("cy"))) { return height->to_string(); } return {}; @@ -313,8 +368,8 @@ std::optional Frame::z_index(const abstract::Document *) const { GraphicStyle Frame::style(const abstract::Document *) const { return {}; } bool ImageElement::is_internal(const abstract::Document *document) const { - auto doc = document_(document); - if (!doc || !doc->files()) { + const Document *doc = document_(document); + if (doc == nullptr || !doc->files()) { return false; } try { @@ -326,20 +381,19 @@ bool ImageElement::is_internal(const abstract::Document *document) const { std::optional ImageElement::file(const abstract::Document *document) const { - auto doc = document_(document); - if (!doc || !is_internal(document)) { + const Document *doc = document_(document); + if (doc == nullptr || !is_internal(document)) { return {}; } return File(doc->files()->open(href(document))); } -std::string ImageElement::href(const abstract::Document *) const { - if (auto ref = m_node.attribute("r:embed")) { - /* TODO +std::string ImageElement::href(const abstract::Document *document) const { + if (pugi::xml_attribute ref = m_node.attribute("r:embed")) { auto relations = document_relations_(document); if (auto rel = relations.find(ref.value()); rel != std::end(relations)) { - return common::Path("word").join(rel->second).string(); - }*/ + return document_path_(document).parent().join(rel->second).string(); + } } return ""; // TODO } diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.hpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.hpp index 45876641..bfc82a5e 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.hpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element.hpp @@ -10,7 +10,9 @@ #include #include +#include "odr/internal/common/path.hpp" #include "odr/internal/common/table_position.hpp" +#include "odr/internal/ooxml/ooxml_util.hpp" #include #include @@ -22,7 +24,8 @@ class SheetCell; class Element : public common::Element { public: - explicit Element(pugi::xml_node node); + Element(pugi::xml_node node, common::Path document_path, + const Relations &document_relations); [[nodiscard]] virtual common::ResolvedStyle partial_style(const abstract::Document *) const; @@ -31,17 +34,16 @@ class Element : public common::Element { [[nodiscard]] bool is_editable(const abstract::Document *) const override; + [[nodiscard]] virtual const common::Path & + document_path_(const abstract::Document *) const; + [[nodiscard]] virtual const Relations & + document_relations_(const abstract::Document *) const; + protected: pugi::xml_node m_node; static const Document *document_(const abstract::Document *); static const StyleRegistry *style_registry_(const abstract::Document *); - static pugi::xml_node sheet_(const abstract::Document *, - const std::string &id); - static pugi::xml_node drawing_(const abstract::Document *, - const std::string &id); - static const std::vector & - shared_strings_(const abstract::Document *); }; template class DefaultElement : public Element { @@ -55,7 +57,17 @@ template class DefaultElement : public Element { class Root final : public DefaultElement { public: - using DefaultElement::DefaultElement; + Root(pugi::xml_node node, common::Path document_path, + const Relations &document_relations); + + [[nodiscard]] const common::Path & + document_path_(const abstract::Document *) const final; + [[nodiscard]] const Relations & + document_relations_(const abstract::Document *) const final; + +private: + common::Path m_document_path; + const Relations &m_document_relations; }; struct SheetIndex final { @@ -82,7 +94,8 @@ struct SheetIndex final { class Sheet final : public Element, public abstract::Sheet { public: - using Element::Element; + Sheet(pugi::xml_node node, common::Path document_path, + const Relations &document_relations); [[nodiscard]] std::string name(const abstract::Document *) const final; @@ -116,15 +129,23 @@ class Sheet final : public Element, public abstract::Sheet { void init_cell_element_(std::uint32_t column, std::uint32_t row, SheetCell *element); void init_dimensions_(TableDimensions dimensions); + void append_shape_(Element *shape); + +protected: + [[nodiscard]] const common::Path & + document_path_(const abstract::Document *) const final; + [[nodiscard]] const Relations & + document_relations_(const abstract::Document *) const final; private: + common::Path m_document_path; + const Relations &m_document_relations; + SheetIndex m_index; std::unordered_map m_cells; Element *m_first_shape{nullptr}; - - pugi::xml_node sheet_node_(const abstract::Document *) const; - pugi::xml_node drawing_node_(const abstract::Document *) const; + Element *m_last_shape{nullptr}; }; class SheetCell final : public Element, public abstract::SheetCell { @@ -150,8 +171,10 @@ class Span final : public Element, public abstract::Span { class Text final : public Element, public abstract::Text { public: - explicit Text(pugi::xml_node node); - Text(pugi::xml_node first, pugi::xml_node last); + explicit Text(pugi::xml_node node, common::Path document_path, + const Relations &document_relations); + Text(pugi::xml_node first, pugi::xml_node last, common::Path document_path, + const Relations &document_relations); [[nodiscard]] std::string content(const abstract::Document *) const final; @@ -167,7 +190,8 @@ class Text final : public Element, public abstract::Text { class Frame final : public Element, public abstract::Frame { public: - using Element::Element; + Frame(pugi::xml_node node, common::Path document_path, + const Relations &document_relations); [[nodiscard]] AnchorType anchor_type(const abstract::Document *) const final; @@ -184,6 +208,15 @@ class Frame final : public Element, public abstract::Frame { z_index(const abstract::Document *) const final; [[nodiscard]] GraphicStyle style(const abstract::Document *) const final; + + [[nodiscard]] const common::Path & + document_path_(const abstract::Document *) const final; + [[nodiscard]] const Relations & + document_relations_(const abstract::Document *) const final; + +private: + common::Path m_document_path; + const Relations &m_document_relations; }; class ImageElement final : public Element, public abstract::Image { diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp index be353461..77bbe4fd 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp @@ -1,9 +1,8 @@ #include +#include #include -#include "odr/internal/common/table_range.hpp" -#include #include namespace odr::internal::ooxml::spreadsheet { @@ -12,18 +11,26 @@ namespace { template std::tuple -parse_element_tree(Document &document, pugi::xml_node node, args_t &&...args); +parse_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations); template <> std::tuple -parse_element_tree(Document &document, pugi::xml_node node); +parse_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations); std::tuple -parse_any_element_tree(Document &document, pugi::xml_node node); +parse_any_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations); void parse_element_children(Document &document, Element *element, - pugi::xml_node node) { - for (auto child_node = node.first_child(); child_node;) { - auto [child, next_sibling] = parse_any_element_tree(document, child_node); + pugi::xml_node node, common::Path document_path, + const Relations &document_relations) { + for (pugi::xml_node child_node = node.first_child(); child_node;) { + auto [child, next_sibling] = parse_any_element_tree( + document, child_node, document_path, document_relations); if (child == nullptr) { child_node = child_node.next_sibling(); } else { @@ -34,55 +41,78 @@ void parse_element_children(Document &document, Element *element, } void parse_element_children(Document &document, Root *element, - pugi::xml_node node) { - for (auto child_node : node.child("sheets").children("sheet")) { + pugi::xml_node node, common::Path document_path, + const Relations &document_relations) { + for (pugi::xml_node child_node : node.child("sheets").children("sheet")) { const char *id = child_node.attribute("r:id").value(); - auto sheet_node = document.get_sheet_root(id); - auto [sheet, _] = parse_element_tree(document, sheet_node); + common::Path sheet_path = + document_path.parent().join(document_relations.at(id)); + auto [sheet_xml, sheet_relations] = document.get_xml(sheet_path); + auto [sheet, _] = parse_element_tree( + document, sheet_xml.document_element(), sheet_path, sheet_relations); element->append_child_(sheet); } } void parse_element_children(Document &document, SheetCell *element, - pugi::xml_node node) { - if (auto type_attr = node.attribute("t"); + pugi::xml_node node, common::Path document_path, + const Relations &document_relations) { + if (pugi::xml_attribute type_attr = node.attribute("t"); type_attr.value() == std::string("s")) { pugi::xml_node v_node = node.child("v"); std::size_t ref = v_node.first_child().text().as_ullong(); pugi::xml_node shared_node = document.get_shared_string(ref); parse_element_children(document, dynamic_cast(element), - shared_node); + shared_node, document_path, document_relations); return; } - parse_element_children(document, dynamic_cast(element), node); + parse_element_children(document, dynamic_cast(element), node, + document_path, document_relations); +} + +void parse_element_children(Document &document, Frame *element, + pugi::xml_node node, common::Path document_path, + const Relations &document_relations) { + if (pugi::xml_node image_node = + node.child("xdr:pic").child("xdr:blipFill").child("a:blip")) { + auto [image, _] = parse_element_tree( + document, image_node, document_path, document_relations); + element->append_child_(image); + } } template std::tuple -parse_element_tree(Document &document, pugi::xml_node node, args_t &&...args) { +parse_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations) { if (!node) { return std::make_tuple(nullptr, pugi::xml_node()); } auto element_unique = - std::make_unique(node, std::forward(args)...); + std::make_unique(node, document_path, document_relations); auto element = element_unique.get(); document.register_element_(std::move(element_unique)); - parse_element_children(document, element, node); + parse_element_children(document, element, node, document_path, + document_relations); return std::make_tuple(element, node.next_sibling()); } template <> std::tuple -parse_element_tree(Document &document, pugi::xml_node node) { +parse_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations) { if (!node) { return std::make_tuple(nullptr, pugi::xml_node()); } - auto element_unique = std::make_unique(node); + auto element_unique = + std::make_unique(node, document_path, document_relations); auto element = element_unique.get(); document.register_element_(std::move(element_unique)); @@ -100,7 +130,8 @@ parse_element_tree(Document &document, pugi::xml_node node) { auto position = common::TablePosition(cell_node.attribute("r").value()); element->init_cell_(position.column(), position.row(), cell_node); - auto [cell, _] = parse_element_tree(document, cell_node); + auto [cell, _] = parse_element_tree( + document, cell_node, document_path, document_relations); element->init_cell_element_(position.column(), position.row(), cell); } } @@ -118,6 +149,22 @@ parse_element_tree(Document &document, pugi::xml_node node) { TableDimensions(position_to.row() + 1, position_to.column() + 1)); } + if (pugi::xml_node drawing_node = node.child("drawing")) { + const char *id = drawing_node.attribute("r:id").value(); + common::Path drawing_path = + document_path.parent().join(document_relations.at(id)); + auto [drawing_xml, drawing_relations] = document.get_xml(drawing_path); + + for (pugi::xml_node shape_node : + drawing_xml.document_element().children()) { + auto [shape, _] = parse_any_element_tree(document, shape_node, + drawing_path, drawing_relations); + if (shape != nullptr) { + element->append_shape_(shape); + } + } + } + return std::make_tuple(element, node.next_sibling()); } @@ -140,7 +187,9 @@ bool is_text_node(const pugi::xml_node node) { template <> std::tuple -parse_element_tree(Document &document, pugi::xml_node first) { +parse_element_tree(Document &document, pugi::xml_node first, + common::Path document_path, + const Relations &document_relations) { if (!first) { return std::make_tuple(nullptr, pugi::xml_node()); } @@ -149,7 +198,8 @@ parse_element_tree(Document &document, pugi::xml_node first) { for (; is_text_node(last.next_sibling()); last = last.next_sibling()) { } - auto element_unique = std::make_unique(first, last); + auto element_unique = + std::make_unique(first, last, document_path, document_relations); auto element = element_unique.get(); document.register_element_(std::move(element_unique)); @@ -157,9 +207,12 @@ parse_element_tree(Document &document, pugi::xml_node first) { } std::tuple -parse_any_element_tree(Document &document, pugi::xml_node node) { +parse_any_element_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations) { using Parser = std::function( - Document & document, pugi::xml_node node)>; + Document & document, pugi::xml_node node, common::Path document_path, + const Relations &document_relations)>; static std::unordered_map parser_table{ {"workbook", parse_element_tree}, @@ -172,7 +225,8 @@ parse_any_element_tree(Document &document, pugi::xml_node node) { if (auto constructor_it = parser_table.find(node.name()); constructor_it != std::end(parser_table)) { - return constructor_it->second(document, node); + return constructor_it->second(document, node, document_path, + document_relations); } return std::make_tuple(nullptr, pugi::xml_node()); @@ -184,10 +238,12 @@ parse_any_element_tree(Document &document, pugi::xml_node node) { namespace odr::internal::ooxml { -spreadsheet::Element *spreadsheet::parse_tree(Document &document, - pugi::xml_node node) { - std::vector> store; - auto [root_id, _] = parse_any_element_tree(document, node); +spreadsheet::Element * +spreadsheet::parse_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations) { + auto [root_id, _] = parse_any_element_tree( + document, node, std::move(document_path), document_relations); return root_id; } diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.hpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.hpp index 9a6d11d2..09fc04dc 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.hpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.hpp @@ -3,6 +3,9 @@ #include +#include +#include + #include #include #include @@ -13,7 +16,9 @@ namespace odr::internal::ooxml::spreadsheet { class Document; class Element; -Element *parse_tree(Document &document, pugi::xml_node node); +Element *parse_tree(Document &document, pugi::xml_node node, + common::Path document_path, + const Relations &document_relations); } // namespace odr::internal::ooxml::spreadsheet