void setGlyphXPositions(bool isRTL)
    {
        double position = 0;
        // logClustersIndex indexes logClusters for the first (or last when
        // RTL) codepoint of the current glyph.  Each time we advance a glyph,
        // we skip over all the codepoints that contributed to the current
        // glyph.
        unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0;

        for (int iter = 0; iter < m_item.num_glyphs; ++iter) {
            // Glyphs are stored in logical order, but for layout purposes we
            // always go left to right.
            int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;

            m_glyphs16[i] = m_item.glyphs[i];
            double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
            m_xPositions[i] = m_offsetX + position + offsetX;

            double advance = truncateFixedPointToInteger(m_item.advances[i]);
            // The first half of the conjuction works around the case where
            // output glyphs aren't associated with any codepoints by the
            // clusters log.
            if (logClustersIndex < m_item.item.length
                && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) {
                advance += m_wordSpacingAdjustment;

                if (m_padding > 0) {
                    unsigned toPad = roundf(m_padPerWordBreak + m_padError);
                    m_padError += m_padPerWordBreak - toPad;

                    if (m_padding < toPad)
                        toPad = m_padding;
                    m_padding -= toPad;
                    advance += toPad;
                }
            }

            // We would like to add m_letterSpacing after each cluster, but I
            // don't know where the cluster information is. This is typically
            // fine for Roman languages, but breaks more complex languages
            // terribly.
            // advance += m_letterSpacing;

            if (isRTL) {
                while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
                    logClustersIndex--;
            } else {
                while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i)
                    logClustersIndex++;
            }

            position += advance;
        }

        m_pixelWidth = position;
        m_offsetX += m_pixelWidth;
    }
// setPadding sets a number of pixels to be distributed across the TextRun.
// WebKit uses this to justify text.
void ComplexTextController::setPadding(int padding)
{
    m_padding = padding;
    if (!m_padding)
        return;

    // If we have padding to distribute, then we try to give an equal
    // amount to each space. The last space gets the smaller amount, if
    // any.
    unsigned numWordBreaks = 0;

    for (unsigned i = 0; i < m_item.stringLength; i++) {
        if (isWordBreak(i))
            numWordBreaks++;
    }

    if (numWordBreaks)
        m_padPerWordBreak = m_padding / numWordBreaks;
    else
        m_padPerWordBreak = 0;
}
Beispiel #3
0
 bool isWordBreak (guint ch, guint BackspaceChar) {
     return isWordBreak (_(ch), BackspaceChar);
 }
Beispiel #4
0
 bool isWordBreak (const gchar *ch, guint BackspaceChar) {
     return isWordBreak (_(ch), BackspaceChar);
 }
