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; }
bool isWordBreak (guint ch, guint BackspaceChar) { return isWordBreak (_(ch), BackspaceChar); }
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; }