src/gui/text/qtextlayout.cpp
branchRCL_3
changeset 8 3f74d0d4af4c
parent 4 3b1da2848fc7
equal deleted inserted replaced
6:dee5afe5301f 8:3f74d0d4af4c
  1329         return;
  1329         return;
  1330 
  1330 
  1331     QTextLine l(line, d);
  1331     QTextLine l(line, d);
  1332     const QScriptLine &sl = d->lines[line];
  1332     const QScriptLine &sl = d->lines[line];
  1333 
  1333 
  1334     const qreal x = position.x() + l.cursorToX(cursorPosition);
  1334     qreal x = position.x() + l.cursorToX(cursorPosition);
  1335 
  1335 
  1336     int itm = d->findItem(cursorPosition - 1);
  1336     int itm = d->findItem(cursorPosition - 1);
  1337     QFixed base = sl.base();
  1337     QFixed base = sl.base();
  1338     QFixed descent = sl.descent;
  1338     QFixed descent = sl.descent;
  1339     bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft);
  1339     bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft);
  1348     qreal y = position.y() + (sl.y + sl.base() - base).toReal();
  1348     qreal y = position.y() + (sl.y + sl.base() - base).toReal();
  1349     bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
  1349     bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
  1350                               && (p->transform().type() > QTransform::TxTranslate);
  1350                               && (p->transform().type() > QTransform::TxTranslate);
  1351     if (toggleAntialiasing)
  1351     if (toggleAntialiasing)
  1352         p->setRenderHint(QPainter::Antialiasing);
  1352         p->setRenderHint(QPainter::Antialiasing);
       
  1353 #if defined(QT_MAC_USE_COCOA)
       
  1354     // Always draw the cursor aligned to pixel boundary.
       
  1355     x = qRound(x);
       
  1356 #endif
  1353     p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
  1357     p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
  1354     if (toggleAntialiasing)
  1358     if (toggleAntialiasing)
  1355         p->setRenderHint(QPainter::Antialiasing, false);
  1359         p->setRenderHint(QPainter::Antialiasing, false);
  1356     if (d->layoutData->hasBidi) {
  1360     if (d->layoutData->hasBidi) {
  1357         const int arrow_extent = 4;
  1361         const int arrow_extent = 4;
  1638 
  1642 
  1639 namespace {
  1643 namespace {
  1640 
  1644 
  1641     struct LineBreakHelper
  1645     struct LineBreakHelper
  1642     {
  1646     {
  1643         LineBreakHelper() : glyphCount(0), maxGlyphs(0), manualWrap(false) {}
  1647         LineBreakHelper()
       
  1648             : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
       
  1649               manualWrap(false)
       
  1650         {
       
  1651         }
       
  1652 
  1644 
  1653 
  1645         QScriptLine tmpData;
  1654         QScriptLine tmpData;
  1646         QScriptLine spaceData;
  1655         QScriptLine spaceData;
  1647 
  1656 
       
  1657         QGlyphLayout glyphs;
       
  1658 
  1648         int glyphCount;
  1659         int glyphCount;
  1649         int maxGlyphs;
  1660         int maxGlyphs;
       
  1661         int currentPosition;
  1650 
  1662 
  1651         QFixed minw;
  1663         QFixed minw;
  1652         QFixed softHyphenWidth;
  1664         QFixed softHyphenWidth;
  1653         QFixed rightBearing;
  1665         QFixed rightBearing;
       
  1666         QFixed minimumRightBearing;
       
  1667 
       
  1668         QFontEngine *fontEngine;
       
  1669         const unsigned short *logClusters;
  1654 
  1670 
  1655         bool manualWrap;
  1671         bool manualWrap;
  1656 
  1672 
  1657         bool checkFullOtherwiseExtend(QScriptLine &line);
  1673         bool checkFullOtherwiseExtend(QScriptLine &line);
       
  1674 
       
  1675         QFixed calculateNewWidth(const QScriptLine &line) const {
       
  1676             return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
       
  1677                     - qMin(rightBearing, QFixed());
       
  1678         }
       
  1679 
       
  1680         inline glyph_t currentGlyph() const
       
  1681         {
       
  1682             Q_ASSERT(currentPosition > 0);
       
  1683             return glyphs.glyphs[logClusters[currentPosition - 1]];
       
  1684         }
       
  1685 
       
  1686         inline void adjustRightBearing()
       
  1687         {
       
  1688             if (currentPosition <= 0)
       
  1689                 return;
       
  1690 
       
  1691             qreal rb;
       
  1692             fontEngine->getGlyphBearings(currentGlyph(), 0, &rb);
       
  1693             rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
       
  1694         }
       
  1695 
       
  1696         inline void resetRightBearing()
       
  1697         {
       
  1698             rightBearing = QFixed(1); // Any positive number is defined as invalid since only
       
  1699                                       // negative right bearings are interesting to us.
       
  1700         }
  1658     };
  1701     };
  1659 
  1702 
  1660 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
  1703 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
  1661 {        
  1704 {        
  1662     LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
  1705     LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
  1663 
  1706 
  1664     QFixed newWidth = line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + rightBearing;
  1707     QFixed newWidth = calculateNewWidth(line);
  1665     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
  1708     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
  1666         return true;
  1709         return true;
  1667 
  1710 
  1668     minw = qMax(minw, tmpData.textWidth);
  1711     minw = qMax(minw, tmpData.textWidth);
  1669     line += tmpData;
  1712     line += tmpData;
  1735     LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
  1778     LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
  1736 
  1779 
  1737     Qt::Alignment alignment = eng->option.alignment();
  1780     Qt::Alignment alignment = eng->option.alignment();
  1738 
  1781 
  1739     const HB_CharAttributes *attributes = eng->attributes();
  1782     const HB_CharAttributes *attributes = eng->attributes();
  1740     int pos = line.from;
  1783     lbh.currentPosition = line.from;
  1741     int end = 0;
  1784     int end = 0;
  1742     QGlyphLayout glyphs;
  1785     lbh.logClusters = eng->layoutData->logClustersPtr;
  1743     const unsigned short *logClusters = eng->layoutData->logClustersPtr;
       
  1744 
  1786 
  1745     while (newItem < eng->layoutData->items.size()) {
  1787     while (newItem < eng->layoutData->items.size()) {
  1746         lbh.rightBearing = 0;
  1788         lbh.resetRightBearing();
  1747         lbh.softHyphenWidth = 0;
  1789         lbh.softHyphenWidth = 0;
  1748         if (newItem != item) {
  1790         if (newItem != item) {
  1749             item = newItem;
  1791             item = newItem;
  1750             const QScriptItem &current = eng->layoutData->items[item];
  1792             const QScriptItem &current = eng->layoutData->items[item];
  1751             if (!current.num_glyphs) {
  1793             if (!current.num_glyphs) {
  1752                 eng->shape(item);
  1794                 eng->shape(item);
  1753                 attributes = eng->attributes();
  1795                 attributes = eng->attributes();
  1754                 logClusters = eng->layoutData->logClustersPtr;
  1796                 lbh.logClusters = eng->layoutData->logClustersPtr;
  1755             }
  1797             }
  1756             pos = qMax(line.from, current.position);
  1798             lbh.currentPosition = qMax(line.from, current.position);
  1757             end = current.position + eng->length(item);
  1799             end = current.position + eng->length(item);
  1758             glyphs = eng->shapedGlyphs(&current);
  1800             lbh.glyphs = eng->shapedGlyphs(&current);
  1759         }
  1801         }
  1760         const QScriptItem &current = eng->layoutData->items[item];
  1802         const QScriptItem &current = eng->layoutData->items[item];
       
  1803         QFontEngine *fontEngine = eng->fontEngine(current);
       
  1804         if (lbh.fontEngine != fontEngine) {
       
  1805             lbh.fontEngine = fontEngine;
       
  1806             lbh.minimumRightBearing = qMin(QFixed(),
       
  1807                                            QFixed::fromReal(fontEngine->minRightBearing()));
       
  1808         }
  1761 
  1809 
  1762         lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
  1810         lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
  1763                                    current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
  1811                                    current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
  1764                                                                             current.ascent);
  1812                                                                             current.ascent);
  1765         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1813         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1785             // if the line consists only of the line separator make sure
  1833             // if the line consists only of the line separator make sure
  1786             // we have a sane height
  1834             // we have a sane height
  1787             if (!line.length && !lbh.tmpData.length)
  1835             if (!line.length && !lbh.tmpData.length)
  1788                 line.setDefaultHeight(eng);
  1836                 line.setDefaultHeight(eng);
  1789             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1837             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1790                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
  1838                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1791                                current, logClusters, glyphs);
  1839                                current, lbh.logClusters, lbh.glyphs);
  1792             } else {
  1840             } else {
  1793                 lbh.tmpData.length++;
  1841                 lbh.tmpData.length++;
  1794             }
  1842             }
  1795             line += lbh.tmpData;
  1843             line += lbh.tmpData;
  1796             goto found;
  1844             goto found;
  1805 
  1853 
  1806             newItem = item + 1;
  1854             newItem = item + 1;
  1807             ++lbh.glyphCount;
  1855             ++lbh.glyphCount;
  1808             if (lbh.checkFullOtherwiseExtend(line))
  1856             if (lbh.checkFullOtherwiseExtend(line))
  1809                 goto found;
  1857                 goto found;
  1810         } else if (attributes[pos].whiteSpace) {
  1858         } else if (attributes[lbh.currentPosition].whiteSpace) {
  1811             while (pos < end && attributes[pos].whiteSpace)
  1859             while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
  1812                 addNextCluster(pos, end, lbh.spaceData, lbh.glyphCount,
  1860                 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
  1813                                current, logClusters, glyphs);
  1861                                current, lbh.logClusters, lbh.glyphs);
  1814 
  1862 
  1815             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1863             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1816                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
  1864                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
  1817                 goto found;
  1865                 goto found;
  1818             }
  1866             }
  1819         } else {
  1867         } else {
  1820             bool sb_or_ws = false;
  1868             bool sb_or_ws = false;
  1821             do {
  1869             do {
  1822                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
  1870                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1823                                current, logClusters, glyphs);
  1871                                current, lbh.logClusters, lbh.glyphs);
  1824 
  1872 
  1825                 if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) {
  1873                 if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) {
  1826                     sb_or_ws = true;
  1874                     sb_or_ws = true;
  1827                     break;
  1875                     break;
  1828                 } else if (breakany && attributes[pos].charStop) {
  1876                 } else if (breakany && attributes[lbh.currentPosition].charStop) {
  1829                     break;
  1877                     break;
  1830                 }
  1878                 }
  1831             } while (pos < end);
  1879             } while (lbh.currentPosition < end);
  1832             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
  1880             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
  1833 
  1881 
  1834             if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) {
  1882             if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) {
  1835                 // if we are splitting up a word because of
  1883                 // if we are splitting up a word because of
  1836                 // a soft hyphen then we ...
  1884                 // a soft hyphen then we ...
  1837                 //
  1885                 //
  1838                 //  a) have to take the width of the soft hyphen into
  1886                 //  a) have to take the width of the soft hyphen into
  1839                 //     account to see if the first syllable(s) /and/
  1887                 //     account to see if the first syllable(s) /and/
  1847                 //     switch to break-anywhere mode, in which we
  1895                 //     switch to break-anywhere mode, in which we
  1848                 //     want the soft-hyphen to slip into the next line
  1896                 //     want the soft-hyphen to slip into the next line
  1849                 //     and thus become invisible again.
  1897                 //     and thus become invisible again.
  1850                 //
  1898                 //
  1851                 if (line.length)
  1899                 if (line.length)
  1852                     lbh.softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
  1900                     lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
  1853                 else if (breakany)
  1901                 else if (breakany)
  1854                     lbh.tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
  1902                     lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
  1855             }
  1903             }
  1856 
  1904 
  1857             // The actual width of the text needs to take the right bearing into account. The
  1905             // The actual width of the text needs to take the right bearing into account. The
  1858             // right bearing is left-ward, which means that if the rightmost pixel is to the right
  1906             // right bearing is left-ward, which means that if the rightmost pixel is to the right
  1859             // of the advance of the glyph, the bearing will be negative. We flip the sign
  1907             // of the advance of the glyph, the bearing will be negative. We flip the sign
  1860             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
  1908             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
  1861             if (pos) {
  1909             // We ignore the right bearing if the minimum negative bearing is too little to
  1862                 QFontEngine *fontEngine = eng->fontEngine(current);
  1910             // expand the text beyond the edge.
  1863                 glyph_t glyph = glyphs.glyphs[logClusters[pos - 1]];
  1911             if (sb_or_ws|breakany) {
  1864                 glyph_metrics_t gi = fontEngine->boundingBox(glyph);
  1912                 if (lbh.calculateNewWidth(line) + lbh.minimumRightBearing > line.width)
  1865                 if (gi.isValid())
  1913                     lbh.adjustRightBearing();
  1866                     lbh.rightBearing = qMax(QFixed(), -(gi.xoff - gi.x - gi.width));
  1914                 if (lbh.checkFullOtherwiseExtend(line)) {
       
  1915                     if (!breakany) {
       
  1916                         line.textWidth += lbh.softHyphenWidth;
       
  1917                     }
       
  1918 
       
  1919                     goto found;
       
  1920                 }
  1867             }
  1921             }
  1868 
  1922         }
  1869             if ((sb_or_ws|breakany) && lbh.checkFullOtherwiseExtend(line)) {
  1923         if (lbh.currentPosition == end)
  1870                 if (!breakany) {
       
  1871                     line.textWidth += lbh.softHyphenWidth;
       
  1872                 }
       
  1873 
       
  1874                 line.textWidth += lbh.rightBearing;
       
  1875 
       
  1876                 goto found;
       
  1877             }
       
  1878         }
       
  1879         if (pos == end)
       
  1880             newItem = item + 1;
  1924             newItem = item + 1;
  1881     }
  1925     }
  1882     LB_DEBUG("reached end of line");
  1926     LB_DEBUG("reached end of line");
  1883     lbh.checkFullOtherwiseExtend(line);
  1927     lbh.checkFullOtherwiseExtend(line);
  1884     line.textWidth += lbh.rightBearing;
       
  1885 
       
  1886 found:       
  1928 found:       
       
  1929     if (lbh.rightBearing > 0) // If right bearing has not yet been adjusted
       
  1930         lbh.adjustRightBearing();
       
  1931     line.textWidth -= qMin(QFixed(), lbh.rightBearing);
       
  1932 
  1887     if (line.length == 0) {
  1933     if (line.length == 0) {
  1888         LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
  1934         LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
  1889                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
  1935                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
  1890                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
  1936                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
  1891         line += lbh.tmpData;
  1937         line += lbh.tmpData;