<commit_before>#include "textBuffer.h"

#include "labels/textLabel.h"
#include "gl/texture.h"
#include "gl/vboMesh.h"

namespace Tangram {

TextBuffer::TextBuffer(std::shared_ptr<VertexLayout> _vertexLayout)
    : LabelMesh(_vertexLayout, GL_TRIANGLES) {
    addVertices({}, {});
}

TextBuffer::~TextBuffer() {}

std::vector<TextBuffer::WordBreak> TextBuffer::findWords(const std::string& _text) {
    std::vector<WordBreak> words;
    unsigned int utf8state = 0;
    const char* str = _text.c_str();
    const char* end = str + strlen(str);
    int wordBegin = 0;
    int wordEnd = -1;
    int itr = 0;

    for (; str != end; ++str) {
        unsigned int byte = *(const unsigned char*)str;

        if (fonsDecUTF8(&utf8state, byte)) {
            continue;
        }

        if (std::isspace(*str)) {
            wordEnd = itr - 1;
            if (wordBegin <= wordEnd) {
                words.push_back({wordBegin, wordEnd});
            }
            wordBegin = itr + 1;
        }
        itr++;
    }

    if (!std::isspace(*(end-1))) {
        words.push_back({wordBegin, itr-1});
    }

    return words;
}

int TextBuffer::applyWordWrapping(std::vector<FONSquad>& _quads,
                                  const TextStyle::Parameters& _params,
                                  const FontContext::FontMetrics& _metrics,
                                  Label::Type _type, glm::vec2* _bbox) {
    struct LineQuad {
        std::vector<FONSquad*> quads;
        float length;
    };

    float yOffset = 0.f, xOffset = 0.f;
    int nLine = 1, lastBreak = 0;

    std::vector<LineQuad> lines;
    std::vector<TextBuffer::WordBreak> words;

    if (_params.wordWrap < _params.text.length() && _type != Label::Type::line) {
       words = findWords(_params.text);
    }

    // Approximation of the first glyph left side bearing
    float firstGlyphLSB = _quads[0].x0 * 0.5f;

    LineQuad line;
    lines.push_back(line); // atleast one line

    // Apply word wrapping based on the word breaks
    for (int iWord = 0; iWord < words.size(); iWord++) {
        int start = words[iWord].start;
        int end = words[iWord].end;
        size_t wordSize = end - start + 1;

        auto& lastLineQuads = lines[nLine - 1].quads;

        // Check if quads need to be added to next line?
        if (iWord > 0 && (lastLineQuads.size() + wordSize) > (size_t)_params.maxLineWidth) {
            auto& quad = _quads[start];
            auto& prevQuad = _quads[words[iWord-1].end];

            float spaceLength = quad.x0 - prevQuad.x1;
            yOffset += _metrics.lineHeight;
            xOffset -= quad.x0 + firstGlyphLSB - spaceLength;
            lines.push_back(LineQuad());
            nLine++;
        }

        for (int i = start; i <= end; i++) {
            auto& q = _quads[i];

            q.x0 += xOffset;
            q.x1 += xOffset;
            q.y0 += yOffset;
            q.y1 += yOffset;

            lines[nLine - 1].quads.push_back(&q);<commit_msg>Fix bounding boxes and an offset issue with wraps<commit_after>#include "textBuffer.h"

#include "labels/textLabel.h"
#include "gl/texture.h"
#include "gl/vboMesh.h"

namespace Tangram {

TextBuffer::TextBuffer(std::shared_ptr<VertexLayout> _vertexLayout)
    : LabelMesh(_vertexLayout, GL_TRIANGLES) {
    addVertices({}, {});
}

TextBuffer::~TextBuffer() {}

std::vector<TextBuffer::WordBreak> TextBuffer::findWords(const std::string& _text) {
    std::vector<WordBreak> words;
    unsigned int utf8state = 0;
    const char* str = _text.c_str();
    const char* end = str + strlen(str);
    int wordBegin = 0;
    int wordEnd = -1;
    int itr = 0;

    for (; str != end; ++str) {
        unsigned int byte = *(const unsigned char*)str;

        if (fonsDecUTF8(&utf8state, byte)) {
            continue;
        }

        if (std::isspace(*str)) {
            wordEnd = itr - 1;
            if (wordBegin <= wordEnd) {
                words.push_back({wordBegin, wordEnd});
            }
            wordBegin = itr + 1;
        }
        itr++;
    }

    if (!std::isspace(*(end-1))) {
        words.push_back({wordBegin, itr-1});
    }

    return words;
}

int TextBuffer::applyWordWrapping(std::vector<FONSquad>& _quads,
                                  const TextStyle::Parameters& _params,
                                  const FontContext::FontMetrics& _metrics,
                                  Label::Type _type, glm::vec2* _bbox) {
    struct LineQuad {
        std::vector<FONSquad*> quads;
        float length;
    };

    float yOffset = 0.f, xOffset = 0.f;
    int nLine = 1;

    std::vector<LineQuad> lines;
    std::vector<TextBuffer::WordBreak> words;

    if (_params.wordWrap < _params.text.length() && _type != Label::Type::line) {
       words = findWords(_params.text);
    } else {
        for (auto& q : _quads) {
            _bbox->x = std::max(_bbox->x, q.x1);
        }
    }

    LineQuad line;
    lines.push_back(line); // atleast one line

    // Apply word wrapping based on the word breaks
    for (int iWord = 0; iWord < words.size(); iWord++) {
        int start = words[iWord].start;
        int end = words[iWord].end;
        size_t wordSize = end - start + 1;

        auto& lastLineQuads = lines[nLine - 1].quads;

        // Check if quads need to be added to next line?
        if (iWord > 0 && (lastLineQuads.size() + wordSize) > (size_t)_params.maxLineWidth) {
            xOffset = 0.0f;
            auto& quad = _quads[start];
            auto& prevQuad = lines[nLine - 1].quads.front();

            float baseLength = quad.x0 - prevQuad->x0;
            yOffset += _metrics.lineHeight;
            xOffset -= (baseLength);
            lines.push_back(LineQuad());
            nLine++;
        }

        for (int i = start; i <= end; i++) {
            auto& q = _quads[i];

            q.x0 += xOffset;
            q.x1 += xOffset;
            q.y0 += yOffset;
            q.y1 += yOffset;

            lines[nLine - 1].quads.push_back(&q);
{'fraction_non_alphanumeric': 0.1360214154257989, 'fraction_numerical': 0.01054040488539401, 'mean_word_length': 2.0131048387096775, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6410245', 'n_tokens_mistral': 2002, 'n_tokens_neox': 1915, 'n_words': 442}