src/gui/text/qtextlayout.cpp
changeset 19 fcece45ef507
parent 18 2f34d5167611
child 29 b72c6db6890b
equal deleted inserted replaced
18:2f34d5167611 19:fcece45ef507
  1642 
  1642 
  1643 namespace {
  1643 namespace {
  1644 
  1644 
  1645     struct LineBreakHelper
  1645     struct LineBreakHelper
  1646     {
  1646     {
  1647         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 
  1648 
  1653 
  1649         QScriptLine tmpData;
  1654         QScriptLine tmpData;
  1650         QScriptLine spaceData;
  1655         QScriptLine spaceData;
  1651 
  1656 
       
  1657         QGlyphLayout glyphs;
       
  1658 
  1652         int glyphCount;
  1659         int glyphCount;
  1653         int maxGlyphs;
  1660         int maxGlyphs;
       
  1661         int currentPosition;
  1654 
  1662 
  1655         QFixed minw;
  1663         QFixed minw;
  1656         QFixed softHyphenWidth;
  1664         QFixed softHyphenWidth;
  1657         QFixed rightBearing;
  1665         QFixed rightBearing;
       
  1666         QFixed minimumRightBearing;
       
  1667 
       
  1668         QFontEngine *fontEngine;
       
  1669         const unsigned short *logClusters;
  1658 
  1670 
  1659         bool manualWrap;
  1671         bool manualWrap;
  1660 
  1672 
  1661         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         }
  1662     };
  1701     };
  1663 
  1702 
  1664 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
  1703 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
  1665 {        
  1704 {        
  1666     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());
  1667 
  1706 
  1668     QFixed newWidth = line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + rightBearing;
  1707     QFixed newWidth = calculateNewWidth(line);
  1669     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
  1708     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
  1670         return true;
  1709         return true;
  1671 
  1710 
  1672     minw = qMax(minw, tmpData.textWidth);
  1711     minw = qMax(minw, tmpData.textWidth);
  1673     line += tmpData;
  1712     line += tmpData;
  1739     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());
  1740 
  1779 
  1741     Qt::Alignment alignment = eng->option.alignment();
  1780     Qt::Alignment alignment = eng->option.alignment();
  1742 
  1781 
  1743     const HB_CharAttributes *attributes = eng->attributes();
  1782     const HB_CharAttributes *attributes = eng->attributes();
  1744     int pos = line.from;
  1783     lbh.currentPosition = line.from;
  1745     int end = 0;
  1784     int end = 0;
  1746     QGlyphLayout glyphs;
  1785     lbh.logClusters = eng->layoutData->logClustersPtr;
  1747     const unsigned short *logClusters = eng->layoutData->logClustersPtr;
       
  1748 
  1786 
  1749     while (newItem < eng->layoutData->items.size()) {
  1787     while (newItem < eng->layoutData->items.size()) {
  1750         lbh.rightBearing = 0;
  1788         lbh.resetRightBearing();
  1751         lbh.softHyphenWidth = 0;
  1789         lbh.softHyphenWidth = 0;
  1752         if (newItem != item) {
  1790         if (newItem != item) {
  1753             item = newItem;
  1791             item = newItem;
  1754             const QScriptItem &current = eng->layoutData->items[item];
  1792             const QScriptItem &current = eng->layoutData->items[item];
  1755             if (!current.num_glyphs) {
  1793             if (!current.num_glyphs) {
  1756                 eng->shape(item);
  1794                 eng->shape(item);
  1757                 attributes = eng->attributes();
  1795                 attributes = eng->attributes();
  1758                 logClusters = eng->layoutData->logClustersPtr;
  1796                 lbh.logClusters = eng->layoutData->logClustersPtr;
  1759             }
  1797             }
  1760             pos = qMax(line.from, current.position);
  1798             lbh.currentPosition = qMax(line.from, current.position);
  1761             end = current.position + eng->length(item);
  1799             end = current.position + eng->length(item);
  1762             glyphs = eng->shapedGlyphs(&current);
  1800             lbh.glyphs = eng->shapedGlyphs(&current);
  1763         }
  1801         }
  1764         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         }
  1765 
  1809 
  1766         lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
  1810         lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
  1767                                    current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
  1811                                    current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
  1768                                                                             current.ascent);
  1812                                                                             current.ascent);
  1769         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1813         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
  1789             // if the line consists only of the line separator make sure
  1833             // if the line consists only of the line separator make sure
  1790             // we have a sane height
  1834             // we have a sane height
  1791             if (!line.length && !lbh.tmpData.length)
  1835             if (!line.length && !lbh.tmpData.length)
  1792                 line.setDefaultHeight(eng);
  1836                 line.setDefaultHeight(eng);
  1793             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1837             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
  1794                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
  1838                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1795                                current, logClusters, glyphs);
  1839                                current, lbh.logClusters, lbh.glyphs);
  1796             } else {
  1840             } else {
  1797                 lbh.tmpData.length++;
  1841                 lbh.tmpData.length++;
  1798             }
  1842             }
  1799             line += lbh.tmpData;
  1843             line += lbh.tmpData;
  1800             goto found;
  1844             goto found;
  1809 
  1853 
  1810             newItem = item + 1;
  1854             newItem = item + 1;
  1811             ++lbh.glyphCount;
  1855             ++lbh.glyphCount;
  1812             if (lbh.checkFullOtherwiseExtend(line))
  1856             if (lbh.checkFullOtherwiseExtend(line))
  1813                 goto found;
  1857                 goto found;
  1814         } else if (attributes[pos].whiteSpace) {
  1858         } else if (attributes[lbh.currentPosition].whiteSpace) {
  1815             while (pos < end && attributes[pos].whiteSpace)
  1859             while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
  1816                 addNextCluster(pos, end, lbh.spaceData, lbh.glyphCount,
  1860                 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
  1817                                current, logClusters, glyphs);
  1861                                current, lbh.logClusters, lbh.glyphs);
  1818 
  1862 
  1819             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1863             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  1820                 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.
  1821                 goto found;
  1865                 goto found;
  1822             }
  1866             }
  1823         } else {
  1867         } else {
  1824             bool sb_or_ws = false;
  1868             bool sb_or_ws = false;
  1825             do {
  1869             do {
  1826                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
  1870                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
  1827                                current, logClusters, glyphs);
  1871                                current, lbh.logClusters, lbh.glyphs);
  1828 
  1872 
  1829                 if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) {
  1873                 if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) {
  1830                     sb_or_ws = true;
  1874                     sb_or_ws = true;
  1831                     break;
  1875                     break;
  1832                 } else if (breakany && attributes[pos].charStop) {
  1876                 } else if (breakany && attributes[lbh.currentPosition].charStop) {
  1833                     break;
  1877                     break;
  1834                 }
  1878                 }
  1835             } while (pos < end);
  1879             } while (lbh.currentPosition < end);
  1836             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
  1880             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
  1837 
  1881 
  1838             if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) {
  1882             if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) {
  1839                 // if we are splitting up a word because of
  1883                 // if we are splitting up a word because of
  1840                 // a soft hyphen then we ...
  1884                 // a soft hyphen then we ...
  1841                 //
  1885                 //
  1842                 //  a) have to take the width of the soft hyphen into
  1886                 //  a) have to take the width of the soft hyphen into
  1843                 //     account to see if the first syllable(s) /and/
  1887                 //     account to see if the first syllable(s) /and/
  1851                 //     switch to break-anywhere mode, in which we
  1895                 //     switch to break-anywhere mode, in which we
  1852                 //     want the soft-hyphen to slip into the next line
  1896                 //     want the soft-hyphen to slip into the next line
  1853                 //     and thus become invisible again.
  1897                 //     and thus become invisible again.
  1854                 //
  1898                 //
  1855                 if (line.length)
  1899                 if (line.length)
  1856                     lbh.softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
  1900                     lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
  1857                 else if (breakany)
  1901                 else if (breakany)
  1858                     lbh.tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
  1902                     lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
  1859             }
  1903             }
  1860 
  1904 
  1861             // 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
  1862             // 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
  1863             // 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
  1864             // 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.
  1865             if (pos) {
  1909             // We ignore the right bearing if the minimum negative bearing is too little to
  1866                 QFontEngine *fontEngine = eng->fontEngine(current);
  1910             // expand the text beyond the edge.
  1867                 glyph_t glyph = glyphs.glyphs[logClusters[pos - 1]];
  1911             if (sb_or_ws|breakany) {
  1868                 glyph_metrics_t gi = fontEngine->boundingBox(glyph);
  1912                 if (lbh.calculateNewWidth(line) + lbh.minimumRightBearing > line.width)
  1869                 if (gi.isValid())
  1913                     lbh.adjustRightBearing();
  1870                     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                 }
  1871             }
  1921             }
  1872 
  1922         }
  1873             if ((sb_or_ws|breakany) && lbh.checkFullOtherwiseExtend(line)) {
  1923         if (lbh.currentPosition == end)
  1874                 if (!breakany) {
       
  1875                     line.textWidth += lbh.softHyphenWidth;
       
  1876                 }
       
  1877 
       
  1878                 line.textWidth += lbh.rightBearing;
       
  1879 
       
  1880                 goto found;
       
  1881             }
       
  1882         }
       
  1883         if (pos == end)
       
  1884             newItem = item + 1;
  1924             newItem = item + 1;
  1885     }
  1925     }
  1886     LB_DEBUG("reached end of line");
  1926     LB_DEBUG("reached end of line");
  1887     lbh.checkFullOtherwiseExtend(line);
  1927     lbh.checkFullOtherwiseExtend(line);
  1888     line.textWidth += lbh.rightBearing;
       
  1889 
       
  1890 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 
  1891     if (line.length == 0) {
  1933     if (line.length == 0) {
  1892         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",
  1893                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
  1935                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
  1894                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
  1936                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
  1895         line += lbh.tmpData;
  1937         line += lbh.tmpData;