#include "core/split.h" #include "box/box_group.h" #include "utils/log.h" using namespace std; using namespace microtex; #ifdef HAVE_LOG static void printBox(const sptr& b, int dep, vector& lines, int max = 0) { logv("%-4d", dep); if (lines.size() < dep + 1) lines.resize(dep + 1, false); for (int i = 0; i < dep - 1; i++) { logv(lines[i] ? " " : " │ "); } if (dep > 0) { logv(lines[dep - 1] ? " └──" : " ├──"); } if (b == nullptr) { logv(ANSI_COLOR_RED " NULL\n" ANSI_RESET); return; } const vector>& children = b->descendants(); const auto size = children.size(); const auto& name = b->name(); const char* fmt = (size > 0 ? ANSI_COLOR_CYAN " %-*s " ANSI_RESET : " %-*s "); logv(fmt, size > 0 ? name.size() : max, name.c_str()); // show metrics and additional info logv( "[%g, (%g + %g) = %g, %g] %s\n", b->_width, b->_height, b->_depth, b->vlen(), b->_shift, b->toString().c_str() ); if (size == 0) return; size_t limit = 0; auto hb = std::dynamic_pointer_cast(b); for (const auto& x : children) limit = std::max(limit, x->name().size()); for (size_t i = 0; i < size; i++) { lines[dep] = i == size - 1; if(hb) { if(std::find(hb->_breakPositions.begin(), hb->_breakPositions.end(), i)!=hb->_breakPositions.end()) { logv("br "); } else { logv("nb "); } } else { logv("nh "); } printBox(children[i], dep + 1, lines, limit); } } void microtex::printBox(const sptr& box) { vector lines; ::printBox(box, 0, lines, box->name().size()); logv("\n"); } #endif // HAVE_LOG std::pair> BoxSplitter::split(const sptr& b, float width, float lineSpace) { auto h = dynamic_pointer_cast(b); sptr box; if (h != nullptr) { auto [splitted, box] = split(h, width, lineSpace); #ifdef HAVE_LOG if (box != b) { logv("[BEFORE SPLIT]:\n"); printBox(b); logv("[AFTER SPLIT]:\n"); printBox(box); } else { logv("[BOX TREE (NO SPLIT HAPPENED)]:\n"); printBox(box); } #endif return {splitted, box}; } else { auto v = dynamic_pointer_cast(b); if (v != nullptr) { for(sptr& child: v->_children) { float old_height = child->_height + child->_depth; auto [splitted, box] = split(child, width, lineSpace); if(splitted) { float new_height = box->_height + box->_depth; #ifdef HAVE_LOG logv(std::string("The above has split a child Box, was "+std::to_string(old_height)+", vbox is now height "+std::to_string(new_height)+".\n").c_str()); #endif child = box; v->_depth += new_height - old_height; } } } else { #ifdef HAVE_LOG logv("Top level is also not a VBox.\n"); #endif } } #ifdef HAVE_LOG logv("[BOX TREE (NO SPLIT ATTEMPTED)]:\n"); printBox(b); #endif return {false, b}; } std::pair> BoxSplitter::split(const sptr& hb, float width, float lineSpace) { if (width == 0 || hb->_width <= width) return {false, hb}; auto* vbox = new VBox(); sptr first, second; stack positions; sptr hbox = hb; bool splitted = false; while (hbox->_width > width && canBreak(positions, hbox, width) != hbox->_width) { Position pos = positions.top(); positions.pop(); auto hboxes = pos._box->split(pos._index - 1); first = hboxes.first; second = hboxes.second; while (!positions.empty()) { pos = positions.top(); positions.pop(); hboxes = pos._box->splitRemove(pos._index); hboxes.first->add(first); hboxes.second->add(0, second); first = hboxes.first; second = hboxes.second; } vbox->add(first, lineSpace); splitted = true; hbox = second; } if (second != nullptr) { vbox->add(second, lineSpace); return {splitted, sptr(vbox)}; } return {splitted, hbox}; } float BoxSplitter::canBreak(stack& s, const sptr& hbox, const float width) { const vector>& children = hbox->_children; const int count = children.size(); // Cumulative width auto* cumWidth = new float[count + 1](); cumWidth[0] = 0; for (int i = 0; i < count; i++) { auto box = children[i]; cumWidth[i + 1] = cumWidth[i] + box->_width; if (cumWidth[i + 1] <= width) continue; int pos = getBreakPosition(hbox, i); auto h = dynamic_pointer_cast(box); if (h != nullptr) { stack sub; float w = canBreak(sub, h, width - cumWidth[i]); if (w != box->_width && (cumWidth[i] + w <= width || pos == -1)) { s.push(Position(i - 1, hbox)); // add to stack vector p; while (!sub.empty()) { p.push_back(sub.top()); sub.pop(); } for (auto it = p.rbegin(); it != p.rend(); it++) s.push(*it); // release cum-width float x = cumWidth[i] + w; delete[] cumWidth; return x; } } if (pos != -1) { s.push(Position(pos, hbox)); float x = cumWidth[pos]; delete[] cumWidth; return x; } } delete[] cumWidth; return hbox->_width; } int BoxSplitter::getBreakPosition(const sptr& hb, int i) { // return i; if (hb->_breakPositions.empty()) return -1; if (hb->_breakPositions.size() == 1 && hb->_breakPositions[0] <= i) { return hb->_breakPositions[0]; } size_t pos = 0; for (; pos < hb->_breakPositions.size(); pos++) { if (hb->_breakPositions[pos] > i) { if (pos == 0) return -1; return hb->_breakPositions[pos - 1]; } } return hb->_breakPositions[pos - 1]; }