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 ¤t = eng->layoutData->items[item]; |
1792 const QScriptItem ¤t = 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(¤t); |
1800 lbh.glyphs = eng->shapedGlyphs(¤t); |
1759 } |
1801 } |
1760 const QScriptItem ¤t = eng->layoutData->items[item]; |
1802 const QScriptItem ¤t = 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; |