std::vector<TextFlow::Word> TextFlow::calculateFitWord(std::u16string content, int maxPixelWidth, float scale) const { // Calculate word from content Word word = calculateWord(content, scale); // End of recursion if ((content.size() == 1 && word.pixelWidth > maxPixelWidth) || content.empty()) { // Return empty vector as signal of failure return std::vector<Word>(); } else if (word.pixelWidth <= maxPixelWidth) { // If word length is ok, just return it return std::vector<Word>(1, word); } else { // Word is too wide and content longer than 1, split it! int length = (int)content.size(); int left = length / 2; int right = length - left; // Combine results from recursive call std::vector<Word> leftWord = calculateFitWord(content.substr(0, left), maxPixelWidth, scale); std::vector<Word> rightWord = calculateFitWord(content.substr(left+1, right), maxPixelWidth, scale); // If one or more of both are empty, forget it if (leftWord.empty() || rightWord.empty()) { return std::vector<Word>(); } else { std::vector<Word> words(leftWord); words.insert(words.end(), rightWord.begin(), rightWord.end()); return words; } } }
bool UserDictionaryDecoder::DecodeArticle( IBitStream *bstr, std::u16string &res, std::u16string const& prefix, LenTable& ltArticles, std::vector<char32_t>& articleSymbols) { unsigned len = bstr->read(16); if (len == 0xFFFF) { len = bstr->read(32); } res.clear(); unsigned symIdx; std::vector<uint32_t> vec; while ((unsigned)res.length() < len) { ltArticles.Decode(*bstr, symIdx); unsigned sym = articleSymbols.at(symIdx); vec.push_back(sym); if (sym >= 0x10000) { if (sym >= 0x10040) { unsigned startIdx = bstr->read(BitLength(len)); unsigned len = sym - 0x1003d; res += res.substr(startIdx, len); vec.push_back(startIdx); } else { unsigned startIdx = bstr->read(BitLength(prefix.length())); unsigned len = sym - 0xfffd; res += prefix.substr(startIdx, len); vec.push_back(startIdx); } } else { res += (char16_t)sym; } } return true; }
void TextFlow::specialCalculateMesh( std::u16string streamlinedContent, float lineHeight, std::vector<glm::vec3>& rVertices, std::vector<glm::vec2>& rTextureCoordinates) { // Reset flow width to get longest line's width of this computation mFlowWidth = 0; // OpenGL setup done in calling method // Get size of space character float pixelOfSpace = 0; Glyph const * pGlyph = mpFont->getGlyph(mFontSize, u' '); if (pGlyph == NULL) { throwWarning( OperationNotifier::Operation::RUNTIME, "TextFlow creation does not find space sign in font"); } else { pixelOfSpace = mScale * pGlyph->advance.x; } // Create mark for overflow Word overflowMark = calculateWord(TEXT_FLOW_OVERFLOW_MARK, mScale); // Get pararaphs separated by \n std::vector<std::u16string> paragraphs; std::u16string paragraphDelimiter = u"\n"; // Seperate into paragraphs size_t pos = 0; std::u16string token; while ((pos = streamlinedContent.find(paragraphDelimiter)) != std::u16string::npos) { token = streamlinedContent.substr(0, pos); paragraphs.push_back(token); streamlinedContent.erase(0, pos + paragraphDelimiter.length()); } paragraphs.push_back(streamlinedContent); // Last paragraph (paragraphs never empty) // Do not generate text flow mesh when there is a failure bool failure = false; // Go over paragraphs (pens are in local pixel coordinate system with origin in lower left corner of element) float yPixelPen = -lineHeight; // First line should be also inside flow for (std::u16string& rPargraph : paragraphs) { // Get words out of paragraph std::vector<Word> words; std::u16string wordDelimiter = u" "; while ((pos = rPargraph.find(wordDelimiter)) != std::u16string::npos) { token = rPargraph.substr(0, pos); rPargraph.erase(0, pos + wordDelimiter.length()); failure |= !insertFitWord(words, token, mWidth, mScale); } // Add last token from paragraph as well failure |= !insertFitWord(words, rPargraph, mWidth, mScale); // Failure appeared, forget it if (!failure) { // Prepare some values uint wordIndex = 0; bool hasNext = !words.empty(); // Go over lines to write paragraph while (hasNext && abs(yPixelPen) <= mHeight) { // Collect words in one line std::vector<Word const *> line; float wordsPixelWidth = 0; float newWordsWithSpacesPixelWidth = 0; // Still words in the paragraph and enough space? Fill into line! while (hasNext && newWordsWithSpacesPixelWidth <= mWidth) { // First word should always fit into width because of previous checks wordsPixelWidth += words[wordIndex].pixelWidth; line.push_back(&words[wordIndex]); wordIndex++; if (wordIndex >= words.size()) { // No words in paragraph left hasNext = false; } else { // Calculate next width of line newWordsWithSpacesPixelWidth = std::ceil( (wordsPixelWidth + (float)words[wordIndex].pixelWidth) // Words size (old ones and new one) + (((float)line.size()) - 1.0f) * pixelOfSpace); // Spaces between words } } // If this is last line and after it still words left, replace it by some mark for overflow if (hasNext && abs(yPixelPen - lineHeight) > mHeight && overflowMark.pixelWidth <= mWidth) { line.clear(); wordsPixelWidth = overflowMark.pixelWidth; line.push_back(&overflowMark); } // Remember longest line's width mFlowWidth = mFlowWidth < ((int) wordsPixelWidth + 1) ? ((int)wordsPixelWidth + 1) : mFlowWidth; // Decide dynamic space for line float dynamicSpace = pixelOfSpace; if (line.size() > 1) { if (mAlignment == TextFlowAlignment::JUSTIFY && hasNext && line.size() > 1) // Do not use dynamic space for last line { // For justify, do something dynamic dynamicSpace = ((float)mWidth - wordsPixelWidth) / ((float)line.size() - 1.0f); } else { // Adjust space to compensate precision errors in other alignments float calculatedDynamicSpace = (float)mWidth - (wordsPixelWidth / (float)(line.size() - 1)); dynamicSpace = std::min(dynamicSpace, calculatedDynamicSpace); } } // Now decide xOffset for line float xOffset = 0; if (mAlignment == TextFlowAlignment::RIGHT || mAlignment == TextFlowAlignment::CENTER) { xOffset = (float)mWidth - ((wordsPixelWidth + ((float)line.size() - 1.0f) * dynamicSpace)); if (mAlignment == TextFlowAlignment::CENTER) { xOffset = xOffset / 2.0f; } } // Combine word geometry to one line float xPixelPen = xOffset; for (uint i = 0; i < line.size(); i++) { // Assuming, that the count of vertices and texture coordinates is equal for (uint j = 0; j < line[i]->spVertices->size(); j++) { const glm::vec3& rVertex = line[i]->spVertices->at(j); rVertices.push_back(glm::vec3(rVertex.x + xPixelPen, rVertex.y + yPixelPen, rVertex.z)); const glm::vec2& rTextureCoordinate = line[i]->spTextureCoordinates->at(j); rTextureCoordinates.push_back(glm::vec2(rTextureCoordinate.s, rTextureCoordinate.t)); } // Advance xPen xPixelPen += dynamicSpace + line[i]->pixelWidth; } // Advance yPen yPixelPen -= lineHeight; } } } // If failure appeared, clean up if (failure) { // Vertex count will become zero rVertices.clear(); rTextureCoordinates.clear(); } // Get height of all lines (yPixelPen is one line to low now) mFlowHeight = (int)std::max(std::ceil(abs(yPixelPen) - lineHeight), 0.0f); }