#include <cstring>
#include "Common/Render/DrawBuffer.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Data/Text/WrapText.h"
bool WordWrapper::IsCJK(uint32_t c) {
if (c < 0x1000) {
return false;
}
bool result = (c >= 0x1100 && c <= 0x11FF);
result = result || (c >= 0x2E80 && c <= 0x2FFF);
#if 0
result = result || (c >= 0x3040 && c <= 0x31FF);
result = result || (c >= 0x3200 && c <= 0x32FF);
result = result || (c >= 0x3300 && c <= 0x33FF);
result = result || (c >= 0x3400 && c <= 0x4DB5);
#else
result = result || (c >= 0x3040 && c <= 0x4DB5);
#endif
result = result || (c >= 0x4E00 && c <= 0x9FBB);
result = result || (c >= 0xAC00 && c <= 0xD7AF);
result = result || (c >= 0xF900 && c <= 0xFAD9);
result = result || (c >= 0x20000 && c <= 0x2A6D6);
result = result || (c >= 0x2F800 && c <= 0x2FA1D);
return result;
}
bool WordWrapper::IsPunctuation(uint32_t c) {
switch (c) {
case ',':
case '.':
case ':':
case '!':
case ')':
case '?':
case 0x00AD:
case 0x3001:
case 0x3002:
case 0x06D4:
case 0xFF01:
case 0xFF09:
case 0xFF1F:
return true;
default:
return false;
}
}
bool WordWrapper::IsSpace(uint32_t c) {
switch (c) {
case '\t':
case ' ':
case 0x2002:
case 0x2003:
case 0x3000:
return true;
default:
return false;
}
}
bool WordWrapper::IsShy(uint32_t c) {
return c == 0x00AD;
}
std::string WordWrapper::Wrapped() {
if (out_.empty()) {
Wrap();
}
return out_;
}
bool WordWrapper::WrapBeforeWord() {
if (flags_ & FLAG_WRAP_TEXT) {
if (x_ + wordWidth_ > maxW_ && !out_.empty()) {
if (IsShy(lastChar_)) {
out_[out_.size() - 2] = '-';
out_[out_.size() - 1] = '\n';
} else {
out_ += "\n";
}
lastChar_ = '\n';
lastLineStart_ = out_.size();
x_ = 0.0f;
forceEarlyWrap_ = false;
return true;
}
}
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
const bool hasEllipsis = out_.size() > 3 && out_.substr(out_.size() - 3) == "...";
if (x_ + wordWidth_ > maxW_ && !hasEllipsis) {
AddEllipsis();
skipNextWord_ = true;
if ((flags_ & FLAG_WRAP_TEXT) == 0) {
scanForNewline_ = true;
}
}
}
return false;
}
void WordWrapper::AddEllipsis() {
if (!out_.empty() && IsSpaceOrShy(lastChar_)) {
UTF8 utf(out_.c_str(), (int)out_.size());
utf.bwd();
out_.resize(utf.byteIndex());
out_ += "...";
} else {
out_ += "...";
}
lastChar_ = '.';
x_ += ellipsisWidth_;
}
void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {
int lastWordStartIndex = lastIndex_;
if (WrapBeforeWord()) {
UTF8 utf8Word(str_, lastWordStartIndex);
while (lastWordStartIndex < endIndex) {
const uint32_t c = utf8Word.next();
if (!IsSpace(c)) {
break;
}
lastWordStartIndex = utf8Word.byteIndex();
}
}
lastEllipsisIndex_ = -1;
if (skipNextWord_) {
lastIndex_ = endIndex;
return;
}
if (x_ <= maxW_) {
out_.append(str_.data() + lastWordStartIndex, str_.data() + endIndex);
} else {
scanForNewline_ = true;
}
if (addNewline && (flags_ & FLAG_WRAP_TEXT)) {
out_ += "\n";
lastChar_ = '\n';
lastLineStart_ = out_.size();
scanForNewline_ = false;
x_ = 0.0f;
} else {
size_t pos = out_.find_last_of('\n');
if (pos != out_.npos) {
lastLineStart_ = pos + 1;
}
if (lastChar == -1 && !out_.empty()) {
UTF8 utf(out_.c_str(), (int)out_.size());
utf.bwd();
lastChar = utf.next();
}
lastChar_ = lastChar;
if (lastLineStart_ != out_.size()) {
x_ = MeasureWidth(std::string_view(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_));
} else {
x_ = 0.0f;
}
}
lastIndex_ = endIndex;
wordWidth_ = 0.0f;
}
void WordWrapper::Wrap() {
size_t len = str_.length();
if (MeasureWidth(str_) <= maxW_) {
out_ = std::string(str_);
return;
}
out_.clear();
out_.reserve(len + len / 16);
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
ellipsisWidth_ = MeasureWidth("...");
}
for (UTF8 utf(str_); !utf.end(); ) {
int beforeIndex = utf.byteIndex();
uint32_t c = utf.next();
int afterIndex = utf.byteIndex();
if (c == '\n') {
if (skipNextWord_) {
lastIndex_ = beforeIndex;
skipNextWord_ = false;
}
AppendWord(afterIndex, c, false);
forceEarlyWrap_ = false;
scanForNewline_ = false;
continue;
}
if (scanForNewline_) {
lastIndex_ = afterIndex;
continue;
}
float newWordWidth = 0.0f;
if (afterIndex <= str_.length()) {
newWordWidth = MeasureWidth(str_.substr(lastIndex_, afterIndex - lastIndex_));
}
if (wordWidth_ > 0.0f && IsSpaceOrShy(c)) {
AppendWord(afterIndex, c, false);
skipNextWord_ = false;
continue;
}
if (skipNextWord_)
continue;
if ((flags_ & FLAG_ELLIPSIZE_TEXT) != 0 && wordWidth_ > 0.0f && lastEllipsisIndex_ == -1) {
float checkX = x_;
if ((flags_ & FLAG_WRAP_TEXT) != 0 && x_ >= maxW_) {
checkX = 0;
}
if (checkX + wordWidth_ + ellipsisWidth_ <= maxW_ && newWordWidth + ellipsisWidth_ > maxW_) {
lastEllipsisIndex_ = beforeIndex;
continue;
}
}
if (wordWidth_ > 0.0f && newWordWidth > maxW_) {
if (lastEllipsisIndex_ != -1) {
AppendWord(lastEllipsisIndex_, -1, false);
AddEllipsis();
skipNextWord_ = true;
if ((flags_ & FLAG_WRAP_TEXT) == 0) {
scanForNewline_ = true;
}
continue;
}
if (x_ > 0.0f && x_ + wordWidth_ > maxW_ && beforeIndex > lastIndex_ && (flags_ & FLAG_WRAP_TEXT) != 0) {
forceEarlyWrap_ = true;
wordWidth_ = 0.0f;
while (utf.byteIndex() > lastIndex_) {
utf.bwd();
}
continue;
}
AppendWord(beforeIndex, -1, true);
forceEarlyWrap_ = false;
continue;
}
if ((flags_ & FLAG_ELLIPSIZE_TEXT) && wordWidth_ > 0.0f && x_ + newWordWidth + ellipsisWidth_ > maxW_) {
if ((flags_ & FLAG_WRAP_TEXT) == 0 && x_ + wordWidth_ + ellipsisWidth_ <= maxW_) {
AppendWord(lastEllipsisIndex_ != -1 ? lastEllipsisIndex_ : beforeIndex, -1, false);
AddEllipsis();
forceEarlyWrap_ = false;
skipNextWord_ = true;
if ((flags_ & FLAG_WRAP_TEXT) == 0) {
scanForNewline_ = true;
}
continue;
}
}
wordWidth_ = newWordWidth;
if (wordWidth_ > 0.0f && (IsCJK(c) || IsPunctuation(c) || forceEarlyWrap_)) {
AppendWord(afterIndex, c, false);
}
}
AppendWord((int)len, 0, false);
}