| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #pragma once |
|
|
| |
| |
| |
| |
|
|
| #include <mcfp/detail/charconv.hpp> |
|
|
| namespace mcfp |
| { |
|
|
| |
| |
| |
| |
|
|
| template <typename T> |
| using charconv = typename detail::charconv<T>; |
|
|
| |
| |
| |
|
|
| template <template<class...> class Op, class... Args> |
| constexpr inline bool is_detected_v = detail::is_detected_v<Op,Args...>; |
|
|
| |
| |
| |
| |
| |
|
|
| class word_wrapper : public std::vector<std::string_view> |
| { |
| public: |
| word_wrapper(std::string_view text, size_t width) |
| : m_width(width) |
| { |
| std::string_view::size_type line_start = 0, line_end = text.find('\n'); |
| |
| for (;;) |
| { |
| auto line = text.substr(line_start, line_end - line_start); |
| if (line.empty()) |
| this->push_back(line); |
| else |
| { |
| auto lines = wrap_line(line); |
| this->insert(this->end(), lines.begin(), lines.end()); |
| } |
|
|
| if (line_end == std::string_view::npos) |
| break; |
|
|
| line_start = line_end + 1; |
| line_end = text.find('\n', line_start); |
| } |
| } |
|
|
| private: |
| std::vector<std::string_view> wrap_line(std::string_view line) |
| { |
| std::vector<std::string_view> result; |
| std::vector<size_t> offsets = { 0 }; |
|
|
| auto b = line.begin(); |
| while (b != line.end()) |
| { |
| auto e = next_line_break(b, line.end()); |
|
|
| offsets.push_back(e - line.begin()); |
|
|
| b = e; |
| } |
|
|
| size_t count = offsets.size() - 1; |
|
|
| std::vector<size_t> minima(count + 1, std::numeric_limits<size_t>::max()); |
| minima[0] = 0; |
| std::vector<size_t> breaks(count + 1, 0); |
|
|
| for (size_t i = 0; i < count; ++i) |
| { |
| size_t j = i + 1; |
| while (j <= count) |
| { |
| size_t w = offsets[j] - offsets[i]; |
|
|
| if (w > m_width) |
| break; |
|
|
| while (w > 0 and std::isspace(line[offsets[i] + w - 1])) |
| --w; |
|
|
| size_t cost = minima[i]; |
| if (j < count) |
| cost += (m_width - w) * (m_width - w); |
|
|
| if (cost < minima[j]) |
| { |
| minima[j] = cost; |
| breaks[j] = i; |
| } |
|
|
| ++j; |
| } |
| } |
|
|
| size_t j = count; |
| while (j > 0) |
| { |
| size_t i = breaks[j]; |
| result.push_back(line.substr(offsets[i], offsets[j] - offsets[i])); |
| j = i; |
| } |
|
|
| reverse(result.begin(), result.end()); |
|
|
| return result; |
| } |
|
|
| std::string_view::const_iterator next_line_break(std::string_view::const_iterator text, std::string_view::const_iterator end) |
| { |
| if (text == end) |
| return text; |
|
|
| enum LineBreakClass |
| { |
| OP, |
| CL, |
| CP, |
| QU, |
| EX, |
| SY, |
| IS, |
| PR, |
| PO, |
| NU, |
| AL, |
| HY, |
| BA, |
| CM, |
| WJ, |
|
|
| MB, |
| SP, |
| }; |
|
|
| static const LineBreakClass kASCII_LineBreakTable[128] = { |
| CM, CM, CM, CM, CM, CM, CM, CM, |
| CM, BA, MB, MB, MB, SP, CM, CM, |
| CM, CM, CM, CM, CM, CM, CM, CM, |
| CM, CM, CM, CM, CM, CM, CM, CM, |
| SP, EX, QU, AL, PR, PO, AL, QU, |
| OP, CP, AL, PR, IS, HY, IS, SY, |
| NU, NU, NU, NU, NU, NU, NU, NU, |
| NU, NU, IS, IS, AL, AL, AL, EX, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, OP, PR, CP, AL, AL, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, AL, AL, AL, AL, AL, |
| AL, AL, AL, OP, BA, CL, AL, CM |
| }; |
|
|
| enum BreakAction |
| { |
| DBK = 0, |
| IBK, |
| PBK, |
| CIB, |
| CPB |
| }; |
|
|
| static const BreakAction brkTable[15][15] = { |
| |
| { PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, CPB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, IBK, IBK, DBK, DBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { PBK, PBK, PBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { IBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { IBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, IBK, IBK, CIB, PBK }, |
| { DBK, PBK, PBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| { IBK, PBK, PBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, CIB, PBK }, |
| }; |
|
|
| uint8_t ch = *text; |
|
|
| LineBreakClass cls; |
|
|
| if (ch < 128) |
| cls = kASCII_LineBreakTable[ch]; |
| else |
| cls = AL; |
|
|
| if (cls == SP) |
| cls = WJ; |
|
|
| LineBreakClass ncls = cls; |
|
|
| while (++text != end and cls != MB) |
| { |
| ch = *text; |
|
|
| LineBreakClass lcls = ncls; |
|
|
| if (ch < 128) |
| ncls = kASCII_LineBreakTable[ch]; |
| else |
| ncls = AL; |
|
|
| if (ncls == MB) |
| { |
| ++text; |
| break; |
| } |
|
|
| if (ncls == SP) |
| continue; |
|
|
| BreakAction brk = brkTable[cls][ncls]; |
|
|
| if (brk == DBK or (brk == IBK and lcls == SP)) |
| break; |
|
|
| cls = ncls; |
| } |
|
|
| return text; |
| } |
|
|
| size_t m_width; |
| }; |
|
|
| } |
|
|