// Parse one line of text, create fragments, end line on word break when width overflows.
void PaintText::parseFragmentsWithWordBreak(
    TextLine& line,             // Line descriptor.
    int startPos,               // Location of beginning of string to examine.
    float maxWidth,             // Can't go beyond this width.
    int* resultPos              // OUT: Ptr to string past the format string.
) {
    int curPos = startPos;          // Beginning of current part of the string we are working on.
    double curWidth = maxWidth;     // The width left to work with.
    const int endPos = m_text.size();// One past the end of the string.

    bool forceEndLine = false;      // True = end-of-line through format.  False = char width.
    LayoutState origLayout = m_layout;// The original layout state before we start the line.

    int wordBreakPos = endPos;      // Previous word break location in text.

    // In this loop we just measure the width.  We find the end of the current line in m_text,
    //  then call parseFragmentsWithCharBreak once we know how far to go.
    while(curPos < endPos) {
        // Is there a format char left in this string?
        const string::size_type formatPos = m_text.find(DT_FORMAT_CHAR, curPos);
        int endFragPos = formatPos;
        if(formatPos == std::string::npos || formatPos >= endPos) {
            // No format char.
            endFragPos = endPos;
        }

        // Loop through the characters until we run out of room.
        const Font& font = m_layout.fontStack.back();
        while(curPos < endFragPos) {
            double charWidth = font.charWidth(m_text[curPos]);
            if(isWordBreak(m_text[curPos])) {
                wordBreakPos = curPos;
            }
            if(curWidth - charWidth < 0.0) {
                // The current character goes past the specified width.
                break;
            }
            curWidth -= charWidth;
            curPos++;
        }

        if(curPos == endPos) {
            // The rest of the text is not as wide as the max.  We are done with this pass.
            wordBreakPos = endPos;
            break;
        } else if(curPos < endFragPos) {
            // We found a last character. Go back to the last word break.
            break;
        }
        assert(curPos == formatPos);    // Other other case: we ran into a format command.

        // Interpret the format command.
        assert(m_text[curPos] == DT_FORMAT_CHAR);
        curPos++;       // Look at the command char.
        if(curPos >= endPos) {
            // No command char.  String ends with single "#".  Ignore it.
            curPos = endPos - 1;
            break;
        }
        if(m_text[curPos] == DT_FORMAT_CHAR) {
            // Double format char.  Equals one format char.
            curWidth -= font.charWidth(DT_FORMAT_CHAR);
            if(curWidth < 0.0) {
                // No room in the line for the one char.  Leave it for next line.
                curPos--;           // Put chars back in string.
                break;              // End of this line.
            }
        } else {
            parseFormat(curPos, &curPos, &forceEndLine);
            if(forceEndLine) {
                wordBreakPos = endPos;
                break;
            }
        }
    }

    // Now we need to generate the fragments.
    // NOTE:  If the text contains a word that is too long for a line, we get a special case
    //  where we never find a word break.  That ends up calling parseFragmentsWithCharBreak with
    //  the end of the string as the limit, so the word is broken on a char boundary, which is 
    //  exactly what we want.
    int endLinePos = wordBreakPos + 1;
    // Include all extra word break characters.
    while(endLinePos+1 < endPos && isWordBreak(m_text[endLinePos])) {
        endLinePos++;
    }
    m_layout = origLayout;                 // Undo any format changes.
    parseFragmentsWithCharBreak(line, startPos, endLinePos, maxWidth, false, resultPos);
}
// Parse one line of text, create fragments, end line when overflow width.
void PaintText::parseFragmentsWithCharBreak(
    TextLine& line,             // Line descriptor.
    int startPos,               // Location of beginning of string to examine.
    int endPos,                 // Location of one past the last character to examine.
    float maxWidth,             // Can't go beyond this width.
    bool ellipsis,              // True = if line doesn't fit, append ellipsis.
    int* resultPos              // OUT: Ptr to string past the format string.
) {
    int curPos = startPos;          // Beginning of current part of the string we are working on.
    double curWidth = maxWidth;     // The width left to work with.

    bool forceEndLine = false;      // True = end-of-line through format.  False = char width.

    while(curPos < endPos) {
        // Is there a format char left in this string?
        const string::size_type formatPos = m_text.find(DT_FORMAT_CHAR, curPos);
        if(formatPos == std::string::npos || formatPos >= endPos) {
            // No format char.
            addFragment(line, endPos, curPos, curWidth);
            break;
        }

        // Create fragment for characters before the format char.
        addFragment(line, formatPos, curPos, curWidth);
        if(curPos < formatPos) {
            // Format is past the max width.  We're done with this line.
            break;
        }

        // Interpret the format command.
        assert(m_text[curPos] == DT_FORMAT_CHAR);
        curPos++;       // Look at the command char.
        if(curPos >= endPos) {
            // No command char.  String ends with single "#".  Ignore it.
            curPos = endPos - 1;
            break;
        }
        if(m_text[curPos] == DT_FORMAT_CHAR) {
            // Double format char.  Equals one format char.
            const int oldPos = curPos;
            addFragment(line, curPos+1, curPos, curWidth);
            if(curPos == oldPos) {
                // No room in the line for the one char.  Leave it for next line.
                curPos--;           // Put chars back in string.
                break;              // End of this line.
            }
        } else {
            parseFormat(curPos, &curPos, &forceEndLine);
            if(forceEndLine) {
                break;
            }
        }
    }

    if(!forceEndLine && curPos < endPos) {
        if(ellipsis) {
            // We need to append an ellipsis. We didn't use the whole string, and we didn't end
            //  the line because of a format command.
            // We use the font at the end of the line.  This is a hack, but it seems like a
            //  reasonable compromise.  If we use the font from the beginning of the line, suppose the
            //  line starts out with bold, followed by a non-bold explanation?
            // We use the color of the last fragment before the ellipsis.

            const Font& font = m_layout.fontStack.back();
            const double ellipsisWidth = font.stringWidth(ELLIPSIS_STRING);
            // If ellipsis doesn't fit in the original width, just ship the truncated string.
            if(ellipsisWidth < maxWidth) {
                while(line.fragments.size() > 0) { 
                    TextFragment& frag = line.fragments.back();
                    // Remove enough space in the last fragment to be able to append the ellipsis.
                    int i;
                    for(i=frag.end; i>=frag.start; i--) {
                        curWidth -= frag.font.charWidth(m_text[i]);
                        if(curWidth+ellipsisWidth <= maxWidth) {
                            // If we back up this far, the ellipsis will fit in the max width.
                            frag.end = i;
                            break;
                        }
                    }
                    if(i < frag.start) {
                        // Used up the whole fragment and still didn't find enough space.
                        line.fragments.pop_back();
                    } else {
                        // Create an ellipsis fragment and append it to the line.
                        TextFragment newFrag = frag;
                        newFrag.start = ELLIPSIS_FRAGMENT;
                        line.fragments.push_back(newFrag);
                        curWidth += ellipsisWidth;
                        break;
                    }
                }
            }
        } else {
            // Get rid of word break chars at the end of this line.
            // First, make sure to skip over any white space.
            while(curPos < endPos && isWordBreak(m_text[curPos])) {
                curPos++;
            }

            // Now, get rid of any word break chars at the tail end of our fragment list.
            while(line.fragments.size() > 0) { 
                TextFragment& frag = line.fragments.back();
                int i;
                for(i=frag.end; i>=frag.start; i--) {
                    if(!isWordBreak(m_text[i])) {
                        break;
                    }
                    curWidth -= frag.font.charWidth(m_text[i]);
                }
                if(i >= frag.start) {
                    // Found something besides a word break in this fragment.
                    break;
                }
                // Used up the whole fragment.  Start on the next one.
                line.fragments.pop_back();
            }
        }
    }

    // Set the width of this line.
    line.width = maxWidth - curWidth;

    // And make sure we know how far we got in the string.
    *resultPos = curPos;
}