2 * This file is part of the DOM implementation for KDE.
4 * (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 2000 Dirk Mueller (mueller@kde.org)
6 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
7 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
27 #include "RenderText.h"
29 #include "DeprecatedString.h"
30 #include "InlineTextBox.h"
33 #include "RenderArena.h"
34 #include "RenderBlock.h"
35 #include "break_lines.h"
36 #include <unicode/ubrk.h>
37 #include <wtf/AlwaysInline.h>
43 UBreakIterator* characterBreakIterator(const StringImpl* i)
48 // The locale is currently ignored when determining character cluster breaks.
49 // This may change in the future, according to Deborah Goldsmith.
50 static bool createdIterator = false;
51 static UBreakIterator* iterator;
53 if (!createdIterator) {
54 status = U_ZERO_ERROR;
55 iterator = ubrk_open(UBRK_CHARACTER, "en_us", 0, 0, &status);
56 createdIterator = true;
61 status = U_ZERO_ERROR;
62 ubrk_setText(iterator, reinterpret_cast<const UChar*>(i->characters()), i->length(), &status);
63 if (status != U_ZERO_ERROR)
69 int RenderText::previousOffset(int current) const
71 UBreakIterator* iterator = characterBreakIterator(str.get());
75 long result = ubrk_preceding(iterator, current);
76 if (result == UBRK_DONE)
82 int RenderText::nextOffset(int current) const
84 UBreakIterator* iterator = characterBreakIterator(str.get());
88 long result = ubrk_following(iterator, current);
89 if (result == UBRK_DONE)
95 RenderText::RenderText(Node* node, StringImpl *_str)
96 : RenderObject(node), str(_str), m_firstTextBox(0), m_lastTextBox(0)
97 , m_minWidth(-1), m_maxWidth(-1), m_selectionState(SelectionNone)
98 , m_linesDirty(false), m_containsReversedText(false)
99 , m_allAsciiChecked(false), m_allAscii(false)
100 , m_monospaceCharacterWidth(0)
104 str = str->replace('\\', backslashAsCurrencySymbol());
105 ASSERT(!str || !str->length() || str->characters());
108 void RenderText::setStyle(RenderStyle *_style)
110 if ( style() != _style ) {
111 bool needToTransformText = (!style() && _style->textTransform() != TTNONE) ||
112 (style() && style()->textTransform() != _style->textTransform());
114 bool needToSecureText = (!style() && _style->textSecurity() != TSNONE);
116 RenderObject::setStyle( _style );
118 if (needToTransformText || needToSecureText) {
119 RefPtr<StringImpl> textToTransform = originalString();
121 setText(textToTransform.get(), true);
123 // setText also calls cacheWidths(), so there is no need to call it again in that case.
129 void RenderText::destroy()
131 if (!documentBeingDestroyed()) {
132 if (firstTextBox()) {
134 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
138 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
142 parent()->dirtyLinesFromChangedChild(this);
145 RenderObject::destroy();
148 void RenderText::extractTextBox(InlineTextBox* box)
150 m_lastTextBox = box->prevTextBox();
151 if (box == m_firstTextBox)
153 if (box->prevTextBox())
154 box->prevTextBox()->setNextLineBox(0);
155 box->setPreviousLineBox(0);
156 for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
157 curr->setExtracted();
160 void RenderText::attachTextBox(InlineTextBox* box)
163 m_lastTextBox->setNextLineBox(box);
164 box->setPreviousLineBox(m_lastTextBox);
167 m_firstTextBox = box;
168 InlineTextBox* last = box;
169 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
170 curr->setExtracted(false);
173 m_lastTextBox = last;
176 void RenderText::removeTextBox(InlineTextBox* box)
178 if (box == m_firstTextBox)
179 m_firstTextBox = box->nextTextBox();
180 if (box == m_lastTextBox)
181 m_lastTextBox = box->prevTextBox();
182 if (box->nextTextBox())
183 box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
184 if (box->prevTextBox())
185 box->prevTextBox()->setNextLineBox(box->nextTextBox());
188 void RenderText::deleteTextBoxes()
190 if (firstTextBox()) {
191 RenderArena* arena = renderArena();
192 InlineTextBox *curr = firstTextBox(), *next = 0;
194 next = curr->nextTextBox();
195 curr->destroy(arena);
198 m_firstTextBox = m_lastTextBox = 0;
202 bool RenderText::isTextFragment() const
207 PassRefPtr<StringImpl> RenderText::originalString() const
209 return element() ? element()->string() : 0;
212 void RenderText::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
214 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
215 rects.append(IntRect(_tx + box->xPos(),
221 DeprecatedValueList<IntRect> RenderText::lineBoxRects()
223 DeprecatedValueList<IntRect> rects;
225 absolutePositionForContent(x, y);
226 absoluteRects(rects, x, y);
230 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos) const
232 // The text runs point to parts of the rendertext's str string
233 // (they don't include '\n')
234 // Find the text run that includes the character at @p offset
235 // and return pos, which is the position of the char in the run.
240 InlineTextBox* s = m_firstTextBox;
242 while (offset > off && s->nextTextBox())
244 s = s->nextTextBox();
245 off = s->m_start + s->m_len;
247 // we are now in the correct text run
248 pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
252 VisiblePosition RenderText::positionForCoordinates(int x, int y)
254 if (!firstTextBox() || stringLength() == 0)
255 return VisiblePosition(element(), 0, DOWNSTREAM);
258 RenderBlock* cb = containingBlock();
259 cb->absolutePositionForContent(absx, absy);
260 if (cb->hasOverflowClip())
261 cb->layer()->subtractScrollOffset(absx, absy);
263 // Get the offset for the position, since this will take rtl text into account.
266 // FIXME: We should be able to roll these special cases into the general cases in the loop below.
267 if (firstTextBox() && y < absy + firstTextBox()->root()->bottomOverflow() && x < absx + firstTextBox()->m_x) {
268 // at the y coordinate of the first line or above
269 // and the x coordinate is to the left of the first text box left edge
270 offset = firstTextBox()->offsetForPosition(x - absx);
271 return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM);
273 if (lastTextBox() && y >= absy + lastTextBox()->root()->topOverflow() && x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
274 // at the y coordinate of the last line or below
275 // and the x coordinate is to the right of the last text box right edge
276 offset = lastTextBox()->offsetForPosition(x - absx);
277 return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM);
280 InlineTextBox* lastBoxAbove = 0;
281 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
282 if (y >= absy + box->root()->topOverflow()) {
283 if (y < absy + box->root()->bottomOverflow()) {
284 offset = box->offsetForPosition(x - absx);
286 if (x == absx + box->m_x)
287 // the x coordinate is equal to the left edge of this box
288 // the affinity must be downstream so the position doesn't jump back to the previous line
289 return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
291 if (x < absx + box->m_x + box->m_width)
292 // and the x coordinate is to the left of the right edge of this box
293 // check to see if position goes in this box
294 return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
296 if (!box->prevOnLine() && x < absx + box->m_x)
297 // box is first on line
298 // and the x coordinate is to the left of the first text box left edge
299 return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
301 if (!box->nextOnLine())
302 // box is last on line
303 // and the x coordinate is to the right of the last text box right edge
304 // generate VisiblePosition, use UPSTREAM affinity if possible
305 return VisiblePosition(element(), offset + box->m_start, VP_UPSTREAM_IF_POSSIBLE);
311 return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->m_start + lastBoxAbove->m_len : 0, DOWNSTREAM);
315 static RenderObject *firstRendererOnNextLine(InlineBox *box) __attribute__ ((unused));
317 static RenderObject *firstRendererOnNextLine(InlineBox *box)
322 RootInlineBox *root = box->root();
326 if (root->endsWithBreak())
329 RootInlineBox *nextRoot = root->nextRootBox();
333 InlineBox *firstChild = nextRoot->firstChild();
337 return firstChild->object();
340 static RenderObject *lastRendererOnPrevLine(InlineBox *box)
345 RootInlineBox *root = box->root();
349 if (root->endsWithBreak())
352 RootInlineBox *prevRoot = root->prevRootBox();
356 InlineBox *lastChild = prevRoot->lastChild();
360 return lastChild->object();
363 static inline bool atLineWrap(InlineTextBox* box, int offset)
365 return box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len;
368 IntRect RenderText::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
370 if (!firstTextBox() || stringLength() == 0)
373 // Find the text box for the given offset
374 InlineTextBox *box = 0;
375 for (box = firstTextBox(); box; box = box->nextTextBox()) {
376 if (box->containsCaretOffset(offset)) {
377 // Check if downstream affinity would make us move to the next line.
378 if (atLineWrap(box, offset) && affinity == DOWNSTREAM) {
379 // Use the next text box
380 box = box->nextTextBox();
381 offset = box->m_start;
383 InlineTextBox *prevBox = box->prevTextBox();
384 if (offset == box->m_start && affinity == UPSTREAM && prevBox && !box->prevOnLine()) {
387 offset = box->m_start + box->m_len;
389 RenderObject *object = lastRendererOnPrevLine(box);
391 return object->caretRect(0, affinity);
403 int height = box->root()->bottomOverflow() - box->root()->topOverflow();
404 int top = box->root()->topOverflow();
406 int left = box->positionForOffset(offset);
408 int rootLeft = box->root()->xPos();
409 // FIXME: should we use the width of the root inline box or the
410 // width of the containing block for this?
411 if (extraWidthToEndOfLine)
412 *extraWidthToEndOfLine = (box->root()->width() + rootLeft) - (left + 1);
415 absolutePositionForContent(absx, absy);
419 RenderBlock *cb = containingBlock();
420 if (style()->autoWrap()) {
421 int availableWidth = cb->lineWidth(top);
422 if (!box->m_reversed)
423 left = min(left, absx + rootLeft + availableWidth - 1);
425 left = max(left, absx + rootLeft);
428 return IntRect(left, top, 1, height);
431 void RenderText::posOfChar(int chr, int &x, int &y)
433 absolutePositionForContent(x, y);
436 if (InlineTextBox* s = findNextInlineTextBox(chr, pos)) {
437 // s is the line containing the character
438 x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
443 bool RenderText::allAscii() const
445 if (m_allAsciiChecked)
447 m_allAsciiChecked = true;
450 for (i = 0; i < str->length(); i++)
451 if ((*str)[i] > 0x7f) {
461 bool RenderText::shouldUseMonospaceCache(const Font *f) const
463 return (f && f->isFixedPitch() && allAscii() && !style()->font().isSmallCaps());
466 // We cache the width of the ' ' character for <pre> text. We could go further
467 // and cache a widths array for all styles, at the expense of increasing the size of the
469 void RenderText::cacheWidths()
471 const Font* f = font(false);
472 if (shouldUseMonospaceCache(f)) {
474 m_monospaceCharacterWidth = f->width(TextRun(&c, 1));
476 m_monospaceCharacterWidth = 0;
480 ALWAYS_INLINE int RenderText::widthFromCache(const Font* f, int start, int len, int tabWidth, int xpos) const
482 if (m_monospaceCharacterWidth != 0) {
484 for (i = start; i < start+len; i++) {
486 UCharDirection dir = u_charDirection(c);
487 if (dir != U_DIR_NON_SPACING_MARK && dir != U_BOUNDARY_NEUTRAL) {
488 if (c == '\t' && tabWidth != 0)
489 w += tabWidth - ((xpos + w) % tabWidth);
491 w += m_monospaceCharacterWidth;
492 if (DeprecatedChar(c).isSpace() && i > start && !DeprecatedChar((*str)[i - 1]).isSpace())
493 w += f->wordSpacing();
500 return f->width(TextRun(string(), start, len, 0), TextStyle(tabWidth, xpos));
503 void RenderText::trimmedMinMaxWidth(int leadWidth,
504 int& beginMinW, bool& beginWS,
505 int& endMinW, bool& endWS,
506 bool& hasBreakableChar, bool& hasBreak,
507 int& beginMaxW, int& endMaxW,
508 int& minW, int& maxW, bool& stripFrontSpaces)
510 bool collapseWhiteSpace = style()->collapseWhiteSpace();
511 if (!collapseWhiteSpace)
512 stripFrontSpaces = false;
514 int len = str->length();
515 if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
521 // if the text has a variable width tab, we need to call
523 calcMinMaxWidth(leadWidth);
527 beginWS = !stripFrontSpaces && m_hasBeginWS;
530 beginMinW = m_beginMinWidth;
531 endMinW = m_endMinWidth;
533 hasBreakableChar = m_hasBreakableChar;
534 hasBreak = m_hasBreak;
536 if (stripFrontSpaces && ((*str)[0] == ' ' || ((*str)[0] == '\n' && !style()->preserveNewline()) || (*str)[0] == '\t')) {
537 const Font *f = font(false); // FIXME: Why is it ok to ignore first-line here?
538 const UChar space = ' ';
539 int spaceWidth = f->width(TextRun(&space, 1));
540 maxW -= spaceWidth + f->wordSpacing();
543 stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
545 if (!style()->autoWrap() || minW > maxW)
548 // Compute our max widths by scanning the string for newlines.
550 const Font *f = font(false);
551 bool firstLine = true;
552 beginMaxW = endMaxW = maxW;
553 for (int i = 0; i < len; i++)
556 while (i+linelen < len && (*str)[i+linelen] != '\n')
561 endMaxW = widthFromCache(f, i, linelen, tabWidth(), leadWidth + endMaxW);
569 else if (firstLine) {
576 // A <pre> run that ends with a newline, as in, e.g.,
577 // <pre>Some text\n\n<span>More text</pre>
583 void RenderText::calcMinMaxWidth()
585 // Use 0 for the leadWidth. If the text contains a variable width tab, the real width
586 // will get measured when trimmedMinMaxWidth calls again with the real leadWidth.
587 ASSERT( !minMaxKnown() );
591 void RenderText::calcMinMaxWidth(int leadWidth)
593 // ### calc Min and Max width...
594 m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
600 int currMinWidth = 0;
601 int currMaxWidth = 0;
602 m_hasBreakableChar = m_hasBreak = m_hasTab = m_hasBeginWS = m_hasEndWS = false;
604 // FIXME: not 100% correct for first-line
605 const Font* f = font(false);
606 int wordSpacing = style()->wordSpacing();
607 int len = str->length();
608 const UChar* txt = str->characters();
609 bool needsWordSpacing = false;
610 bool ignoringSpaces = false;
611 bool isSpace = false;
612 bool firstWord = true;
613 bool firstLine = true;
614 int nextBreakable = -1;
615 for (int i = 0; i < len; i++) {
618 bool previousCharacterIsSpace = isSpace;
620 bool isNewline = false;
622 if (style()->preserveNewline()) {
628 } else if (c == '\t') {
629 if (!style()->collapseWhiteSpace()) {
638 if ((isSpace || isNewline) && i == 0)
640 if ((isSpace || isNewline) && i == len-1)
643 if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
644 ignoringSpaces = true;
646 if (ignoringSpaces && !isSpace)
647 ignoringSpaces = false;
649 // Ignore spaces and soft hyphens
650 if (ignoringSpaces || c == SOFT_HYPHEN) {
654 bool hasBreak = isBreakable(txt, i, len, nextBreakable);
656 while (c != '\n' && c != ' ' && c != '\t' && c != SOFT_HYPHEN) {
661 if (isBreakable(txt, j, len, nextBreakable))
667 int w = widthFromCache(f, i, wordlen, tabWidth(), leadWidth + currMaxWidth);
671 bool isSpace = (j < len) && c == ' ';
672 bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
673 if (j < len && style()->autoWrap())
674 m_hasBreakableChar = true;
676 // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
677 // last word in the run.
678 if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
679 currMaxWidth += wordSpacing;
683 // If the first character in the run is breakable, then we consider ourselves to have a beginning
684 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
685 // being appended to a previous text run when considering the total minimum width of the containing block.
687 m_hasBreakableChar = true;
688 m_beginMinWidth = hasBreak ? 0 : w;
692 if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
698 // Nowrap can never be broken, so don't bother setting the
699 // breakable character boolean. Pre can only be broken if we encounter a newline.
700 if (style()->autoWrap() || isNewline)
701 m_hasBreakableChar = true;
703 if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
706 if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
710 m_beginMinWidth = currMaxWidth;
713 if (currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
718 currMaxWidth += f->width(TextRun(txt + i, 1), TextStyle(tabWidth(), leadWidth + currMaxWidth));
719 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len-1;
724 if (needsWordSpacing && len > 1)
725 currMaxWidth += wordSpacing;
727 m_minWidth = max(currMinWidth, m_minWidth);
728 m_maxWidth = max(currMaxWidth, m_maxWidth);
730 if (!style()->autoWrap())
731 m_minWidth = m_maxWidth;
733 if (style()->whiteSpace() == PRE) {
734 // FIXME: pre-wrap and pre-line need to be dealt with possibly? This code won't be right
737 m_beginMinWidth = m_maxWidth;
738 m_endMinWidth = currMaxWidth;
742 //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
745 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
749 currPos < from+len && ((*str)[currPos] == '\n' || (*str)[currPos] == ' ' || (*str)[currPos] == '\t');
751 return currPos >= (from+len);
754 int RenderText::minXPos() const
756 if (!m_firstTextBox) return 0;
758 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
759 retval = min(retval, (int)box->m_x);
763 int RenderText::xPos() const
765 return m_firstTextBox ? m_firstTextBox->m_x : 0;
768 int RenderText::yPos() const
770 return m_firstTextBox ? m_firstTextBox->m_y : 0;
773 const Font& RenderText::font()
775 return style()->font();
778 void RenderText::setSelectionState(SelectionState s)
782 m_selectionState = s;
783 if (s == SelectionStart || s == SelectionEnd || s == SelectionBoth) {
784 int startPos, endPos;
785 selectionStartEnd(startPos, endPos);
786 if(selectionState() == SelectionStart) {
787 endPos = str->length();
789 // to handle selection from end of text to end of line
790 if (startPos != 0 && startPos == endPos) {
791 startPos = endPos - 1;
793 } else if(selectionState() == SelectionEnd)
796 for (box = firstTextBox(); box; box = box->nextTextBox()) {
797 if (box->isSelected(startPos, endPos)) {
798 RootInlineBox* line = box->root();
800 line->setHasSelectedChildren(true);
805 for (box = firstTextBox(); box; box = box->nextTextBox()) {
806 RootInlineBox* line = box->root();
808 line->setHasSelectedChildren(s == SelectionInside);
812 containingBlock()->setSelectionState(s);
815 void RenderText::setTextWithOffset(StringImpl *text, unsigned offset, unsigned len, bool force)
817 unsigned oldLen = str ? str->length() : 0;
818 unsigned newLen = text ? text->length() : 0;
819 int delta = newLen - oldLen;
820 unsigned end = len ? offset+len-1 : offset;
822 RootInlineBox* firstRootBox = 0;
823 RootInlineBox* lastRootBox = 0;
825 bool dirtiedLines = false;
827 // Dirty all text boxes that include characters in between offset and offset+len.
828 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
829 // Text run is entirely before the affected range.
830 if (curr->end() < offset)
833 // Text run is entirely after the affected range.
834 if (curr->start() > end) {
835 curr->offsetRun(delta);
836 RootInlineBox* root = curr->root();
839 if (!dirtiedLines) { // The affected area was in between two runs. Go ahead and mark the root box of the run after the affected area as dirty.
840 firstRootBox->markDirty();
846 else if (curr->end() >= offset && curr->end() <= end) {
847 curr->dirtyLineBoxes(); // Text run overlaps with the left end of the affected range.
850 else if (curr->start() <= offset && curr->end() >= end) {
851 curr->dirtyLineBoxes(); // Text run subsumes the affected range.
854 else if (curr->start() <= end && curr->end() >= end) {
855 curr->dirtyLineBoxes(); // Text run overlaps with right end of the affected range.
860 // Now we have to walk all of the clean lines and adjust their cached line break information
861 // to reflect our updated offsets.
863 lastRootBox = lastRootBox->nextRootBox();
865 RootInlineBox* prev = firstRootBox->prevRootBox();
869 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
870 if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
871 curr->setLineBreakPos(curr->lineBreakPos()+delta);
874 m_linesDirty = dirtiedLines;
875 setText(text, force);
878 #define BULLET_CHAR 0x2022
879 #define SQUARE_CHAR 0x25AA
880 #define CIRCLE_CHAR 0x25E6
882 void RenderText::setText(StringImpl *text, bool force)
886 if (!force && str == text)
889 m_allAsciiChecked = false;
893 str = str->replace('\\', backslashAsCurrencySymbol());
895 switch (style()->textTransform()) {
898 // find previous text renderer if one exists
900 UChar previous = ' ';
901 for (o = previousInPreOrder(); o && (o->isInlineFlow() || o->isText() && static_cast<RenderText*>(o)->string()->length() == 0); o = o->previousInPreOrder())
903 if (o && o->isText()) {
904 StringImpl* prevStr = static_cast<RenderText*>(o)->string();
905 previous = (*prevStr)[prevStr->length() - 1];
907 str = str->capitalize(previous);
910 case UPPERCASE: str = str->upper(); break;
911 case LOWERCASE: str = str->lower(); break;
916 switch(style()->textSecurity()) {
918 str = str->secure(BULLET_CHAR);
921 str = str->secure(CIRCLE_CHAR);
924 str = str->secure(SQUARE_CHAR);
934 // ### what should happen if we change the text of a
936 ASSERT(!isBR() || (str->length() == 1 && (*str)[0] == '\n'));
937 ASSERT(!str->length() || str->characters());
939 setNeedsLayoutAndMinMaxRecalc();
942 int RenderText::height() const
946 retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y;
950 short RenderText::lineHeight(bool firstLine, bool) const
952 // Always use the interior line height of the parent (e.g., if our parent is an inline block).
953 return parent()->lineHeight(firstLine, true);
956 void RenderText::dirtyLineBoxes(bool fullLayout, bool)
960 else if (!m_linesDirty) {
961 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
962 box->dirtyLineBoxes();
964 m_linesDirty = false;
967 InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool)
969 ASSERT(!isRootLineBox);
970 InlineTextBox* textBox = new (renderArena()) InlineTextBox(this);
972 m_firstTextBox = m_lastTextBox = textBox;
974 m_lastTextBox->setNextLineBox(textBox);
975 textBox->setPreviousLineBox(m_lastTextBox);
976 m_lastTextBox = textBox;
981 void RenderText::position(InlineBox* box)
983 InlineTextBox *s = static_cast<InlineTextBox*>(box);
985 // ### should not be needed!!!
987 // We want the box to be destroyed.
989 s->destroy(renderArena());
990 m_firstTextBox = m_lastTextBox = 0;
994 m_containsReversedText |= s->m_reversed;
997 unsigned int RenderText::width(unsigned int from, unsigned int len, int xpos, bool firstLine) const
999 if (from >= str->length())
1001 if (from + len > str->length())
1002 len = str->length() - from;
1004 const Font *f = font(firstLine);
1005 return width(from, len, f, xpos);
1008 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f, int xpos) const
1010 if (!str->characters() || from > str->length())
1012 if (from + len > str->length())
1013 len = str->length() - from;
1016 if (!style()->preserveNewline() && f == &style()->font() && from == 0 && len == str->length())
1018 else if (f == &style()->font())
1019 w = widthFromCache(f, from, len, tabWidth(), xpos);
1021 w = f->width(TextRun(string(), from, len, 0), TextStyle(tabWidth(), xpos));
1026 int RenderText::width() const
1028 int minx = 100000000;
1031 for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) {
1034 if(s->m_x + s->m_width > maxx)
1035 maxx = s->m_x + s->m_width;
1038 return max(0, maxx-minx);
1041 IntRect RenderText::getAbsoluteRepaintRect()
1043 RenderObject *cb = containingBlock();
1044 return cb->getAbsoluteRepaintRect();
1047 IntRect RenderText::selectionRect()
1050 if (selectionState() == SelectionNone)
1052 RenderBlock* cb = containingBlock();
1056 // Now calculate startPos and endPos for painting selection.
1057 // We include a selection while endPos > 0
1058 int startPos, endPos;
1059 if (selectionState() == SelectionInside) {
1060 // We are fully selected.
1062 endPos = str->length();
1064 selectionStartEnd(startPos, endPos);
1065 if (selectionState() == SelectionStart)
1066 endPos = str->length();
1067 else if (selectionState() == SelectionEnd)
1071 if (startPos == endPos)
1075 cb->absolutePositionForContent(absx, absy);
1076 RenderLayer* layer = cb->layer();
1078 layer->subtractScrollOffset(absx, absy);
1079 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1080 rect.unite(box->selectionRect(absx, absy, startPos, endPos));
1085 short RenderText::verticalPositionHint( bool firstLine ) const
1087 return parent()->verticalPositionHint( firstLine );
1090 const Font *RenderText::font(bool firstLine) const
1092 return &style(firstLine)->font();
1095 int RenderText::caretMinOffset() const
1097 InlineTextBox *box = firstTextBox();
1100 int minOffset = box->m_start;
1101 for (box = box->nextTextBox(); box; box = box->nextTextBox())
1102 minOffset = min(minOffset, box->m_start);
1106 int RenderText::caretMaxOffset() const
1108 InlineTextBox* box = lastTextBox();
1110 return str->length();
1111 int maxOffset = box->m_start + box->m_len;
1112 for (box = box->prevTextBox(); box; box = box->prevTextBox())
1113 maxOffset = max(maxOffset,box->m_start + box->m_len);
1117 unsigned RenderText::caretMaxRenderedOffset() const
1120 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1125 InlineBox* RenderText::inlineBox(int offset, EAffinity affinity)
1127 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1128 if (box->containsCaretOffset(offset)) {
1129 if (atLineWrap(box, offset) && affinity == DOWNSTREAM)
1130 return box->nextTextBox();
1133 if (offset < box->m_start)
1134 // The offset we're looking for is before this node
1135 // this means the offset must be in content that is
1137 return box->prevTextBox() ? box->prevTextBox() : firstTextBox();