File indexing completed on 2024-04-28 15:24:16

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 2000-2003 Dirk Mueller (mueller@kde.org)
0006  *           (C) 2003, 2006 Apple Computer, Inc.
0007  *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
0008  *           (C) 2008 Germain Garand (germain@ebooksfrance.org)
0009  *
0010  * This library is free software; you can redistribute it and/or
0011  * modify it under the terms of the GNU Library General Public
0012  * License as published by the Free Software Foundation; either
0013  * version 2 of the License, or (at your option) any later version.
0014  *
0015  * This library is distributed in the hope that it will be useful,
0016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0018  * Library General Public License for more details.
0019  *
0020  * You should have received a copy of the GNU Library General Public License
0021  * along with this library; see the file COPYING.LIB.  If not, write to
0022  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0023  * Boston, MA 02110-1301, USA.
0024  *
0025  */
0026 
0027 //#define DEBUG_LAYOUT
0028 //#define BIDI_DEBUG
0029 
0030 #include "render_text.h"
0031 #include "render_canvas.h"
0032 #include "break_lines.h"
0033 #include "render_arena.h"
0034 #include "rendering/render_position.h"
0035 #include <xml/dom_nodeimpl.h>
0036 
0037 #include <misc/loader.h>
0038 #include <misc/helper.h>
0039 
0040 #include <QBitmap>
0041 #include <QImage>
0042 #include <QPainter>
0043 #include "khtml_debug.h"
0044 #include <limits.h>
0045 #include <math.h>
0046 
0047 #include <config-khtml.h>
0048 
0049 #if HAVE_ALLOCA_H
0050 // explicitly included for systems that don't provide it in stdlib.h or malloc.h
0051 #  include <alloca.h>
0052 #else
0053 #  if HAVE_MALLOC_H
0054 #    include <malloc.h>
0055 #  else
0056 #    include <stdlib.h>
0057 #  endif
0058 #endif
0059 
0060 using namespace khtml;
0061 using namespace DOM;
0062 
0063 #ifndef NDEBUG
0064 static bool inInlineTextBoxDetach;
0065 #endif
0066 
0067 void InlineTextBox::detach(RenderArena *renderArena, bool noRemove)
0068 {
0069     if (!noRemove) {
0070         remove();
0071     }
0072 
0073 #ifndef NDEBUG
0074     inInlineTextBoxDetach = true;
0075 #endif
0076     delete this;
0077 #ifndef NDEBUG
0078     inInlineTextBoxDetach = false;
0079 #endif
0080 
0081     // Recover the size left there for us by operator delete and free the memory.
0082     renderArena->free(*(size_t *)this, this);
0083 }
0084 
0085 void *InlineTextBox::operator new(size_t sz, RenderArena *renderArena) throw()
0086 {
0087     return renderArena->allocate(sz);
0088 }
0089 
0090 void InlineTextBox::operator delete(void *ptr, size_t sz)
0091 {
0092     assert(inInlineTextBoxDetach);
0093 
0094 #ifdef KHTML_USE_ARENA_ALLOCATOR
0095     // Stash size where detach can find it.
0096     *(size_t *)ptr = sz;
0097 #endif
0098 }
0099 
0100 void InlineTextBox::selectionStartEnd(int &sPos, int &ePos)
0101 {
0102     int startPos, endPos;
0103     if (object()->selectionState() == RenderObject::SelectionInside) {
0104         startPos = 0;
0105         endPos = renderText()->string()->l;
0106     } else {
0107         renderText()->selectionStartEnd(startPos, endPos);
0108         if (object()->selectionState() == RenderObject::SelectionStart) {
0109             endPos = renderText()->string()->l;
0110         } else if (object()->selectionState() == RenderObject::SelectionEnd) {
0111             startPos = 0;
0112         }
0113     }
0114 
0115     sPos = qMax(startPos - m_start, 0);
0116     ePos = qMin(endPos - m_start, (int)m_len);
0117 }
0118 
0119 RenderObject::SelectionState InlineTextBox::selectionState()
0120 {
0121     RenderObject::SelectionState state = object()->selectionState();
0122     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd ||
0123             state == RenderObject::SelectionBoth) {
0124         int startPos, endPos;
0125         renderText()->selectionStartEnd(startPos, endPos);
0126 
0127         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
0128         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
0129         if (start && end) {
0130             state = RenderObject::SelectionBoth;
0131         } else if (start) {
0132             state = RenderObject::SelectionStart;
0133         } else if (end) {
0134             state = RenderObject::SelectionEnd;
0135         } else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
0136                    (state == RenderObject::SelectionStart || endPos > m_start + m_len)) {
0137             state = RenderObject::SelectionInside;
0138         }
0139     }
0140     return state;
0141 }
0142 
0143 void InlineTextBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
0144 {
0145     if (object()->isBR() || object()->style()->visibility() != VISIBLE ||
0146             m_truncation == cFullTruncation || i.phase == PaintActionOutline) {
0147         return;
0148     }
0149 
0150     if (i.phase == PaintActionSelection && object()->selectionState() == RenderObject::SelectionNone)
0151         // When only painting the selection, don't bother to paint if there is none.
0152     {
0153         return;
0154     }
0155 
0156     int xPos = tx + m_x;
0157     int w = width();
0158     if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x())) {
0159         return;
0160     }
0161 
0162     // Set our font.
0163     RenderStyle *styleToUse = object()->style(m_firstLine);
0164     int d = styleToUse->textDecorationsInEffect();
0165     if (styleToUse->font() != i.p->font()) {
0166         i.p->setFont(styleToUse->font());
0167     }
0168     const Font *font = &styleToUse->htmlFont();
0169     bool haveSelection = selectionState() != RenderObject::SelectionNone;
0170 
0171     // Now calculate startPos and endPos, for painting selection.
0172     // We paint selection while endPos > 0
0173     int ePos = 0, sPos = 0;
0174     if (haveSelection && !object()->canvas()->staticMode()) {
0175         selectionStartEnd(sPos, ePos);
0176     }
0177     i.p->setPen(styleToUse->color());
0178 
0179     if (m_len > 0 && i.phase != PaintActionSelection) {
0180         int endPoint = m_len;
0181         if (m_truncation != cNoTruncation) {
0182             endPoint = m_truncation - m_start;
0183         }
0184         if (styleToUse->textShadow()) {
0185             paintShadow(i.p, font, tx, ty, styleToUse->textShadow());
0186         }
0187         if (!haveSelection || sPos != 0 || ePos != m_len) {
0188             font->drawText(i.p, m_x + tx, m_y + ty + m_baseline, renderText()->string()->s, renderText()->string()->l, m_start, endPoint,
0189                            m_toAdd, m_reversed ? Qt::RightToLeft : Qt::LeftToRight);
0190         }
0191     }
0192 
0193     if (d != TDNONE && i.phase != PaintActionSelection && styleToUse->htmlHacks()) {
0194         i.p->setPen(styleToUse->color());
0195         paintDecoration(i.p, font, tx, ty, d);
0196     }
0197 
0198     if (haveSelection && i.phase == PaintActionSelection) {
0199         //qCDebug(KHTML_LOG) << this << " paintSelection with startPos=" << sPos << " endPos=" << ePos;
0200         if (sPos < ePos) {
0201             paintSelection(font, renderText(), i.p, styleToUse, tx, ty, sPos, ePos, d);
0202         }
0203     }
0204 }
0205 
0206 /** returns the proper ::selection pseudo style for the given element
0207  * @return the style or 0 if no ::selection pseudo applies.
0208  */
0209 inline const RenderStyle *retrieveSelectionPseudoStyle(const RenderObject *obj)
0210 {
0211     // https://www.w3.org/Style/CSS/Test/CSS3/Selectors/20021129/html/tests/css3-modsel-162.html
0212     // is of the opinion that ::selection of parent elements is also to be applied
0213     // to children, so let's do it.
0214     while (obj) {
0215         const RenderStyle *style = obj->style()->getPseudoStyle(RenderStyle::SELECTION);
0216         if (style) {
0217             return style;
0218         }
0219 
0220         obj = obj->parent();
0221     }/*wend*/
0222     return nullptr;
0223 }
0224 
0225 void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle *style, int tx, int ty, int startPos, int endPos, int deco)
0226 {
0227     if (startPos > m_len) {
0228         return;
0229     }
0230     if (startPos < 0) {
0231         startPos = 0;
0232     }
0233 
0234     QColor hc;
0235     QColor hbg;
0236     const RenderStyle *pseudoStyle = retrieveSelectionPseudoStyle(text);
0237     if (pseudoStyle) {
0238         // ### support outline (mandated by CSS3)
0239         // ### support background-image? (optional by CSS3)
0240         if (pseudoStyle->backgroundColor().isValid()) {
0241             hbg = pseudoStyle->backgroundColor();
0242         }
0243         hc = pseudoStyle->color();
0244     } else {
0245         hc = style->palette().color(QPalette::Active, QPalette::HighlightedText);
0246         hbg = style->palette().color(QPalette::Active, QPalette::Highlight);
0247         // ### should be at most retrieved once per render text
0248         QColor bg = khtml::retrieveBackgroundColor(text);
0249         // It may happen that the contrast is -- well -- virtually non existent.
0250         // In this case, simply swap the colors, thus in compliance with
0251         // NN4 (win32 only), IE, and Mozilla.
0252         if (!khtml::hasSufficientContrast(hbg, bg)) {
0253             qSwap(hc, hbg);
0254         }
0255     }
0256 
0257     p->setPen(hc);
0258 
0259     //qCDebug(KHTML_LOG) << "textRun::painting(" << QString::fromRawData(text->str->s + m_start, m_len).left(30) << ") at(" << m_x+tx << "/" << m_y+ty << ")";
0260 
0261     const bool needClipping = startPos != 0 || endPos != m_len;
0262 
0263     if (needClipping) {
0264         p->save();
0265 
0266         int visualSelectionStart = f->width(text->str->s, text->str->l, m_start, startPos, false, m_start, m_start + m_len, m_toAdd);
0267         int visualSelectionEnd = f->width(text->str->s, text->str->l, m_start, endPos, false, m_start, m_start + m_len, m_toAdd);
0268         int visualSelectionWidth = visualSelectionEnd - visualSelectionStart;
0269         if (m_reversed) {
0270             visualSelectionStart = f->width(text->str->s, text->str->l, m_start, m_len, false) - visualSelectionEnd;
0271         }
0272 
0273         QRect selectionRect(m_x + tx + visualSelectionStart, m_y + ty, visualSelectionWidth, height());
0274         QRegion r(selectionRect);
0275         if (p->hasClipping()) {
0276             r &= p->clipRegion();
0277         }
0278         p->setClipRegion(r, Qt::IntersectClip);
0279     }
0280 
0281     f->drawText(p, m_x + tx, m_y + ty + m_baseline, text->str->s, text->str->l,
0282                 m_start, m_len, m_toAdd,
0283                 m_reversed ? Qt::RightToLeft : Qt::LeftToRight,
0284                 needClipping ? 0 : startPos, needClipping ? m_len : endPos,
0285                 hbg, m_y + ty, height(), deco);
0286 
0287     if (needClipping) {
0288         p->restore();
0289     }
0290 }
0291 
0292 void InlineTextBox::paintDecoration(QPainter *pt, const Font *f, int _tx, int _ty, int deco)
0293 {
0294     _tx += m_x;
0295     _ty += m_y;
0296 
0297     if (m_truncation == cFullTruncation) {
0298         return;
0299     }
0300 
0301     int width = m_width - 1;
0302     if (m_truncation != cNoTruncation) {
0303         width = static_cast<RenderText *>(m_object)->width(m_start, m_truncation - m_start, m_firstLine);
0304     }
0305 
0306     RenderObject *p = object();
0307 
0308     QColor underline, overline, linethrough;
0309     p->getTextDecorationColors(deco, underline, overline, linethrough, p->style()->htmlHacks());
0310 
0311     if (deco & UNDERLINE) {
0312         pt->setPen(underline);
0313         f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::UNDERLINE);
0314     }
0315     if (deco & OVERLINE) {
0316         pt->setPen(overline);
0317         f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::OVERLINE);
0318     }
0319     if (deco & LINE_THROUGH) {
0320         pt->setPen(linethrough);
0321         f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::LINE_THROUGH);
0322     }
0323     // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to
0324     // support it. Lars
0325 }
0326 
0327 void InlineTextBox::paintShadow(QPainter *pt, const Font *f, int _tx, int _ty, const ShadowData *shadow)
0328 {
0329     int x = m_x + _tx + shadow->x;
0330     int y = m_y + _ty + shadow->y;
0331     const RenderText *text = renderText();
0332 
0333     if (shadow->blur <= 0) {
0334         QColor c = pt->pen().color();
0335         pt->setPen(shadow->color);
0336         f->drawText(pt, x, y + m_baseline, text->str->s, text->str->l,
0337                     m_start, m_len, m_toAdd,
0338                     m_reversed ? Qt::RightToLeft : Qt::LeftToRight);
0339         pt->setPen(c);
0340 
0341     } else {
0342         const int thickness = shadow->blur;
0343         const int w = m_width + 2 * thickness;
0344         const int h = m_height + 2 * thickness;
0345         const QRgb color = shadow->color.rgba();
0346         const int gray = qGray(color);
0347         const bool inverse = (gray < 100);
0348         const QRgb bgColor = (inverse) ? qRgb(255, 255, 255) : qRgb(0, 0, 0);
0349         QImage img(w, h, QImage::Format_RGB32);
0350         img.fill(bgColor);
0351         QPainter p;
0352 
0353         p.begin(&img);
0354         p.setPen(shadow->color);
0355         p.setFont(pt->font());
0356         f->drawText(&p, thickness, thickness + m_baseline, text->str->s, text->str->l,
0357                     m_start, m_len, m_toAdd,
0358                     m_reversed ? Qt::RightToLeft : Qt::LeftToRight);
0359 
0360         p.end();
0361 
0362         int md = thickness * thickness; // max-dist^2
0363 
0364         // blur map (division cache)
0365         float *bmap = (float *)alloca(sizeof(float) * (md + 1));
0366         for (int n = 0; n <= md; n++) {
0367             float f;
0368             f = n / (float)(md + 1);
0369             f = 1.0 - f * f;
0370             bmap[n] = f;
0371         }
0372 
0373         float factor = 0.0; // maximal potential opacity-sum
0374         for (int n = -thickness; n <= thickness; n++)
0375             for (int m = -thickness; m <= thickness; m++) {
0376                 int d = n * n + m * m;
0377                 if (d <= md) {
0378                     factor += bmap[d];
0379                 }
0380             }
0381 
0382         // arbitratry factor adjustment to make shadows solid.
0383         factor = factor / 1.333;
0384 
0385         // alpha map
0386         float *amap = (float *)alloca(sizeof(float) * (h * w));
0387         memset(amap, 0, h * w * (sizeof(float)));
0388         for (int j = thickness; j < h - thickness; j++) {
0389             const QRgb *line = (QRgb *)img.scanLine(j);
0390             for (int i = thickness; i < w - thickness; i++) {
0391                 QRgb col = line[i];
0392                 if (col == bgColor) {
0393                     continue;
0394                 }
0395                 float g = qGray(col);
0396                 if (inverse) {
0397                     g = (255 - g) / (255 - gray);
0398                 } else {
0399                     g = g / gray;
0400                 }
0401                 for (int n = -thickness; n <= thickness; n++) {
0402                     for (int m = -thickness; m <= thickness; m++) {
0403                         int d = n * n + m * m;
0404                         if (d > md) {
0405                             continue;
0406                         }
0407                         float f = bmap[d];
0408                         amap[(i + m) + (j + n)*w] += (g * f);
0409                     }
0410                 }
0411             }
0412         }
0413 
0414         QImage res(w, h, QImage::Format_ARGB32);
0415         int r = qRed(color);
0416         int g = qGreen(color);
0417         int b = qBlue(color);
0418 
0419         // divide by factor
0420         factor = 1.0 / factor;
0421 
0422         for (int j = 0; j < h; j++) {
0423             QRgb *line = (QRgb *)res.scanLine(j);
0424             for (int i = 0; i < w; i++) {
0425                 int a = (int)(amap[i + j * w] * factor * 255.0);
0426                 if (a > 255) {
0427                     a = 255;
0428                 }
0429                 line[i] = qRgba(r, g, b, a);
0430             }
0431         }
0432 
0433         pt->drawImage(x - thickness, y - thickness, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither);
0434     }
0435     // Paint next shadow effect
0436     if (shadow->next) {
0437         paintShadow(pt, f, _tx, _ty, shadow->next);
0438     }
0439 }
0440 
0441 /**
0442  * Distributes pixels to justify text.
0443  * @param numSpaces spaces left, will be decremented by one
0444  * @param toAdd number of pixels left to be distributed, will have the
0445  *  amount of pixels distributed during this call subtracted.
0446  * @return number of pixels to distribute
0447  */
0448 static inline int justifyWidth(int &numSpaces, int &toAdd)
0449 {
0450     int a = 0;
0451     if (numSpaces) {
0452         a = toAdd / numSpaces;
0453         toAdd -= a;
0454         numSpaces--;
0455     }/*end if*/
0456     return a;
0457 }
0458 
0459 FindSelectionResult InlineTextBox::checkSelectionPoint(int _x, int _y, int _tx, int _ty, int &offset)
0460 {
0461 //       qCDebug(KHTML_LOG) << "InlineTextBox::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
0462 //                     << " _tx+m_x=" << _tx+m_x << " _ty+m_y=" << _ty+m_y;
0463     offset = 0;
0464 
0465     if (_y < _ty + m_y) {
0466         return SelectionPointBefore;    // above -> before
0467     }
0468 
0469     if (_y > _ty + m_y + m_height) {
0470         // below -> after
0471         // Set the offset to the max
0472         offset = m_len;
0473         return SelectionPointAfter;
0474     }
0475     if (_x > _tx + m_x + m_width) {
0476         // to the right
0477         return  SelectionPointAfterInLine;
0478     }
0479 
0480     // The Y matches, check if we're on the left
0481     if (_x < _tx + m_x) {
0482         return SelectionPointBeforeInLine;
0483     }
0484 
0485     // consider spacing for justified text
0486     int toAdd = m_toAdd;
0487     RenderText *text = static_cast<RenderText *>(object());
0488     Q_ASSERT(text->isText());
0489     bool justified = text->style()->textAlign() == JUSTIFY && toAdd > 0;
0490     int numSpaces = 0;
0491     if (justified) {
0492         for (int i = 0; i < m_len; i++)
0493             if (text->str->s[m_start + i].category() == QChar::Separator_Space) {
0494                 numSpaces++;
0495             }
0496 
0497     }/*end if*/
0498 
0499     int delta = _x - (_tx + m_x);
0500     //qCDebug(KHTML_LOG) << "InlineTextBox::checkSelectionPoint delta=" << delta;
0501     int pos = 0;
0502     const Font *f = text->htmlFont(m_firstLine);
0503     if (m_reversed) {
0504         delta -= m_width;
0505         while (pos < m_len) {
0506             int w = f->charWidth(text->str->s, text->str->l, m_start + pos, text->isSimpleText());
0507             if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space) {
0508                 w += justifyWidth(numSpaces, toAdd);
0509             }
0510             int w2 = w / 2;
0511             w -= w2;
0512             delta += w2;
0513             if (delta >= 0) {
0514                 break;
0515             }
0516             pos++;
0517             delta += w;
0518         }
0519     } else {
0520         while (pos < m_len) {
0521             int w = f->charWidth(text->str->s, text->str->l, m_start + pos, text->isSimpleText());
0522             if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space) {
0523                 w += justifyWidth(numSpaces, toAdd);
0524             }
0525             int w2 = w / 2;
0526             w -= w2;
0527             delta -= w2;
0528             if (delta <= 0) {
0529                 break;
0530             }
0531             pos++;
0532             delta -= w;
0533         }
0534     }
0535 //     qCDebug(KHTML_LOG) << " Text  --> inside at position " << pos;
0536     offset = pos;
0537     return SelectionPointInside;
0538 }
0539 
0540 long InlineTextBox::caretMinOffset() const
0541 {
0542     return m_start;
0543 }
0544 
0545 long InlineTextBox::caretMaxOffset() const
0546 {
0547     return m_start + m_len;
0548 }
0549 
0550 unsigned long InlineTextBox::caretMaxRenderedOffset() const
0551 {
0552     return m_start + m_len;
0553 }
0554 
0555 int InlineTextBox::offsetForPoint(int _x, int &ax) const
0556 {
0557     // Do binary search for finding out offset, saves some time for long
0558     // runs.
0559     int start = 0;
0560     int end = m_len;
0561     ax = m_x;
0562     int offset = (start + end) / 2;
0563     while (end - start > 0) {
0564         // always snap to the right column. This makes up for "jumpy" vertical
0565         // navigation.
0566         if (end - start == 1) {
0567             start = end;
0568         }
0569 
0570         offset = (start + end) / 2;
0571         ax = m_x + widthFromStart(offset);
0572         if (ax > _x) {
0573             end = offset;
0574         } else if (ax < _x) {
0575             start = offset;
0576         } else {
0577             break;
0578         }
0579     }
0580     return m_start + offset;
0581 }
0582 
0583 int InlineTextBox::widthFromStart(int pos) const
0584 {
0585     // gasp! sometimes pos is i < 0 which crashes Font::width
0586     // qCDebug(KHTML_LOG) << this << pos;
0587     pos = qMax(pos, 0);
0588 
0589     const RenderText *t = renderText();
0590     Q_ASSERT(t->isText());
0591     const Font *f = t->htmlFont(m_firstLine);
0592     const QFontMetrics &fm = t->fontMetrics(m_firstLine);
0593 
0594     int numSpaces = 0;
0595     // consider spacing for justified text
0596     bool justified = t->style()->textAlign() == JUSTIFY;
0597     //qCDebug(KHTML_LOG) << "InlineTextBox::width(int)";
0598     if (justified && m_toAdd > 0) do {
0599             //qCDebug(KHTML_LOG) << "justify";
0600 
0601 //    const QString cstr = QString::fromRawData(t->str->s + m_start, m_len);
0602             for (int i = 0; i < m_len; i++)
0603                 if (t->str->s[m_start + i].category() == QChar::Separator_Space) {
0604                     numSpaces++;
0605                 }
0606             if (numSpaces == 0) {
0607                 break;
0608             }
0609 
0610             int toAdd = m_toAdd;
0611             int w = 0;      // accumulated width
0612             int start = 0;  // start of non-space sequence
0613             int current = 0;    // current position
0614             while (current < pos) {
0615                 // add spacing
0616                 while (current < pos && t->str->s[m_start + current].category() == QChar::Separator_Space) {
0617                     w += f->getWordSpacing();
0618                     w += f->getLetterSpacing();
0619                     w += justifyWidth(numSpaces, toAdd);
0620                     w += fm.width(' '); // ### valid assumption? (LS)
0621                     current++; start++;
0622                 }/*wend*/
0623                 if (current >= pos) {
0624                     break;
0625                 }
0626 
0627                 // seek next space
0628                 while (current < pos && t->str->s[m_start + current].category() != QChar::Separator_Space) {
0629                     current++;
0630                 }
0631 
0632                 // check run without spaces
0633                 if (current > start) {
0634                     w += f->width(t->str->s + m_start, m_len, start, current - start, false);
0635                     start = current;
0636                 }
0637             }
0638 
0639             return w;
0640 
0641         } while (false); /*end if*/
0642 
0643     //qCDebug(KHTML_LOG) << "default";
0644     // else use existing width function
0645     // qCDebug(KHTML_LOG) << "result width:" << f->width(t->str->s + m_start, m_len, 0, pos, false);
0646     return f->width(t->str->s + m_start, m_len, 0, pos, false);
0647 
0648 }
0649 
0650 void InlineTextBox::deleteLine(RenderArena *arena)
0651 {
0652     static_cast<RenderText *>(m_object)->removeTextBox(this);
0653     detach(arena, true /*noRemove*/);
0654 }
0655 
0656 void InlineTextBox::extractLine()
0657 {
0658     if (m_extracted) {
0659         return;
0660     }
0661     static_cast<RenderText *>(m_object)->extractTextBox(this);
0662 }
0663 
0664 void InlineTextBox::attachLine()
0665 {
0666     if (!m_extracted) {
0667         return;
0668     }
0669     static_cast<RenderText *>(m_object)->attachTextBox(this);
0670 }
0671 
0672 int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool &foundBox)
0673 {
0674     if (foundBox) {
0675         m_truncation = cFullTruncation;
0676         return -1;
0677     }
0678 
0679     int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
0680 
0681     // 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.
0682     if (ltr) {
0683         if (ellipsisX <= m_x) {
0684             // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
0685             m_truncation = cFullTruncation;
0686             foundBox = true;
0687             return -1;
0688         }
0689 
0690         if (ellipsisX < m_x + m_width) {
0691             if (m_reversed) {
0692                 return -1;    // FIXME: Support LTR truncation when the last run is RTL someday.
0693             }
0694 
0695             foundBox = true;
0696 
0697             int ax;
0698             int offset = offsetForPoint(ellipsisX, ax) - 1;
0699             if (offset <= m_start) {
0700                 // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
0701                 // and the ellipsis edge.
0702                 m_truncation = cFullTruncation;
0703                 return qMin(ellipsisX, (int)m_x);
0704             }
0705 
0706             // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
0707             m_truncation = offset;
0708             return widthFromStart(offset - m_start);
0709         }
0710     } else {
0711         // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
0712     }
0713     return -1;
0714 }
0715 
0716 // -----------------------------------------------------------------------------
0717 
0718 RenderText::RenderText(DOM::NodeImpl *node, DOMStringImpl *_str)
0719     : RenderObject(node)
0720 {
0721     // init RenderObject attributes
0722     setRenderText();   // our object inherits from RenderText
0723 
0724     m_minWidth = -1;
0725     m_maxWidth = -1;
0726     str = _str;
0727     if (str) {
0728         str->ref();
0729     }
0730     KHTMLAssert(!str || !str->l || str->s);
0731 
0732     m_selectionState = SelectionNone;
0733     m_hasReturn = true;
0734     m_isSimpleText = false;
0735     m_firstTextBox = m_lastTextBox = nullptr;
0736 
0737 #ifdef DEBUG_LAYOUT
0738     const QString cstr = QString::fromRawData(str->s, str->l);
0739     qCDebug(KHTML_LOG) << "RenderText ctr( " << cstr.length() << " )  '" << cstr << "'";
0740 #endif
0741 }
0742 
0743 void RenderText::setStyle(RenderStyle *_style)
0744 {
0745     if (style() != _style) {
0746         bool changedText = ((!style() && (_style->textTransform() != TTNONE ||
0747                                           !_style->preserveLF() || !_style->preserveWS())) ||
0748                             (style() && (style()->textTransform() != _style->textTransform() ||
0749                                          style()->whiteSpace() != _style->whiteSpace())));
0750 
0751         RenderObject::setStyle(_style);
0752         m_lineHeight = RenderObject::lineHeight(false);
0753 
0754         if (!isBR() && changedText) {
0755             DOM::DOMStringImpl *textToTransform = originalString();
0756             if (textToTransform) {
0757                 setText(textToTransform, true);
0758             }
0759         }
0760     }
0761 }
0762 
0763 RenderText::~RenderText()
0764 {
0765     if (str) {
0766         str->deref();
0767     }
0768     assert(!m_firstTextBox);
0769     assert(!m_lastTextBox);
0770 }
0771 
0772 void RenderText::detach()
0773 {
0774     if (!documentBeingDestroyed()) {
0775         if (firstTextBox()) {
0776             if (isBR()) {
0777                 RootInlineBox *next = firstTextBox()->root()->nextRootBox();
0778                 if (next) {
0779                     next->markDirty();
0780                 }
0781             }
0782             for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
0783                 box->remove();
0784             }
0785         } else if (parent()) {
0786             parent()->dirtyLinesFromChangedChild(this);
0787         }
0788     }
0789     deleteInlineBoxes();
0790     RenderObject::detach();
0791 }
0792 
0793 void RenderText::extractTextBox(InlineTextBox *box)
0794 {
0795     m_lastTextBox = box->prevTextBox();
0796     if (box == m_firstTextBox) {
0797         m_firstTextBox = nullptr;
0798     }
0799     if (box->prevTextBox()) {
0800         box->prevTextBox()->setNextLineBox(nullptr);
0801     }
0802     box->setPreviousLineBox(nullptr);
0803     for (InlineRunBox *curr = box; curr; curr = curr->nextLineBox()) {
0804         curr->setExtracted();
0805     }
0806 }
0807 
0808 void RenderText::attachTextBox(InlineTextBox *box)
0809 {
0810     if (m_lastTextBox) {
0811         m_lastTextBox->setNextLineBox(box);
0812         box->setPreviousLineBox(m_lastTextBox);
0813     } else {
0814         m_firstTextBox = box;
0815     }
0816     InlineTextBox *last = box;
0817     for (InlineTextBox *curr = box; curr; curr = curr->nextTextBox()) {
0818         curr->setExtracted(false);
0819         last = curr;
0820     }
0821     m_lastTextBox = last;
0822 }
0823 
0824 void RenderText::removeTextBox(InlineTextBox *box)
0825 {
0826     if (box == m_firstTextBox) {
0827         m_firstTextBox = box->nextTextBox();
0828     }
0829     if (box == m_lastTextBox) {
0830         m_lastTextBox = box->prevTextBox();
0831     }
0832     if (box->nextTextBox()) {
0833         box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
0834     }
0835     if (box->prevTextBox()) {
0836         box->prevTextBox()->setNextLineBox(box->nextTextBox());
0837     }
0838 }
0839 
0840 void RenderText::removeInlineBox(InlineBox *_box)
0841 {
0842     KHTMLAssert(_box->isInlineTextBox());
0843     removeTextBox(static_cast<InlineTextBox *>(_box));
0844 }
0845 
0846 void RenderText::deleteInlineBoxes(RenderArena * /*arena*/)
0847 {
0848     if (firstTextBox()) {
0849         RenderArena *arena = renderArena();
0850         InlineTextBox *next;
0851         for (InlineTextBox *curr = firstTextBox(); curr; curr = next) {
0852             next = curr->nextTextBox();
0853             curr->detach(arena, true /*noRemove*/);
0854         }
0855         m_firstTextBox = m_lastTextBox = nullptr;
0856     }
0857 }
0858 
0859 void RenderText::dirtyInlineBoxes(bool fullLayout, bool)
0860 {
0861     if (fullLayout) {
0862         deleteInlineBoxes();
0863     } else {
0864         for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
0865             box->dirtyInlineBoxes();
0866         }
0867     }
0868 }
0869 
0870 bool RenderText::isTextFragment() const
0871 {
0872     return false;
0873 }
0874 
0875 DOM::DOMStringImpl *RenderText::originalString() const
0876 {
0877     return element() ? element()->string() : nullptr;
0878 }
0879 
0880 const InlineTextBox *RenderText::findInlineTextBox(int offset, int &pos, bool checkFirstLetter) const
0881 {
0882     Q_UNUSED(checkFirstLetter);
0883     // The text boxes point to parts of the rendertext's str string
0884     // (they don't include '\n')
0885     // Find the text box that includes the character at @p offset
0886     // and return pos, which is the position of the char in the run.
0887 
0888     if (!m_firstTextBox) {
0889         return nullptr;
0890     }
0891 
0892     InlineTextBox *s = m_firstTextBox;
0893     int off = s->m_len;
0894     while (offset > off && s->nextTextBox()) {
0895         s = s->nextTextBox();
0896         off = s->m_start + s->m_len;
0897     }
0898     // we are now in the correct text run
0899     if (offset >= s->m_start && offset < s->m_start + s->m_len) {
0900         pos = offset - s->m_start;
0901     } else {
0902         pos = (offset > off ? s->m_len : s->m_len - (off - offset));
0903     }
0904     return s;
0905 }
0906 
0907 bool RenderText::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction /*hitTestAction*/, bool /*inBox*/)
0908 {
0909     assert(parent());
0910 
0911     bool inside = false;
0912     if (style()->visibility() != HIDDEN) {
0913         for (InlineTextBox *s = firstTextBox(); s; s = s->nextTextBox()) {
0914             if ((_y >= _ty + s->m_y) && (_y < _ty + s->m_y + s->m_height) &&
0915                     (_x >= _tx + s->m_x) && (_x < _tx + s->m_x + s->m_width)) {
0916                 inside = true;
0917                 break;
0918             }
0919         }
0920     }
0921 
0922     // #### ported over from Safari. Can this happen at all? (lars)
0923 
0924     if (inside && element()) {
0925         if (info.innerNode() && info.innerNode()->renderer() &&
0926                 !info.innerNode()->renderer()->isInline()) {
0927             // Within the same layer, inlines are ALWAYS fully above blocks.  Change inner node.
0928             info.setInnerNode(element());
0929 
0930             // Clear everything else.
0931             info.setInnerNonSharedNode(nullptr);
0932             info.setURLElement(nullptr);
0933         }
0934 
0935         if (!info.innerNode()) {
0936             info.setInnerNode(element());
0937         }
0938 
0939         if (!info.innerNonSharedNode()) {
0940             info.setInnerNonSharedNode(element());
0941         }
0942     }
0943 
0944     return inside;
0945 }
0946 
0947 FindSelectionResult RenderText::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &)
0948 {
0949 //        qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
0950 //                     << " _tx=" << _tx << " _ty=" << _ty;
0951 //qCDebug(KHTML_LOG) << renderName() << "::checkSelectionPoint x=" << xPos() << " y=" << yPos() << " w=" << width() << " h=" << height();
0952 
0953     NodeImpl *lastNode = nullptr;
0954     int lastOffset = 0;
0955     FindSelectionResult lastResult = SelectionPointAfter;
0956 
0957     for (InlineTextBox *s = firstTextBox(); s; s = s->nextTextBox()) {
0958         FindSelectionResult result;
0959 // #### ?
0960 //         result = s->checkSelectionPoint(_x, _y, _tx, _ty, offset);
0961         if (_y < _ty + s->m_y) {
0962             result = SelectionPointBefore;
0963         } else if (_y >= _ty + s->m_y + s->height()) {
0964             result = SelectionPointAfterInLine;
0965         } else if (_x < _tx + s->m_x) {
0966             result = SelectionPointBeforeInLine;
0967         } else if (_x >= _tx + s->m_x + s->width()) {
0968             result = SelectionPointAfterInLine;
0969         } else {
0970             int dummy;
0971             result = SelectionPointInside;
0972             // offsetForPoint shifts to the right: correct it
0973             offset = s->offsetForPoint(_x - _tx, dummy) - 1;
0974         }
0975 
0976 //         qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint " << this << " line " << si << " result=" << result << " offset=" << offset;
0977         if (result == SelectionPointInside) { // x,y is inside the textrun
0978 //            offset += s->m_start; // add the offset from the previous lines
0979 //             qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint inside -> " << offset;
0980             node = element();
0981             return SelectionPointInside;
0982         } else if (result == SelectionPointBefore) {
0983             if (!lastNode) {
0984                 // x,y is before the textrun -> stop here
0985                 offset = 0;
0986 //                qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint " << this << "before us -> returning Before";
0987                 node = element();
0988                 return SelectionPointBefore;
0989             }
0990         } else if (result == SelectionPointBeforeInLine) {
0991             offset = s->m_start;
0992             node = element();
0993             return SelectionPointInside;
0994         } else if (result == SelectionPointAfterInLine) {
0995             lastOffset = s->m_start + s->m_len;
0996             lastNode = element();
0997             lastResult = result;
0998             // no return here
0999         }
1000 
1001     }
1002 
1003     if (lastNode) {
1004         offset = lastOffset;
1005         node = lastNode;
1006 //         qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint: lastNode " << lastNode << " lastOffset " << lastOffset;
1007         return lastResult;
1008     }
1009 
1010     // set offset to max
1011     offset = str->l;
1012     //qDebug("setting node to %p", element());
1013     node = element();
1014 //     qCDebug(KHTML_LOG) << "RenderText::checkSelectionPoint: node " << node << " offset " << offset;
1015     return SelectionPointAfter;
1016 }
1017 
1018 unsigned RenderText::convertToDOMPosition(unsigned position) const
1019 {
1020     if (isBR()) {
1021         return 0;
1022     }
1023     /*const */DOMStringImpl *domString = originalString();
1024     /*const */DOMStringImpl *renderedString = string();
1025     if (domString == renderedString) {
1026         // qCDebug(KHTML_LOG) << "[rendered == dom]" << position;
1027         return position;
1028     }
1029     /* // qCDebug(KHTML_LOG) << "[convert]" << position << endl
1030         << DOMString(domString) << endl
1031         << DOMString(renderedString);*/
1032 
1033     if (!domString || !renderedString) {
1034         return position;
1035     }
1036 
1037     unsigned domLength = domString->length();
1038     unsigned i = 0, j = 0;
1039     for (; i < domLength && j < position;) {
1040         bool isRenderedSpace = renderedString->unicode()[j].isSpace();
1041         bool isDOMSpace = domString->unicode()[i].isSpace();
1042         if (isRenderedSpace && isDOMSpace) {
1043             ++i;
1044             ++j;
1045             continue;
1046         }
1047         if (isRenderedSpace) {
1048             ++j;
1049             continue;
1050         }
1051         if (isDOMSpace) {
1052             ++i;
1053             continue;
1054         }
1055         ++i;
1056         ++j;
1057     }
1058     // qCDebug(KHTML_LOG) << "[result]" << i;
1059     return i;
1060 }
1061 
1062 unsigned RenderText::convertToRenderedPosition(unsigned position) const
1063 {
1064     if (isBR()) {
1065         return 0;
1066     }
1067     /*const */DOMStringImpl *domString = originalString();
1068     /*const */DOMStringImpl *renderedString = string();
1069     if (domString == renderedString) {
1070         // qCDebug(KHTML_LOG) << "[rendered == dom]" << position;
1071         return position;
1072     }
1073     /* // qCDebug(KHTML_LOG) << "[convert]" << position << endl
1074         << DOMString(domString) << endl
1075         << DOMString(renderedString);*/
1076 
1077     if (!domString || !renderedString) {
1078         return position;
1079     }
1080 
1081     unsigned renderedLength = renderedString->length();
1082     unsigned i = 0, j = 0;
1083     for (; i < position && j < renderedLength;) {
1084         bool isRenderedSpace = renderedString->unicode()[j].isSpace();
1085         bool isDOMSpace = domString->unicode()[i].isSpace();
1086         if (isRenderedSpace && isDOMSpace) {
1087             ++i;
1088             ++j;
1089             continue;
1090         }
1091         if (isRenderedSpace) {
1092             ++j;
1093             continue;
1094         }
1095         if (isDOMSpace) {
1096             ++i;
1097             continue;
1098         }
1099         ++i;
1100         ++j;
1101     }
1102     // qCDebug(KHTML_LOG) << "[result]" << j;
1103     return j;
1104 }
1105 
1106 RenderPosition RenderText::positionForCoordinates(int _x, int _y)
1107 {
1108     // qCDebug(KHTML_LOG) << this << _x << _y;
1109     if (!firstTextBox() || stringLength() == 0) {
1110         return Position(element(), 0);
1111     }
1112 
1113     int absx, absy;
1114     containingBlock()->absolutePosition(absx, absy);
1115     // qCDebug(KHTML_LOG) << "absolute(" << absx << absy << ")";
1116 
1117     if (_y < absy + firstTextBox()->root()->bottomOverflow() && _x < absx + firstTextBox()->m_x) {
1118         // at the y coordinate of the first line or above
1119         // and the x coordinate is to the left than the first text box left edge
1120         return RenderPosition(element(), firstTextBox()->m_start);
1121     }
1122 
1123     if (_y >= absy + lastTextBox()->root()->topOverflow() && _x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
1124         // at the y coordinate of the last line or below
1125         // and the x coordinate is to the right than the last text box right edge
1126         return RenderPosition(element(), lastTextBox()->m_start + lastTextBox()->m_len);
1127     }
1128 
1129     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1130         // qCDebug(KHTML_LOG) << "[check box]" << box;
1131         if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
1132             if (_x < absx + box->m_x + box->m_width) {
1133                 // and the x coordinate is to the left of the right edge of this box
1134                 // check to see if position goes in this box
1135                 int offset;
1136                 box->checkSelectionPoint(_x, absy + box->yPos(), absx, absy, offset);
1137                 // qCDebug(KHTML_LOG) << "offset" << offset;
1138                 if (offset != -1) {
1139                     // qCDebug(KHTML_LOG) << "return" << Position(element(), convertToDOMPosition(offset + box->m_start));
1140                     return RenderPosition(element(), offset + box->m_start);
1141                 }
1142             } else if (!box->prevOnLine() && _x < absx + box->m_x)
1143                 // box is first on line
1144                 // and the x coordinate is to the left than the first text box left edge
1145             {
1146                 return RenderPosition(element(), box->m_start);
1147             } else if (!box->nextOnLine() && _x >= absx + box->m_x + box->m_width)
1148                 // box is last on line
1149                 // and the x coordinate is to the right than the last text box right edge
1150             {
1151                 return RenderPosition(element(), box->m_start + box->m_len);
1152             }
1153         }
1154     }
1155     return RenderPosition(element(), 0);
1156 }
1157 
1158 void RenderText::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) const
1159 {
1160     // qCDebug(KHTML_LOG) << offset << flags;
1161     if (!m_firstTextBox) {
1162         _x = _y = height = -1;
1163         width = 1;
1164         return;
1165     }
1166 
1167     int pos;
1168     const InlineTextBox *s = findInlineTextBox(offset, pos, true);
1169     const RenderText *t = s->renderText();
1170 //  qCDebug(KHTML_LOG) << "offset="<<offset << " pos="<<pos;
1171 
1172     const QFontMetrics &fm = t->metrics(s->m_firstLine);
1173     height = fm.height(); // s->m_height;
1174 
1175     _x = s->m_x + s->widthFromStart(pos);
1176     _y = s->m_y + s->baseline() - fm.ascent();
1177     // qCDebug(KHTML_LOG) << "(" << _x << _y << ")";
1178     width = 1;
1179     if (flags & CFOverride) {
1180         width = offset < caretMaxOffset() ? fm.width(str->s[offset]) : 1;
1181         // qCDebug(KHTML_LOG) << "CFOverride" << width;
1182     }/*end if*/
1183 #if 0
1184     // qCDebug(KHTML_LOG) << "_x="<<_x << " s->m_x="<<s->m_x
1185             << " s->m_start" << s->m_start
1186             << " s->m_len" << s->m_len << " _y=" << _y;
1187 #endif
1188 
1189     int absx, absy;
1190 
1191     if (absolutePosition(absx, absy)) {
1192         //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
1193         _x += absx;
1194         _y += absy;
1195     } else {
1196         // we don't know our absolute position, and there is no point returning
1197         // just a relative one
1198         _x = _y = -1;
1199     }
1200 }
1201 
1202 long RenderText::caretMinOffset() const
1203 {
1204     if (!m_firstTextBox) {
1205         return 0;
1206     }
1207     // FIXME: it is *not* guaranteed that the first run contains the lowest offset
1208     // Either make this a linear search (slow),
1209     // or maintain an index (needs much mem),
1210     // or calculate and store it in bidi.cpp (needs calculation even if not needed)
1211     // (LS)
1212     return m_firstTextBox->m_start;
1213 }
1214 
1215 long RenderText::caretMaxOffset() const
1216 {
1217     InlineTextBox *box = m_lastTextBox;
1218     if (!box) {
1219         return str->l;
1220     }
1221     int maxOffset = box->m_start + box->m_len;
1222     // ### slow
1223     for (box = box->prevTextBox(); box; box = box->prevTextBox()) {
1224         maxOffset = qMax(maxOffset, box->m_start + box->m_len);
1225     }
1226     return maxOffset;
1227 }
1228 
1229 unsigned long RenderText::caretMaxRenderedOffset() const
1230 {
1231     int l = 0;
1232     // ### no guarantee that the order is ascending
1233     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1234         l += box->m_len;
1235     }
1236     return l;
1237 }
1238 
1239 InlineBox *RenderText::inlineBox(long offset)
1240 {
1241     // ### make educated guess about most likely position
1242     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1243         if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
1244             return box;
1245         } else if (offset < box->m_start) {
1246             // The offset we're looking for is before this node
1247             // this means the offset must be in content that is
1248             // not rendered.
1249             return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
1250         }
1251     }
1252 
1253     return nullptr;
1254 }
1255 
1256 bool RenderText::absolutePosition(int &xPos, int &yPos, bool) const
1257 {
1258     return RenderObject::absolutePosition(xPos, yPos, false);
1259 }
1260 
1261 bool RenderText::posOfChar(int chr, int &x, int &y) const
1262 {
1263     if (!parent()) {
1264         return false;
1265     }
1266     parent()->absolutePosition(x, y, false);
1267 
1268     int pos;
1269     const InlineTextBox *s = findInlineTextBox(chr, pos);
1270 
1271     if (s) {
1272         // s is the line containing the character
1273         x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
1274         y += s->m_y;
1275         return true;
1276     }
1277 
1278     return false;
1279 }
1280 
1281 static bool isSimpleChar(const unsigned short c)
1282 {
1283     // Exclude ranges with many Mn/Me/Mc and the various combining diacriticals ranges.
1284     // Unicode version used is 4.1.0
1285 
1286     // General Combining Diacritical Marks
1287     if (c < 0x300) {
1288         return true;
1289     }
1290     if (c <= 0x36F) {
1291         return false;
1292     }
1293 
1294     // Cyrillic's
1295     if (c < 0x483) {
1296         return true;
1297     }
1298     if (c <= 0x489) {
1299         return false;
1300     }
1301 
1302     // Hebrew's
1303     if (c < 0x0591) {
1304         return true;
1305     }
1306     if (c <= 0x05C7 && !(c == 0x05BE || c == 0x05C0 || c == 0x05C3 || c == 0x05C6)) {
1307         return false;
1308     }
1309 
1310     // Unicode range 6 to 11 (Arabic to Korean Hangul)
1311     if (c < 0x0600) {
1312         return true;
1313     }
1314     if (c <= 0x11F9) {
1315         return false;
1316     }
1317 
1318     // Unicode range 17 to 1A (Tagalog to Buginese)
1319     // (also excl. Ethiopic Combining Gemination Mark)
1320     if (c < 0x1700 && c != 0x135F) {
1321         return true;
1322     }
1323     if (c <= 0x1A1F) {
1324         return false;
1325     }
1326 
1327     // Combining Diacritical Marks Supplement
1328     if (c < 0x1DC0) {
1329         return true;
1330     }
1331     if (c <= 0x1DFF) {
1332         return false;
1333     }
1334 
1335     // Diacritical Marks for Symbols
1336     if (c < 0x20D0) {
1337         return true;
1338     }
1339     if (c <= 0x20EB) {
1340         return false;
1341     }
1342 
1343     // Combining Half Marks
1344     if (c < 0xFE20) {
1345         return true;
1346     }
1347     if (c <= 0xFE2F) {
1348         return false;
1349     }
1350 
1351     return true;
1352 }
1353 
1354 void RenderText::calcMinMaxWidth()
1355 {
1356     KHTMLAssert(!minMaxKnown());
1357 
1358     // ### calc Min and Max width...
1359     m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
1360     m_maxWidth = 0;
1361 
1362     if (isBR()) {
1363         return;
1364     }
1365 
1366     int currMinWidth = 0;
1367     int currMaxWidth = 0;
1368     m_isSimpleText = true;
1369     m_hasBreakableChar = m_hasBreak = m_hasBeginWS = m_hasEndWS = false;
1370 
1371     // ### not 100% correct for first-line
1372     const Font *f = htmlFont(false);
1373     int wordSpacing = style()->wordSpacing();
1374     int len = str->l;
1375     bool isSpace = false;
1376     bool firstWord = true;
1377     bool firstLine = true;
1378     for (int i = 0; i < len; i++) {
1379         unsigned short c = str->s[i].unicode();
1380         bool isNewline = false;
1381 
1382         // If line-breaks survive to here they are preserved
1383         if (c == '\n') {
1384             if (style()->preserveLF()) {
1385                 m_hasBreak = true;
1386                 isNewline = true;
1387                 isSpace = false;
1388             } else {
1389                 isSpace = true;
1390             }
1391         } else {
1392             isSpace = c == ' ';
1393         }
1394 
1395         if ((isSpace || isNewline) && i == 0) {
1396             m_hasBeginWS = true;
1397         }
1398         if ((isSpace || isNewline) && i == len - 1) {
1399             m_hasEndWS = true;
1400         }
1401 
1402         if (i && c == SOFT_HYPHEN) {
1403             continue;
1404         }
1405 
1406         int wordlen = 0;
1407         while (i + wordlen < len && (i + wordlen == 0 || str->s[i + wordlen].unicode() != SOFT_HYPHEN) &&
1408                 !(isBreakable(str->s, i + wordlen, str->l))) {
1409             // check if we may use the simpler algorithm for estimating text width
1410             m_isSimpleText = (m_isSimpleText && isSimpleChar(str->s[i + wordlen].unicode()));
1411             wordlen++;
1412         }
1413 
1414         if (wordlen) {
1415             int w = f->width(str->s, str->l, i, wordlen, m_isSimpleText);
1416             currMinWidth += w;
1417             currMaxWidth += w;
1418 
1419             // Add in wordspacing to our maxwidth, but not if this is the last word.
1420             if (wordSpacing && !containsOnlyWhitespace(i + wordlen, len - (i + wordlen))) {
1421                 currMaxWidth += wordSpacing;
1422             }
1423 
1424             if (firstWord) {
1425                 firstWord = false;
1426                 m_beginMinWidth = w;
1427             }
1428             m_endMinWidth = w;
1429 
1430             if (currMinWidth > m_minWidth) {
1431                 m_minWidth = currMinWidth;
1432             }
1433             currMinWidth = 0;
1434 
1435             i += wordlen - 1;
1436         } else {
1437             // Nowrap can never be broken, so don't bother setting the
1438             // breakable character boolean. Pre can only be broken if we encounter a newline.
1439             if (style()->autoWrap() || isNewline) {
1440                 m_hasBreakableChar = true;
1441             }
1442 
1443             if (currMinWidth > m_minWidth) {
1444                 m_minWidth = currMinWidth;
1445             }
1446             currMinWidth = 0;
1447 
1448             if (isNewline) { // Only set if isPre was true and we saw a newline.
1449                 if (firstLine) {
1450                     firstLine = false;
1451                     if (!style()->autoWrap()) {
1452                         m_beginMinWidth = currMaxWidth;
1453                     }
1454                 }
1455 
1456                 if (currMaxWidth > m_maxWidth) {
1457                     m_maxWidth = currMaxWidth;
1458                 }
1459                 currMaxWidth = 0;
1460             } else {
1461                 currMaxWidth += f->charWidth(str->s, str->l, i + wordlen, m_isSimpleText);
1462             }
1463         }
1464     }
1465 
1466     if (currMinWidth > m_minWidth) {
1467         m_minWidth = currMinWidth;
1468     }
1469     if (currMaxWidth > m_maxWidth) {
1470         m_maxWidth = currMaxWidth;
1471     }
1472 
1473     if (!style()->autoWrap()) {
1474         m_minWidth = m_maxWidth;
1475         if (style()->preserveLF()) {
1476             if (firstLine) {
1477                 m_beginMinWidth = m_maxWidth;
1478             }
1479             m_endMinWidth = currMaxWidth;
1480         }
1481     }
1482 
1483     setMinMaxKnown();
1484     //qCDebug(KHTML_LOG) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth;
1485 
1486 }
1487 
1488 int RenderText::minXPos() const
1489 {
1490     if (!m_firstTextBox) {
1491         return 0;
1492     }
1493     int retval = 6666666;
1494     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1495         retval = qMin(retval, static_cast<int>(box->m_x));
1496     }
1497     return retval;
1498 }
1499 
1500 int RenderText::inlineXPos() const
1501 {
1502     return minXPos();
1503 }
1504 
1505 int RenderText::inlineYPos() const
1506 {
1507     return m_firstTextBox ? m_firstTextBox->yPos() : 0;
1508 }
1509 
1510 const QFont &RenderText::font()
1511 {
1512     return style()->font();
1513 }
1514 
1515 void RenderText::setText(DOMStringImpl *text, bool force)
1516 {
1517     if (!force && str == text) {
1518         return;
1519     }
1520 
1521     setTextInternal(text);
1522 }
1523 
1524 void RenderText::setTextInternal(DOMStringImpl *text)
1525 {
1526     DOMStringImpl *oldstr = str;
1527     if (text && style()) {
1528         str = text->collapseWhiteSpace(style()->preserveLF(), style()->preserveWS());
1529     } else {
1530         str = text;
1531     }
1532 
1533     if (str) {
1534         str->ref();
1535     }
1536     if (oldstr) {
1537         oldstr->deref();
1538     }
1539 
1540     if (str && style()) {
1541         oldstr = str;
1542         switch (style()->textTransform()) {
1543         case CAPITALIZE: {
1544             RenderObject *o;
1545             bool runOnString = false;
1546             // find previous non-empty text renderer if one exists
1547             for (o = previousRenderer(); o; o = o->previousRenderer()) {
1548                 if (!o->isInlineFlow()) {
1549                     if (!o->isText()) {
1550                         break;
1551                     }
1552                     DOMStringImpl *prevStr = static_cast<RenderText *>(o)->string();
1553                     // !prevStr can happen with css like "content:open-quote;"
1554                     if (!prevStr) {
1555                         break;
1556                     }
1557                     if (prevStr->length() == 0) {
1558                         continue;
1559                     }
1560                     QChar c = (*prevStr)[prevStr->length() - 1];
1561                     if (!c.isSpace()) {
1562                         runOnString = true;
1563                     }
1564                     break;
1565                 }
1566             }
1567             str = str->capitalize(runOnString);
1568             break;
1569         }
1570         case UPPERCASE:
1571             str = str->upper();
1572             break;
1573         case LOWERCASE:
1574             str = str->lower();
1575             break;
1576         case TTNONE:
1577         default:
1578             break;
1579         }
1580         str->ref();
1581         oldstr->deref();
1582     }
1583 
1584     // ### what should happen if we change the text of a
1585     // RenderBR object ?
1586     KHTMLAssert(!isBR() || (str->l == 1 && (*str->s) == '\n'));
1587     KHTMLAssert(!str->l || str->s);
1588 
1589     if (parent()) {
1590         setNeedsLayoutAndMinMaxRecalc();
1591     }
1592 #ifdef BIDI_DEBUG
1593     QString cstr = QString::fromRawData(str->s, str->l);
1594     qCDebug(KHTML_LOG) << "RenderText::setText( " << cstr.length() << " ) '" << cstr << "'";
1595 #endif
1596 }
1597 
1598 int RenderText::height() const
1599 {
1600     int retval = 0;
1601     if (firstTextBox())
1602 #ifdef I_LOVE_UPDATING_TESTREGRESSIONS_BASELINES
1603         retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y;
1604 #else
1605         retval = lastTextBox()->m_y + m_lineHeight - firstTextBox()->m_y;
1606     else {
1607         retval = metrics(false).height();
1608     }
1609 #endif
1610     return retval;
1611 }
1612 
1613 short RenderText::lineHeight(bool firstLine) const
1614 {
1615     if (firstLine) {
1616         return RenderObject::lineHeight(firstLine);
1617     }
1618 
1619     return m_lineHeight;
1620 }
1621 
1622 short RenderText::baselinePosition(bool firstLine) const
1623 {
1624     const QFontMetrics &fm = metrics(firstLine);
1625     return fm.ascent() +
1626            (lineHeight(firstLine) - fm.height()) / 2;
1627 }
1628 
1629 InlineBox *RenderText::createInlineBox(bool, bool isRootLineBox)
1630 {
1631     KHTMLAssert(!isRootLineBox);
1632     Q_UNUSED(isRootLineBox);
1633     InlineTextBox *textBox = new(renderArena()) InlineTextBox(this);
1634     if (!m_firstTextBox) {
1635         m_firstTextBox = m_lastTextBox = textBox;
1636     } else {
1637         m_lastTextBox->setNextLineBox(textBox);
1638         textBox->setPreviousLineBox(m_lastTextBox);
1639         m_lastTextBox = textBox;
1640     }
1641     return textBox;
1642 }
1643 
1644 void RenderText::position(InlineBox *box, int from, int len, bool reverse)
1645 {
1646 //qCDebug(KHTML_LOG) << "position: from="<<from<<" len="<<len;
1647 
1648     reverse = reverse && !style()->visuallyOrdered();
1649 
1650     KHTMLAssert(box->isInlineTextBox());
1651     InlineTextBox *s = static_cast<InlineTextBox *>(box);
1652     s->m_start = from;
1653     s->m_len = len;
1654     s->m_reversed = reverse;
1655 }
1656 
1657 unsigned int RenderText::width(unsigned int from, unsigned int len, bool firstLine) const
1658 {
1659     if (!str->s || from > str->l) {
1660         return 0;
1661     }
1662     if (from + len > str->l) {
1663         len = str->l - from;
1664     }
1665 
1666     const Font *f = htmlFont(firstLine);
1667     return width(from, len, f);
1668 }
1669 
1670 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f) const
1671 {
1672     if (!str->s || from > str->l) {
1673         return 0;
1674     }
1675     if (from + len > str->l) {
1676         len = str->l - from;
1677     }
1678 
1679     if (f == &style()->htmlFont() && from == 0 && len == str->l) {
1680         return m_maxWidth;
1681     }
1682 
1683     int w = f->width(str->s, str->l, from, len, m_isSimpleText);
1684 
1685     //qCDebug(KHTML_LOG) << "RenderText::width(" << from << ", " << len << ") = " << w;
1686     return w;
1687 }
1688 
1689 short RenderText::width() const
1690 {
1691     int w;
1692     int minx = 100000000;
1693     int maxx = 0;
1694     // slooow
1695     for (InlineTextBox *s = firstTextBox(); s; s = s->nextTextBox()) {
1696         if (s->m_x < minx) {
1697             minx = s->m_x;
1698         }
1699         if (s->m_x + s->m_width > maxx) {
1700             maxx = s->m_x + s->m_width;
1701         }
1702     }
1703 
1704     w = qMax(0, maxx - minx);
1705 
1706     return w;
1707 }
1708 
1709 void RenderText::repaint(Priority p)
1710 {
1711     RenderObject *cb = containingBlock();
1712     if (cb) {
1713         cb->repaint(p);
1714     }
1715 }
1716 
1717 QList< QRectF > RenderText::getClientRects()
1718 {
1719     QList<QRectF> list;
1720 
1721     int x = 0;
1722     int y = 0;
1723     absolutePosition(x, y);
1724 
1725     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1726         QRectF textBoxRect(box->xPos() + x, box->yPos() + y,
1727                            box->width(), box->height());
1728 
1729         list.append(clientRectToViewport(textBoxRect));
1730     }
1731     return list;
1732 }
1733 
1734 bool RenderText::isFixedWidthFont() const
1735 {
1736     return QFontInfo(style()->font()).fixedPitch();
1737 }
1738 
1739 short RenderText::verticalPositionHint(bool firstLine) const
1740 {
1741     return parent()->verticalPositionHint(firstLine);
1742 }
1743 
1744 const QFontMetrics &RenderText::metrics(bool firstLine) const
1745 {
1746     if (firstLine && hasFirstLine()) {
1747         RenderStyle *pseudoStyle  = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
1748         if (pseudoStyle) {
1749             return pseudoStyle->fontMetrics();
1750         }
1751     }
1752     return style()->fontMetrics();
1753 }
1754 
1755 const Font *RenderText::htmlFont(bool firstLine) const
1756 {
1757     const Font *f = nullptr;
1758     if (firstLine && hasFirstLine()) {
1759         RenderStyle *pseudoStyle  = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
1760         if (pseudoStyle) {
1761             f = &pseudoStyle->htmlFont();
1762         }
1763     } else {
1764         f = &style()->htmlFont();
1765     }
1766     return f;
1767 }
1768 
1769 bool RenderText::containsOnlyWhitespace(unsigned int from, unsigned int len) const
1770 {
1771     unsigned int currPos;
1772     for (currPos = from;
1773             currPos < from + len && (str->s[currPos] == '\n' || str->s[currPos].direction() == QChar::DirWS);
1774             currPos++) {};
1775     return currPos >= (from + len);
1776 }
1777 
1778 void RenderText::trimmedMinMaxWidth(int &beginMinW, bool &beginWS,
1779                                     int &endMinW, bool &endWS,
1780                                     bool &hasBreakableChar, bool &hasBreak,
1781                                     int &beginMaxW, int &endMaxW,
1782                                     int &minW, int &maxW, bool &stripFrontSpaces)
1783 {
1784     bool preserveWS = style()->preserveWS();
1785     bool preserveLF = style()->preserveLF();
1786     bool autoWrap = style()->autoWrap();
1787     if (preserveWS) {
1788         stripFrontSpaces = false;
1789     }
1790 
1791     int len = str->l;
1792     if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
1793         maxW = 0;
1794         hasBreak = false;
1795         return;
1796     }
1797 
1798     minW = m_minWidth;
1799     maxW = m_maxWidth;
1800     beginWS = stripFrontSpaces ? false : m_hasBeginWS;
1801     endWS = m_hasEndWS;
1802 
1803     beginMinW = m_beginMinWidth;
1804     endMinW = m_endMinWidth;
1805 
1806     hasBreakableChar = m_hasBreakableChar;
1807     hasBreak = m_hasBreak;
1808 
1809     if (stripFrontSpaces && (str->s[0].direction() == QChar::DirWS || (!preserveLF && str->s[0] == '\n'))) {
1810         const Font *f = htmlFont(false);
1811         QChar space[1]; space[0] = ' ';
1812         int spaceWidth = f->charWidth(space, 1, 0, m_isSimpleText);
1813         maxW -= spaceWidth;
1814     }
1815 
1816     stripFrontSpaces = !preserveWS && m_hasEndWS;
1817 
1818     if (!autoWrap) {
1819         minW = maxW;
1820     } else if (minW > maxW) {
1821         minW = maxW;
1822     }
1823 
1824     // Compute our max widths by scanning the string for newlines.
1825     if (hasBreak) {
1826         const Font *f = htmlFont(false);
1827         bool firstLine = true;
1828         beginMaxW = endMaxW = maxW;
1829         for (int i = 0; i < len; i++) {
1830             int linelen = 0;
1831             while (i + linelen < len && str->s[i + linelen] != '\n') {
1832                 linelen++;
1833             }
1834 
1835             if (linelen) {
1836                 endMaxW = f->width(str->s, str->l, i, linelen, m_isSimpleText);
1837                 if (firstLine) {
1838                     firstLine = false;
1839                     beginMaxW = endMaxW;
1840                 }
1841                 i += linelen;
1842             } else if (firstLine) {
1843                 beginMaxW = 0;
1844                 firstLine = false;
1845             }
1846             if (i == len - 1)
1847                 // A <pre> run that ends with a newline, as in, e.g.,
1848                 // <pre>Some text\n\n<span>More text</pre>
1849             {
1850                 endMaxW = 0;
1851             }
1852         }
1853     }
1854 }
1855 
1856 bool RenderText::isPointInsideSelection(int x, int y, const Selection &) const
1857 {
1858     RenderText *rt = const_cast<RenderText *>(this);
1859     SelectionState selstate = selectionState();
1860     if (selstate == SelectionInside) {
1861         return true;
1862     }
1863     if (selstate == SelectionNone) {
1864         return false;
1865     }
1866     if (!firstTextBox()) {
1867         return false;
1868     }
1869 
1870     int absx, absy;
1871     if (!rt->absolutePosition(absx, absy)) {
1872         return false;
1873     }
1874 
1875     int startPos, endPos, offset;
1876     rt->selectionStartEnd(startPos, endPos);
1877 
1878     if (selstate == SelectionEnd) {
1879         startPos = 0;
1880     }
1881     if (selstate == SelectionStart) {
1882         endPos = str->l;
1883     }
1884 
1885     SelPointState sps;
1886     DOM::NodeImpl *node;
1887     /*FindSelectionResult res = */rt->checkSelectionPoint(x, y, absx, absy, node, offset, sps);
1888 
1889     return offset >= startPos && offset < endPos;
1890 }
1891 
1892 #ifdef ENABLE_DUMP
1893 
1894 static QString quoteAndEscapeNonPrintables(const QString &s)
1895 {
1896     QString result;
1897     result += '"';
1898     for (int i = 0; i != s.length(); ++i) {
1899         QChar c = s.at(i);
1900         if (c == '\\') {
1901             result += "\\\\";
1902         } else if (c == '"') {
1903             result += "\\\"";
1904         } else {
1905             ushort u = c.unicode();
1906             if (u >= 0x20 && u < 0x7F) {
1907                 result += c;
1908             } else {
1909                 QString hex;
1910                 hex.sprintf("\\x{%X}", u);
1911                 result += hex;
1912             }
1913         }
1914     }
1915     result += '"';
1916     return result;
1917 }
1918 
1919 static void writeTextRun(QTextStream &ts, const RenderText &o, const InlineTextBox &run)
1920 {
1921     ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width << ": "
1922        << quoteAndEscapeNonPrintables(o.data().string().mid(run.m_start, run.m_len));
1923 }
1924 
1925 void RenderText::dump(QTextStream &stream, const QString &ind) const
1926 {
1927     RenderObject::dump(stream, ind);
1928 
1929     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1930         stream << endl << ind << "   ";
1931         writeTextRun(stream, *this, *box);
1932     }
1933 }
1934 #endif
1935 
1936 RenderTextFragment::RenderTextFragment(DOM::NodeImpl *_node, DOM::DOMStringImpl *_str,
1937                                        int startOffset, int endOffset)
1938     : RenderText(_node, _str->substring(startOffset, endOffset)),
1939       m_start(startOffset), m_end(endOffset), m_generatedContentStr(nullptr), m_firstLetter(nullptr)
1940 {}
1941 
1942 RenderTextFragment::RenderTextFragment(DOM::NodeImpl *_node, DOM::DOMStringImpl *_str)
1943     : RenderText(_node, _str), m_start(0), m_firstLetter(nullptr)
1944 {
1945     m_generatedContentStr = _str;
1946     if (_str) {
1947         _str->ref();
1948         m_end = _str->l;
1949     } else {
1950         m_end = 0;
1951     }
1952 }
1953 
1954 RenderTextFragment::~RenderTextFragment()
1955 {
1956     if (m_generatedContentStr) {
1957         m_generatedContentStr->deref();
1958     }
1959 }
1960 
1961 void RenderTextFragment::detach()
1962 {
1963     if (m_firstLetter) {
1964         m_firstLetter->detach();
1965     }
1966 
1967     RenderText::detach();
1968 }
1969 
1970 bool RenderTextFragment::isTextFragment() const
1971 {
1972     return true;
1973 }
1974 
1975 DOM::DOMStringImpl *RenderTextFragment::originalString() const
1976 {
1977     DOM::DOMStringImpl *result = nullptr;
1978     if (element()) {
1979         result = element()->string();
1980     } else {
1981         result = contentString();
1982     }
1983     if (result && (start() > 0 || start() < result->l)) {
1984         result = result->substring(start(), end());
1985     }
1986     return result;
1987 }
1988 
1989 void RenderTextFragment::setTextInternal(DOM::DOMStringImpl *text)
1990 {
1991     if (m_firstLetter) {
1992         m_firstLetter->detach();
1993         m_firstLetter = nullptr;
1994     }
1995     RenderText::setTextInternal(text);
1996 }
1997 
1998 #undef BIDI_DEBUG
1999 #undef DEBUG_LAYOUT