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