#include "layout_tree.hxx" #include "include/core/SkFont.h" #include "include/core/SkFontMgr.h" #include "include/core/SkFontTypes.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkTypeface.h" #include "layout/layout.hxx" #include "rapidxml.hpp" #include #include #include #include #include #include static std::uniform_int_distribution random_id(0, std::numeric_limits::max()); static std::random_device dev; static std::mt19937 rng(dev()); inline const auto NODE_TYPE_BOX = "box"; inline const auto NODE_TYPE_ROW = "row"; inline const auto NODE_TYPE_COL = "col"; inline const auto NODE_TYPE_TEXT = "text"; uint64_t __generate_random_node_id() { return random_id(rng); }; Clef::BoxNode::BoxNode() : orientation(Orientation::Vertical) { prev_sibiling = nullptr; next_sibiling = nullptr; parent = nullptr; type = Clef::LayoutTree::NodeType::Box; } Clef::LayoutBound Clef::BoxNode::measure() { switch (orientation) { case Orientation::Vertical: { float max_child_width = -1; float total_height = 0; for (const auto &child : children) { const auto bound = child->measure(); if (bound.width > max_child_width) { max_child_width = bound.width; } total_height += bound.height; } return {0, 0, max_child_width, total_height}; } case Orientation::Horizontal: { float max_child_height = -1; float total_width = 0; for (const auto &child : children) { const auto bound = child->measure(); if (bound.height > max_child_height) { max_child_height = bound.height; } total_width += bound.width; } return {0, 0, total_width, max_child_height}; } } } Clef::TextNode::TextNode(const char *content, SkFont font) : content(content), font(font) { prev_sibiling = nullptr; next_sibiling = nullptr; parent = nullptr; type = Clef::LayoutTree::NodeType::Text; } Clef::LayoutBound Clef::TextNode::measure() { SkRect rect; font.measureText(content.c_str(), content.size(), SkTextEncoding::kUTF8, &rect); return {0, 0, rect.width(), rect.height()}; } Clef::LayoutTree::LayoutTree(Clef::LayoutTree::Node *root) : root(root) {} Clef::LayoutTree::Node * Clef::LayoutTree::Node::from_xml_node(const RenderingContext &ctx, rapidxml::xml_node *node) { if (std::strncmp(NODE_TYPE_ROW, node->name(), 3) == 0 || std::strncmp(NODE_TYPE_COL, node->name(), 3) == 0) { auto container_node = new BoxNode(); container_node->id = __generate_random_node_id(); if (std::strncmp(NODE_TYPE_ROW, node->name(), 3) == 0) { container_node->orientation = Orientation::Horizontal; } else { container_node->orientation = Orientation::Vertical; } auto current_node = node->first_node(); Node *last_node = nullptr; while (current_node) { auto n = Node::from_xml_node(ctx, current_node); if (!n) { return nullptr; } if (last_node) { n->prev_sibiling = last_node; last_node->next_sibiling = n; } n->parent = container_node; container_node->children.emplace_back(n); last_node = n; current_node = current_node->next_sibling(); } return container_node; } if (std::strncmp(NODE_TYPE_TEXT, node->name(), 4) == 0) { const auto content = node->value(); if (!content) { return nullptr; } auto typeface = ctx.font_mgr->matchFamilyStyle("Inter", SkFontStyle::Normal()); SkFont f(typeface, 20); auto n = new TextNode(content, f); n->id = __generate_random_node_id(); std::cout << "assigned id " << n->id << std::endl; return n; } return nullptr; } Clef::LayoutTree Clef::LayoutTree::from_xml(const RenderingContext &ctx, const rapidxml::xml_document<> &doc) { Clef::LayoutTree::Node *root; auto xml_root = doc.first_node(); if (!xml_root) { throw std::runtime_error("provided layout file appears to be empty."); } root = Node::from_xml_node(ctx, xml_root); if (!root) { throw std::runtime_error("failed to parse cml"); } std::cout << "cml parsed successfully" << std::endl; return root; }