void GlyphArrangement::addFittedText (const Font& f,
                                      const String& text,
                                      const float x, const float y,
                                      const float width, const float height,
                                      Justification layout,
                                      int maximumLines,
                                      float minimumHorizontalScale)
{
    if (minimumHorizontalScale == 0.0f)
        minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor();

    // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0
    jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f);

    if (text.containsAnyOf ("\r\n"))
    {
        addLinesWithLineBreaks (text, f, x, y, width, height, layout);
    }
    else
    {
        const int startIndex = glyphs.size();
        const String trimmed (text.trim());
        addLineOfText (f, trimmed, x, y);
        const int numGlyphs = glyphs.size() - startIndex;

        if (numGlyphs > 0)
        {
            const float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
                                      - glyphs.getReference (startIndex).getLeft();

            if (lineWidth > 0)
            {
                if (lineWidth * minimumHorizontalScale < width)
                {
                    if (lineWidth > width)
                        stretchRangeOfGlyphs (startIndex, numGlyphs, width / lineWidth);

                    justifyGlyphs (startIndex, numGlyphs, x, y, width, height, layout);
                }
                else if (maximumLines <= 1)
                {
                    fitLineIntoSpace (startIndex, numGlyphs, x, y, width, height,
                                      f, layout, minimumHorizontalScale);
                }
                else
                {
                    splitLines (trimmed, f, startIndex, x, y, width, height,
                                maximumLines, lineWidth, layout, minimumHorizontalScale);
                }
            }
        }
    }
}
void GlyphArrangement::splitLines (const String& text, Font font, int startIndex,
                                   float x, float y, float width, float height, int maximumLines,
                                   float lineWidth, Justification layout, float minimumHorizontalScale)
{
    const int length = text.length();
    const int originalStartIndex = startIndex;
    int numLines = 1;

    if (length <= 12 && ! text.containsAnyOf (" -\t\r\n"))
        maximumLines = 1;

    maximumLines = jmin (maximumLines, length);

    while (numLines < maximumLines)
    {
        ++numLines;

        const float newFontHeight = height / (float) numLines;

        if (newFontHeight < font.getHeight())
        {
            font.setHeight (jmax (8.0f, newFontHeight));

            removeRangeOfGlyphs (startIndex, -1);
            addLineOfText (font, text, x, y);

            lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
                            - glyphs.getReference (startIndex).getLeft();
        }

        // Try to estimate the point at which there are enough lines to fit the text,
        // allowing for unevenness in the lengths due to differently sized words.
        const float lineLengthUnevennessAllowance = 80.0f;

        if (numLines > (lineWidth + lineLengthUnevennessAllowance) / width || newFontHeight < 8.0f)
            break;
    }

    if (numLines < 1)
        numLines = 1;

    float lineY = y;
    float widthPerLine = lineWidth / numLines;

    for (int line = 0; line < numLines; ++line)
    {
        int i = startIndex;
        float lineStartX = glyphs.getReference (startIndex).getLeft();

        if (line == numLines - 1)
        {
            widthPerLine = width;
            i = glyphs.size();
        }
        else
        {
            while (i < glyphs.size())
            {
                lineWidth = (glyphs.getReference (i).getRight() - lineStartX);

                if (lineWidth > widthPerLine)
                {
                    // got to a point where the line's too long, so skip forward to find a
                    // good place to break it..
                    const int searchStartIndex = i;

                    while (i < glyphs.size())
                    {
                        if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width)
                        {
                            if (glyphs.getReference (i).isWhitespace()
                                 || glyphs.getReference (i).getCharacter() == '-')
                            {
                                ++i;
                                break;
                            }
                        }
                        else
                        {
                            // can't find a suitable break, so try looking backwards..
                            i = searchStartIndex;

                            for (int back = 1; back < jmin (7, i - startIndex - 1); ++back)
                            {
                                if (glyphs.getReference (i - back).isWhitespace()
                                     || glyphs.getReference (i - back).getCharacter() == '-')
                                {
                                    i -= back - 1;
                                    break;
                                }
                            }

                            break;
                        }

                        ++i;
                    }

                    break;
                }

                ++i;
            }

            int wsStart = i;
            while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace())
                --wsStart;

            int wsEnd = i;
            while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace())
                ++wsEnd;

            removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
            i = jmax (wsStart, startIndex + 1);
        }

        i -= fitLineIntoSpace (startIndex, i - startIndex,
                               x, lineY, width, font.getHeight(), font,
                               layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
                               minimumHorizontalScale);

        startIndex = i;
        lineY += font.getHeight();

        if (startIndex >= glyphs.size())
            break;
    }

    justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
                   x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
}
void GlyphArrangement::addJustifiedText (const Font& font,
                                         const String& text,
                                         float x, float y,
                                         const float maxLineWidth,
                                         Justification horizontalLayout)
{
    int lineStartIndex = glyphs.size();
    addLineOfText (font, text, x, y);

    const float originalY = y;

    while (lineStartIndex < glyphs.size())
    {
        int i = lineStartIndex;

        if (glyphs.getReference(i).getCharacter() != '\n'
              && glyphs.getReference(i).getCharacter() != '\r')
            ++i;

        const float lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth;
        int lastWordBreakIndex = -1;

        while (i < glyphs.size())
        {
            const PositionedGlyph& pg = glyphs.getReference (i);
            const juce_wchar c = pg.getCharacter();

            if (c == '\r' || c == '\n')
            {
                ++i;

                if (c == '\r' && i < glyphs.size()
                     && glyphs.getReference(i).getCharacter() == '\n')
                    ++i;

                break;
            }

            if (pg.isWhitespace())
            {
                lastWordBreakIndex = i + 1;
            }
            else if (pg.getRight() - 0.0001f >= lineMaxX)
            {
                if (lastWordBreakIndex >= 0)
                    i = lastWordBreakIndex;

                break;
            }

            ++i;
        }

        const float currentLineStartX = glyphs.getReference (lineStartIndex).getLeft();
        float currentLineEndX = currentLineStartX;

        for (int j = i; --j >= lineStartIndex;)
        {
            if (! glyphs.getReference (j).isWhitespace())
            {
                currentLineEndX = glyphs.getReference (j).getRight();
                break;
            }
        }

        float deltaX = 0.0f;

        if (horizontalLayout.testFlags (Justification::horizontallyJustified))
            spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth);
        else if (horizontalLayout.testFlags (Justification::horizontallyCentred))
            deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f;
        else if (horizontalLayout.testFlags (Justification::right))
            deltaX = maxLineWidth - (currentLineEndX - currentLineStartX);

        moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex,
                           x + deltaX - currentLineStartX, y - originalY);

        lineStartIndex = i;

        y += font.getHeight();
    }
}
Пример #4
0
void GlyphArrangement::addFittedText (const Font& f,
                                      const String& text,
                                      const float x, const float y,
                                      const float width, const float height,
                                      const Justification& layout,
                                      int maximumLines,
                                      const float minimumHorizontalScale)
{
    // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0
    jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f);

    if (text.containsAnyOf ("\r\n"))
    {
        GlyphArrangement ga;
        ga.addJustifiedText (f, text, x, y, width, layout);

        const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));

        float dy = y - bb.getY();

        if (layout.testFlags (Justification::verticallyCentred))
            dy += (height - bb.getHeight()) * 0.5f;
        else if (layout.testFlags (Justification::bottom))
            dy += height - bb.getHeight();

        ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);

        glyphs.ensureStorageAllocated (glyphs.size() + ga.glyphs.size());

        for (int i = 0; i < ga.glyphs.size(); ++i)
            glyphs.add (ga.glyphs.getUnchecked (i));

        ga.glyphs.clear (false);
        return;
    }

    int startIndex = glyphs.size();
    addLineOfText (f, text.trim(), x, y);

    if (glyphs.size() > startIndex)
    {
        float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight()
                            - glyphs.getUnchecked (startIndex)->getLeft();

        if (lineWidth <= 0)
            return;

        if (lineWidth * minimumHorizontalScale < width)
        {
            if (lineWidth > width)
                stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex,
                                      width / lineWidth);

            justifyGlyphs (startIndex, glyphs.size() - startIndex,
                           x, y, width, height, layout);
        }
        else if (maximumLines <= 1)
        {
            fitLineIntoSpace (startIndex, glyphs.size() - startIndex,
                              x, y, width, height, f, layout, minimumHorizontalScale);
        }
        else
        {
            Font font (f);
            String txt (text.trim());
            const int length = txt.length();
            const int originalStartIndex = startIndex;
            int numLines = 1;

            if (length <= 12 && ! txt.containsAnyOf (" -\t\r\n"))
                maximumLines = 1;

            maximumLines = jmin (maximumLines, length);

            while (numLines < maximumLines)
            {
                ++numLines;

                const float newFontHeight = height / (float) numLines;

                if (newFontHeight < font.getHeight())
                {
                    font.setHeight (jmax (8.0f, newFontHeight));

                    removeRangeOfGlyphs (startIndex, -1);
                    addLineOfText (font, txt, x, y);

                    lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight()
                                    - glyphs.getUnchecked (startIndex)->getLeft();
                }

                if (numLines > lineWidth / width || newFontHeight < 8.0f)
                    break;
            }

            if (numLines < 1)
                numLines = 1;

            float lineY = y;
            float widthPerLine = lineWidth / numLines;
            int lastLineStartIndex = 0;

            for (int line = 0; line < numLines; ++line)
            {
                int i = startIndex;
                lastLineStartIndex = i;
                float lineStartX = glyphs.getUnchecked (startIndex)->getLeft();

                if (line == numLines - 1)
                {
                    widthPerLine = width;
                    i = glyphs.size();
                }
                else
                {
                    while (i < glyphs.size())
                    {
                        lineWidth = (glyphs.getUnchecked (i)->getRight() - lineStartX);

                        if (lineWidth > widthPerLine)
                        {
                            // got to a point where the line's too long, so skip forward to find a
                            // good place to break it..
                            const int searchStartIndex = i;

                            while (i < glyphs.size())
                            {
                                if ((glyphs.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width)
                                {
                                    if (glyphs.getUnchecked (i)->isWhitespace()
                                         || glyphs.getUnchecked (i)->getCharacter() == '-')
                                    {
                                        ++i;
                                        break;
                                    }
                                }
                                else
                                {
                                    // can't find a suitable break, so try looking backwards..
                                    i = searchStartIndex;

                                    for (int back = 1; back < jmin (5, i - startIndex - 1); ++back)
                                    {
                                        if (glyphs.getUnchecked (i - back)->isWhitespace()
                                             || glyphs.getUnchecked (i - back)->getCharacter() == '-')
                                        {
                                            i -= back - 1;
                                            break;
                                        }
                                    }

                                    break;
                                }

                                ++i;
                            }

                            break;
                        }

                        ++i;
                    }

                    int wsStart = i;
                    while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace())
                        --wsStart;

                    int wsEnd = i;

                    while (wsEnd < glyphs.size() && glyphs.getUnchecked (wsEnd)->isWhitespace())
                        ++wsEnd;

                    removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
                    i = jmax (wsStart, startIndex + 1);
                }

                i -= fitLineIntoSpace (startIndex, i - startIndex,
                                       x, lineY, width, font.getHeight(), font,
                                       layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
                                       minimumHorizontalScale);

                startIndex = i;
                lineY += font.getHeight();

                if (startIndex >= glyphs.size())
                    break;
            }

            justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
                           x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
        }
    }
}