Reviewed by Darin Adler.
[WebKit-https.git] / WebCore / rendering / InlineTextBox.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "InlineTextBox.h"
25
26 #include "Document.h"
27 #include "Editor.h"
28 #include "Frame.h"
29 #include "GraphicsContext.h"
30 #include "HitTestResult.h"
31 #include "RenderArena.h"
32 #include "RenderBlock.h"
33 #include "RenderTheme.h"
34 #include "Text.h"
35 #include "break_lines.h"
36 #include <wtf/AlwaysInline.h>
37
38 using namespace std;
39
40 namespace WebCore {
41
42 int InlineTextBox::selectionTop()
43 {
44     return root()->selectionTop();
45 }
46
47 int InlineTextBox::selectionHeight()
48 {
49     return root()->selectionHeight();
50 }
51
52 bool InlineTextBox::isSelected(int startPos, int endPos) const
53 {
54     int sPos = max(startPos - m_start, 0);
55     int ePos = min(endPos - m_start, (int)m_len);
56     return (sPos < ePos);
57 }
58
59 RenderObject::SelectionState InlineTextBox::selectionState()
60 {
61     RenderObject::SelectionState state = object()->selectionState();
62     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
63         int startPos, endPos;
64         object()->selectionStartEnd(startPos, endPos);
65         // The position after a hard line break is considered to be past its end.
66         int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
67
68         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
69         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
70         if (start && end)
71             state = RenderObject::SelectionBoth;
72         else if (start)
73             state = RenderObject::SelectionStart;
74         else if (end)
75             state = RenderObject::SelectionEnd;
76         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
77                  (state == RenderObject::SelectionStart || endPos > lastSelectable))
78             state = RenderObject::SelectionInside;
79         else if (state == RenderObject::SelectionBoth)
80             state = RenderObject::SelectionNone;
81     }
82     return state;
83 }
84
85 IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
86 {
87     int sPos = max(startPos - m_start, 0);
88     int ePos = min(endPos - m_start, (int)m_len);
89     
90     if (sPos >= ePos)
91         return IntRect();
92
93     RenderText* textObj = textObject();
94     int selTop = selectionTop();
95     int selHeight = selectionHeight();
96     const Font& f = textObj->style(m_firstLine)->font();
97
98     IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
99                                                         IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos));
100     if (r.x() > tx + m_x + m_width)
101         r.setWidth(0);
102     else if (r.right() - 1 > tx + m_x + m_width)
103         r.setWidth(tx + m_x + m_width - r.x());
104     return r;
105 }
106
107 void InlineTextBox::deleteLine(RenderArena* arena)
108 {
109     static_cast<RenderText*>(m_object)->removeTextBox(this);
110     destroy(arena);
111 }
112
113 void InlineTextBox::extractLine()
114 {
115     if (m_extracted)
116         return;
117
118     static_cast<RenderText*>(m_object)->extractTextBox(this);
119 }
120
121 void InlineTextBox::attachLine()
122 {
123     if (!m_extracted)
124         return;
125     
126     static_cast<RenderText*>(m_object)->attachTextBox(this);
127 }
128
129 int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
130 {
131     if (foundBox) {
132         m_truncation = cFullTruncation;
133         return -1;
134     }
135
136     int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
137     
138     // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
139     if (ltr) {
140         if (ellipsisX <= m_x) {
141             // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
142             m_truncation = cFullTruncation;
143             foundBox = true;
144             return -1;
145         }
146
147         if (ellipsisX < m_x + m_width) {
148             if (m_reversed)
149                 return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
150
151             foundBox = true;
152
153             int offset = offsetForPosition(ellipsisX, false);
154             if (offset == 0) {
155                 // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
156                 // and the ellipsis edge.
157                 m_truncation = cFullTruncation;
158                 return min(ellipsisX, m_x);
159             }
160             
161             // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
162             m_truncation = offset;
163             return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, textPos(), m_firstLine);
164         }
165     }
166     else {
167         // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
168     }
169     return -1;
170 }
171
172 Color correctedTextColor(Color textColor, Color backgroundColor) 
173 {
174     // Adjust the text color if it is too close to the background color,
175     // by darkening or lightening it to move it further away.
176     
177     int d = differenceSquared(textColor, backgroundColor);
178     // semi-arbitrarily chose 65025 (255^2) value here after a few tests; 
179     if (d > 65025) {
180         return textColor;
181     }
182     
183     int distanceFromWhite = differenceSquared(textColor, Color::white);
184     int distanceFromBlack = differenceSquared(textColor, Color::black);
185
186     if (distanceFromWhite < distanceFromBlack) {
187         return textColor.dark();
188     }
189     
190     return textColor.light();
191 }
192
193 void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness)
194 {
195     int mode = context->textDrawingMode();
196     if (strokeThickness > 0) {
197         int newMode = mode | cTextStroke;
198         if (mode != newMode) {
199             context->setTextDrawingMode(newMode);
200             mode = newMode;
201         }
202     }
203     
204     if (mode & cTextFill && fillColor != context->fillColor())
205         context->setFillColor(fillColor);
206
207     if (mode & cTextStroke) {
208         if (strokeColor != context->strokeColor())
209             context->setStrokeColor(strokeColor);
210         if (strokeThickness != context->strokeThickness())
211             context->setStrokeThickness(strokeThickness);
212     }
213 }
214
215 bool InlineTextBox::isLineBreak() const
216 {
217     return object()->isBR() || (object()->style()->preserveNewline() && len() == 1 && (*textObject()->text())[start()] == '\n');
218 }
219
220 bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
221 {
222     if (isLineBreak())
223         return false;
224
225     IntRect rect(tx + m_x, ty + m_y, m_width, m_height);
226     if (m_truncation != cFullTruncation && object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
227         object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
228         return true;
229     }
230     return false;
231 }
232
233 void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
234 {
235     if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE ||
236             m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline)
237         return;
238     
239     ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines);
240
241     int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow();
242     int w = width() + 2 * parent()->maxHorizontalVisualOverflow();
243     if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x())
244         return;
245
246     bool isPrinting = textObject()->document()->printing();
247     
248     // Determine whether or not we're selected.
249     bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
250     if (!haveSelection && paintInfo.phase == PaintPhaseSelection)
251         // When only painting the selection, don't bother to paint if there is none.
252         return;
253
254     // Determine whether or not we have composition underlines to draw.
255     bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node();
256     bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines();
257
258     // Set our font.
259     RenderStyle* styleToUse = object()->style(m_firstLine);
260     int d = styleToUse->textDecorationsInEffect();
261     const Font* font = &styleToUse->font();
262     if (*font != paintInfo.context->font())
263         paintInfo.context->setFont(*font);
264
265     // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
266     // and composition underlines.
267     if (paintInfo.phase != PaintPhaseSelection && !isPrinting) {
268 #if PLATFORM(MAC)
269         // Custom highlighters go behind everything else.
270         if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
271             paintCustomHighlight(tx, ty, styleToUse->highlight());
272 #endif
273
274         if (containsComposition && !useCustomUnderlines)
275             paintCompositionBackground(paintInfo.context, tx, ty, styleToUse, font,
276                 object()->document()->frame()->editor()->compositionStart(),
277                 object()->document()->frame()->editor()->compositionEnd());
278
279         paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, true);
280
281         if (haveSelection && !useCustomUnderlines)
282             paintSelection(paintInfo.context, tx, ty, styleToUse, font);
283     }
284
285     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
286     if (m_len <= 0)
287         return;
288
289     Color textFillColor;
290     Color textStrokeColor;
291     float textStrokeWidth = styleToUse->textStrokeWidth();
292
293     if (paintInfo.forceBlackText) {
294         textFillColor = Color::black;
295         textStrokeColor = Color::black;
296     } else {
297         textFillColor = styleToUse->textFillColor();
298         if (!textFillColor.isValid())
299             textFillColor = styleToUse->color();
300         
301         // Make the text fill color legible against a white background
302         if (styleToUse->forceBackgroundsToWhite())
303             textFillColor = correctedTextColor(textFillColor, Color::white);
304             
305         textStrokeColor = styleToUse->textStrokeColor();
306         if (!textStrokeColor.isValid())
307             textStrokeColor = styleToUse->color();
308         
309         // Make the text stroke color legible against a white background
310         if (styleToUse->forceBackgroundsToWhite())
311             textStrokeColor = correctedTextColor(textStrokeColor, Color::white);
312     }
313
314     // For stroked painting, we have to change the text drawing mode.  It's probably dangerous to leave that mutated as a side
315     // effect, so only when we know we're stroking, do a save/restore.
316     if (textStrokeWidth > 0)
317         paintInfo.context->save();
318
319     updateGraphicsContext(paintInfo.context, textFillColor, textStrokeColor, textStrokeWidth);
320     
321     // Set a text shadow if we have one.
322     // FIXME: Support multiple shadow effects.  Need more from the CG API before
323     // we can do this.
324     bool setShadow = false;
325     if (styleToUse->textShadow()) {
326         paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y),
327                                      styleToUse->textShadow()->blur, styleToUse->textShadow()->color);
328         setShadow = true;
329     }
330
331     bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
332     bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
333                                               // necessary when a custom ::selection foreground color is applied.
334     Color selectionFillColor = textFillColor;
335     Color selectionStrokeColor = textStrokeColor;
336     float selectionStrokeWidth = textStrokeWidth;
337     ShadowData* selectionTextShadow = 0;
338     if (haveSelection) {
339         // Check foreground color first.
340         Color foreground = object()->selectionForegroundColor();
341         if (foreground.isValid() && foreground != selectionFillColor) {
342             if (!paintSelectedTextOnly)
343                 paintSelectedTextSeparately = true;
344             selectionFillColor = foreground;
345         }
346         RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
347         if (pseudoStyle) {
348             if (pseudoStyle->textShadow()) {
349                 if (!paintSelectedTextOnly)
350                     paintSelectedTextSeparately = true;
351                 if (pseudoStyle->textShadow())
352                     selectionTextShadow = pseudoStyle->textShadow();
353             }
354             
355             float strokeWidth = pseudoStyle->textStrokeWidth();
356             if (strokeWidth != selectionStrokeWidth) {
357                 if (!paintSelectedTextOnly)
358                     paintSelectedTextSeparately = true;
359                 selectionStrokeWidth = strokeWidth;
360             }
361
362             Color stroke = pseudoStyle->textStrokeColor();
363             if (!stroke.isValid())
364                 stroke = pseudoStyle->color();
365             if (stroke != selectionStrokeColor) {
366                 if (!paintSelectedTextOnly)
367                     paintSelectedTextSeparately = true;
368                 selectionStrokeColor = stroke;
369             }
370         }
371     }
372
373     StringImpl* textStr = textObject()->text();
374
375     if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
376         // paint all the text
377         // FIXME: Handle RTL direction, handle reversed strings.  For now truncation can only be turned on
378         // for non-reversed LTR strings.
379         int endPoint = m_len;
380         if (m_truncation != cNoTruncation)
381             endPoint = m_truncation;
382         paintInfo.context->drawText(TextRun(textStr->characters() + m_start, endPoint, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
383                                     IntPoint(m_x + tx, m_y + ty + m_baseline));
384     } else {
385         int sPos, ePos;
386         selectionStartEnd(sPos, ePos);
387         if (paintSelectedTextSeparately) {
388             // paint only the text that is not selected
389             if (sPos >= ePos)
390                 paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
391                                             IntPoint(m_x + tx, m_y + ty + m_baseline));
392             else {
393                 if (sPos - 1 >= 0)
394                     paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
395                                                 IntPoint(m_x + tx, m_y + ty + m_baseline),  0, sPos);
396                 if (ePos < m_start + m_len)
397                     paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
398                                                 IntPoint(m_x + tx, m_y + ty + m_baseline), ePos);
399             }
400         }
401
402         if (sPos < ePos) {
403             // paint only the text that is selected
404             if (selectionStrokeWidth > 0)
405                 paintInfo.context->save();
406         
407             updateGraphicsContext(paintInfo.context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth);
408
409             if (selectionTextShadow)
410                 paintInfo.context->setShadow(IntSize(selectionTextShadow->x, selectionTextShadow->y),
411                                              selectionTextShadow->blur, selectionTextShadow->color);
412             paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
413                                         IntPoint(m_x + tx, m_y + ty + m_baseline), sPos, ePos);
414             if (selectionTextShadow)
415                 paintInfo.context->clearShadow();
416                 
417             if (selectionStrokeWidth > 0)
418                 paintInfo.context->restore();
419         }
420     }
421
422     // Paint decorations
423     if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) {
424         paintInfo.context->setStrokeColor(styleToUse->color());
425         paintDecoration(paintInfo.context, tx, ty, d);
426     }
427
428     if (paintInfo.phase != PaintPhaseSelection) {
429         paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false);
430
431         if (useCustomUnderlines) {
432             const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines();
433             size_t numUnderlines = underlines.size();
434
435             for (size_t index = 0; index < numUnderlines; ++index) {
436                 const CompositionUnderline& underline = underlines[index];
437
438                 if (underline.endOffset <= start())
439                     // underline is completely before this run.  This might be an underline that sits
440                     // before the first run we draw, or underlines that were within runs we skipped 
441                     // due to truncation.
442                     continue;
443                 
444                 if (underline.startOffset <= end()) {
445                     // underline intersects this run.  Paint it.
446                     paintCompositionUnderline(paintInfo.context, tx, ty, underline);
447                     if (underline.endOffset > end() + 1)
448                         // underline also runs into the next run. Bail now, no more marker advancement.
449                         break;
450                 } else
451                     // underline is completely after this run, bail.  A later run will paint it.
452                     break;
453             }
454         }
455     }
456
457     if (setShadow)
458         paintInfo.context->clearShadow();
459         
460     if (textStrokeWidth > 0)
461         paintInfo.context->restore();
462 }
463
464 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
465 {
466     int startPos, endPos;
467     if (object()->selectionState() == RenderObject::SelectionInside) {
468         startPos = 0;
469         endPos = textObject()->textLength();
470     } else {
471         textObject()->selectionStartEnd(startPos, endPos);
472         if (object()->selectionState() == RenderObject::SelectionStart)
473             endPos = textObject()->textLength();
474         else if (object()->selectionState() == RenderObject::SelectionEnd)
475             startPos = 0;
476     }
477
478     sPos = max(startPos - m_start, 0);
479     ePos = min(endPos - m_start, (int)m_len);
480 }
481
482 void InlineTextBox::paintSelection(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f)
483 {
484     // See if we have a selection to paint at all.
485     int sPos, ePos;
486     selectionStartEnd(sPos, ePos);
487     if (sPos >= ePos)
488         return;
489
490     Color textColor = style->color();
491     Color c = object()->selectionBackgroundColor();
492     if (!c.isValid() || c.alpha() == 0)
493         return;
494
495     // If the text color ends up being the same as the selection background, invert the selection
496     // background.  This should basically never happen, since the selection has transparency.
497     if (textColor == c)
498         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
499
500     p->save();
501     updateGraphicsContext(p, c, c, 0);  // Don't draw text at all!
502     int y = selectionTop();
503     int h = selectionHeight();
504     p->clip(IntRect(m_x + tx, y + ty, m_width, h));
505     p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
506                             IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
507     p->restore();
508 }
509
510 void InlineTextBox::paintCompositionBackground(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f, int startPos, int endPos)
511 {
512     int offset = m_start;
513     int sPos = max(startPos - offset, 0);
514     int ePos = min(endPos - offset, (int)m_len);
515
516     if (sPos >= ePos)
517         return;
518
519     p->save();
520
521     Color c = Color(225, 221, 85);
522     
523     updateGraphicsContext(p, c, c, 0); // Don't draw text at all!
524
525     int y = selectionTop();
526     int h = selectionHeight();
527     p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
528                             IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
529     p->restore();
530 }
531
532 #if PLATFORM(MAC)
533 void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type)
534 {
535     RootInlineBox* r = root();
536     FloatRect rootRect(tx + r->xPos(), ty + selectionTop(), r->width(), selectionHeight());
537     FloatRect textRect(tx + xPos(), rootRect.y(), width(), rootRect.height());
538
539     object()->document()->frame()->paintCustomHighlight(type, textRect, rootRect, true, false, object()->node());
540 }
541 #endif
542
543 void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, int deco)
544 {
545     tx += m_x;
546     ty += m_y;
547
548     if (m_truncation == cFullTruncation)
549         return;
550     
551     int width = (m_truncation == cNoTruncation) ? m_width
552         : static_cast<RenderText*>(m_object)->width(m_start, m_truncation, textPos(), m_firstLine);
553     
554     // Get the text decoration colors.
555     Color underline, overline, linethrough;
556     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
557     
558     // Use a special function for underlines to get the positioning exactly right.
559     bool isPrinting = textObject()->document()->printing();
560     context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1.
561     if (deco & UNDERLINE) {
562         context->setStrokeColor(underline);
563         // Leave one pixel of white between the baseline and the underline.
564         context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), width, isPrinting);
565     }
566     if (deco & OVERLINE) {
567         context->setStrokeColor(overline);
568         context->drawLineForText(IntPoint(tx, ty), width, isPrinting);
569     }
570     if (deco & LINE_THROUGH) {
571         context->setStrokeColor(linethrough);
572         context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), width, isPrinting);
573     }
574 }
575
576 void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f, bool grammar)
577 {
578     // Never print spelling/grammar markers (5327887)
579     if (textObject()->document()->printing())
580         return;
581
582     if (m_truncation == cFullTruncation)
583         return;
584
585     tx += m_x;
586     ty += m_y;
587         
588     int start = 0;                  // start of line to draw, relative to tx
589     int width = m_width;            // how much line to draw
590     bool useWholeWidth = true;
591     unsigned paintStart = m_start;
592     unsigned paintEnd = end()+1;      // end points at the last char, not past it
593     if (paintStart <= marker.startOffset) {
594         paintStart = marker.startOffset;
595         useWholeWidth = false;
596         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
597     }
598     if (paintEnd != marker.endOffset) {      // end points at the last char, not past it
599         paintEnd = min(paintEnd, marker.endOffset);
600         useWholeWidth = false;
601     }
602     if (m_truncation != cNoTruncation) {
603         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
604         useWholeWidth = false;
605     }
606     if (!useWholeWidth) {
607         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
608     }
609     
610     // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
611     // display a toolTip. We don't do this for misspelling markers.
612     if (grammar) {
613         int y = selectionTop();
614         IntPoint startPoint = IntPoint(m_x + tx, y + ty);
615         int startPosition = max(marker.startOffset - m_start, (unsigned)0);
616         int endPosition = min(marker.endOffset - m_start, (unsigned)m_len);    
617         TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
618         IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, startPoint, selectionHeight(), startPosition, endPosition));
619         object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect);
620     }
621     
622     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
623     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
624     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
625     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
626     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
627     // we pin to two pixels under the baseline.
628     int lineThickness = cMisspellingLineThickness;
629     int descent = m_height - m_baseline;
630     int underlineOffset;
631     if (descent <= (2 + lineThickness)) {
632         // place the underline at the very bottom of the text in small/medium fonts
633         underlineOffset = m_height - lineThickness;
634     } else {
635         // in larger fonts, tho, place the underline up near the baseline to prevent big gap
636         underlineOffset = m_baseline + 2;
637     }
638     pt->drawLineForMisspellingOrBadGrammar(IntPoint(tx + start, ty + underlineOffset), width, grammar);
639 }
640
641 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f)
642 {
643    // Use same y positioning and height as for selection, so that when the selection and this highlight are on
644    // the same word there are no pieces sticking out.
645     int y = selectionTop();
646     int h = selectionHeight();
647     
648     int sPos = max(marker.startOffset - m_start, (unsigned)0);
649     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);    
650     TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
651     IntPoint startPoint = IntPoint(m_x + tx, y + ty);
652     
653     // Always compute and store the rect associated with this marker
654     IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, startPoint, h, sPos, ePos));
655     object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect);
656      
657     // Optionally highlight the text
658     if (object()->document()->frame()->markedTextMatchesAreHighlighted()) {
659         Color color = theme()->platformTextSearchHighlightColor();
660         pt->save();
661         updateGraphicsContext(pt, color, color, 0);  // Don't draw text at all!
662         pt->clip(IntRect(tx + m_x, ty + y, m_width, h));
663         pt->drawHighlightForText(run, startPoint, h, color, sPos, ePos);
664         pt->restore();
665     }
666 }
667
668 void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, RenderStyle* style, const Font* f, bool background)
669 {
670     Vector<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
671     Vector<DocumentMarker>::iterator markerIt = markers.begin();
672
673     // Give any document markers that touch this run a chance to draw before the text has been drawn.
674     // Note end() points at the last char, not one past it like endOffset and ranges do.
675     for ( ; markerIt != markers.end(); markerIt++) {
676         DocumentMarker marker = *markerIt;
677         
678         // Paint either the background markers or the foreground markers, but not both
679         switch (marker.type) {
680             case DocumentMarker::Grammar:
681             case DocumentMarker::Spelling:
682                 if (background)
683                     continue;
684                 break;
685                 
686             case DocumentMarker::TextMatch:
687                 if (!background)
688                     continue;
689                 break;
690             
691             default:
692                 ASSERT_NOT_REACHED();
693         }
694
695         if (marker.endOffset <= start())
696             // marker is completely before this run.  This might be a marker that sits before the
697             // first run we draw, or markers that were within runs we skipped due to truncation.
698             continue;
699         
700         if (marker.startOffset > end())
701             // marker is completely after this run, bail.  A later run will paint it.
702             break;
703         
704         // marker intersects this run.  Paint it.
705         switch (marker.type) {
706             case DocumentMarker::Spelling:
707                 paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, false);
708                 break;
709             case DocumentMarker::Grammar:
710                 paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, true);
711                 break;
712             case DocumentMarker::TextMatch:
713                 paintTextMatchMarker(pt, tx, ty, marker, style, f);
714                 break;
715             default:
716                 ASSERT_NOT_REACHED();
717         }
718
719         if (marker.endOffset > end() + 1)
720             // marker also runs into the next run. Bail now, no more marker advancement.
721             break;
722     }
723 }
724
725
726 void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int ty, const CompositionUnderline& underline)
727 {
728     tx += m_x;
729     ty += m_y;
730
731     if (m_truncation == cFullTruncation)
732         return;
733     
734     int start = 0;                 // start of line to draw, relative to tx
735     int width = m_width;           // how much line to draw
736     bool useWholeWidth = true;
737     unsigned paintStart = m_start;
738     unsigned paintEnd = end() + 1; // end points at the last char, not past it
739     if (paintStart <= underline.startOffset) {
740         paintStart = underline.startOffset;
741         useWholeWidth = false;
742         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
743     }
744     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
745         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
746         useWholeWidth = false;
747     }
748     if (m_truncation != cNoTruncation) {
749         paintEnd = min(paintEnd, (unsigned)m_start + m_truncation);
750         useWholeWidth = false;
751     }
752     if (!useWholeWidth) {
753         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
754     }
755
756     // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
757     // All other marked text underlines are 1px thick.
758     // If there's not enough space the underline will touch or overlap characters.
759     int lineThickness = 1;
760     if (underline.thick && m_height - m_baseline >= 2)
761         lineThickness = 2;
762
763     ctx->setStrokeColor(underline.color);
764     ctx->setStrokeThickness(lineThickness);
765     ctx->drawLineForText(IntPoint(tx + start, ty + m_height - lineThickness), width, textObject()->document()->printing());
766 }
767
768 int InlineTextBox::caretMinOffset() const
769 {
770     return m_start;
771 }
772
773 int InlineTextBox::caretMaxOffset() const
774 {
775     return m_start + m_len;
776 }
777
778 unsigned InlineTextBox::caretMaxRenderedOffset() const
779 {
780     return m_start + m_len;
781 }
782
783 int InlineTextBox::textPos() const
784 {
785     if (xPos() == 0)
786         return 0;
787         
788     RenderBlock *blockElement = object()->containingBlock();
789     return m_reversed ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
790                       : xPos() - blockElement->borderLeft() - blockElement->paddingLeft();
791 }
792
793 int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
794 {
795     if (isLineBreak())
796         return 0;
797
798     RenderText* text = static_cast<RenderText*>(m_object);
799     RenderStyle *style = text->style(m_firstLine);
800     const Font* f = &style->font();
801     return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
802                                 _x - m_x, includePartialGlyphs);
803 }
804
805 int InlineTextBox::positionForOffset(int offset) const
806 {
807     if (isLineBreak())
808         return m_x;
809
810     RenderText* text = static_cast<RenderText*>(m_object);
811     const Font& f = text->style(m_firstLine)->font();
812     int from = m_reversed ? offset - m_start : 0;
813     int to = m_reversed ? m_len : offset - m_start;
814     // FIXME: Do we need to add rightBearing here?
815     return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
816                                                    IntPoint(m_x, 0), 0, from, to)).right();
817 }
818
819 bool InlineTextBox::containsCaretOffset(int offset) const
820 {
821     // Offsets before the box are never "in".
822     if (offset < m_start)
823         return false;
824
825     int pastEnd = m_start + m_len;
826
827     // Offsets inside the box (not at either edge) are always "in".
828     if (offset < pastEnd)
829         return true;
830
831     // Offsets outside the box are always "out".
832     if (offset > pastEnd)
833         return false;
834
835     // Offsets at the end are "out" for line breaks (they are on the next line).
836     if (isLineBreak())
837         return false;
838
839     // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
840     return true;
841 }
842
843 } // namespace WebCore