summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qtextlayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text/qtextlayout.cpp')
-rw-r--r--src/gui/text/qtextlayout.cpp155
1 files changed, 101 insertions, 54 deletions
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index b150f50..60e5296 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -1589,27 +1589,54 @@ void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
#define LB_DEBUG if (0) qDebug
#endif
-static inline bool checkFullOtherwiseExtend(QScriptLine &line, QScriptLine &tmpData, QScriptLine &spaceData,
- int glyphCount, int maxGlyphs, QFixed &minw, bool manualWrap,
- QFixed softHyphenWidth = QFixed())
-{
+namespace {
+
+ struct LineBreakHelper
+ {
+ LineBreakHelper() : glyphCount(0), maxGlyphs(0), manualWrap(false) {}
+
+ QScriptLine tmpData;
+ QScriptLine spaceData;
+
+ int glyphCount;
+ int maxGlyphs;
+
+ QFixed minw;
+ QFixed softHyphenWidth;
+ QFixed rightBearing;
+
+ bool manualWrap;
+
+ bool checkFullOtherwiseExtend(QScriptLine &line);
+ };
+
+inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
+{
LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
- if (line.length && !manualWrap &&
- (line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth > line.width || glyphCount > maxGlyphs))
+
+ QFixed newWidth = line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + rightBearing;
+ if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
return true;
+
minw = qMax(minw, tmpData.textWidth);
line += tmpData;
line.textWidth += spaceData.textWidth;
+
line.length += spaceData.length;
tmpData.textWidth = 0;
tmpData.length = 0;
spaceData.textWidth = 0;
spaceData.length = 0;
+
return false;
}
+} // anonymous namespace
+
+
static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
- const QScriptItem &current, const unsigned short *logClusters, const QGlyphLayout &glyphs)
+ const QScriptItem &current, const unsigned short *logClusters,
+ const QGlyphLayout &glyphs)
{
int glyphPosition = logClusters[pos];
do { // got to the first next cluster
@@ -1642,9 +1669,13 @@ void QTextLine::layout_helper(int maxGlyphs)
Q_ASSERT(line.from < eng->layoutData->string.length());
+ LineBreakHelper lbh;
+
+ lbh.maxGlyphs = maxGlyphs;
+
QTextOption::WrapMode wrapMode = eng->option.wrapMode();
bool breakany = (wrapMode == QTextOption::WrapAnywhere);
- bool manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
+ lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
// #### binary search!
int item = -1;
@@ -1654,12 +1685,7 @@ void QTextLine::layout_helper(int maxGlyphs)
break;
}
- QFixed minw = 0;
- int glyphCount = 0;
-
LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
- QScriptLine tmpData;
- QScriptLine spaceData;
Qt::Alignment alignment = eng->option.alignment();
@@ -1668,7 +1694,10 @@ void QTextLine::layout_helper(int maxGlyphs)
int end = 0;
QGlyphLayout glyphs;
const unsigned short *logClusters = eng->layoutData->logClustersPtr;
+
while (newItem < eng->layoutData->items.size()) {
+ lbh.rightBearing = 0;
+ lbh.softHyphenWidth = 0;
if (newItem != item) {
item = newItem;
const QScriptItem &current = eng->layoutData->items[item];
@@ -1683,57 +1712,60 @@ void QTextLine::layout_helper(int maxGlyphs)
}
const QScriptItem &current = eng->layoutData->items[item];
- tmpData.ascent = qMax(tmpData.ascent, current.ascent);
- tmpData.descent = qMax(tmpData.descent, current.descent);
+ lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
+ lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
- if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ if (lbh.checkFullOtherwiseExtend(line))
goto found;
- QFixed x = line.x + line.textWidth + tmpData.textWidth + spaceData.textWidth;
- spaceData.textWidth += eng->calculateTabWidth(item, x);
- spaceData.length++;
+ QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
+ lbh.spaceData.textWidth += eng->calculateTabWidth(item, x);
+ lbh.spaceData.length++;
newItem = item + 1;
- ++glyphCount;
- if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ ++lbh.glyphCount;
+ if (lbh.checkFullOtherwiseExtend(line))
goto found;
} else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
// if the line consists only of the line separator make sure
// we have a sane height
- if (!line.length && !tmpData.length)
+ if (!line.length && !lbh.tmpData.length)
line.setDefaultHeight(eng);
if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
- addNextCluster(pos, end, tmpData, glyphCount, current, logClusters, glyphs);
+ addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
+ current, logClusters, glyphs);
} else {
- tmpData.length++;
+ lbh.tmpData.length++;
}
- line += tmpData;
+ line += lbh.tmpData;
goto found;
} else if (current.analysis.flags == QScriptAnalysis::Object) {
- tmpData.length++;
+ lbh.tmpData.length++;
QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
if (eng->block.docHandle())
eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
- tmpData.textWidth += current.width;
+ lbh.tmpData.textWidth += current.width;
newItem = item + 1;
- ++glyphCount;
- if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ ++lbh.glyphCount;
+ if (lbh.checkFullOtherwiseExtend(line))
goto found;
} else if (attributes[pos].whiteSpace) {
while (pos < end && attributes[pos].whiteSpace)
- addNextCluster(pos, end, spaceData, glyphCount, current, logClusters, glyphs);
+ addNextCluster(pos, end, lbh.spaceData, lbh.glyphCount,
+ current, logClusters, glyphs);
- if (!manualWrap && spaceData.textWidth > line.width) {
- spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
+ if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
+ lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
goto found;
}
} else {
bool sb_or_ws = false;
do {
- addNextCluster(pos, end, tmpData, glyphCount, current, logClusters, glyphs);
+ addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
+ current, logClusters, glyphs);
if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) {
sb_or_ws = true;
@@ -1742,9 +1774,8 @@ void QTextLine::layout_helper(int maxGlyphs)
break;
}
} while (pos < end);
- minw = qMax(tmpData.textWidth, minw);
+ lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
- QFixed softHyphenWidth;
if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) {
// if we are splitting up a word because of
// a soft hyphen then we ...
@@ -1763,16 +1794,29 @@ void QTextLine::layout_helper(int maxGlyphs)
// and thus become invisible again.
//
if (line.length)
- softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
+ lbh.softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
else if (breakany)
- tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
+ lbh.tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
}
- if ((sb_or_ws|breakany)
- && checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap, softHyphenWidth)) {
+ // The actual width of the text needs to take the right bearing into account. The
+ // right bearing is left-ward, which means that if the rightmost pixel is to the right
+ // of the advance of the glyph, the bearing will be negative. We flip the sign
+ // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
+ if (pos) {
+ QFontEngine *fontEngine = eng->fontEngine(current);
+ glyph_t glyph = glyphs.glyphs[logClusters[pos - 1]];
+ glyph_metrics_t gi = fontEngine->boundingBox(glyph);
+ lbh.rightBearing = -qRound(gi.xoff - gi.x - gi.width);
+ }
+
+ if ((sb_or_ws|breakany) && lbh.checkFullOtherwiseExtend(line)) {
if (!breakany) {
- line.textWidth += softHyphenWidth;
+ line.textWidth += lbh.softHyphenWidth;
}
+
+ line.textWidth += lbh.rightBearing;
+
goto found;
}
}
@@ -1780,45 +1824,48 @@ void QTextLine::layout_helper(int maxGlyphs)
newItem = item + 1;
}
LB_DEBUG("reached end of line");
- checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap);
-found:
+ lbh.checkFullOtherwiseExtend(line);
+ line.textWidth += lbh.rightBearing;
+
+found:
if (line.length == 0) {
LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
- tmpData.length, tmpData.textWidth.toReal(), spaceData.length, spaceData.textWidth.toReal());
- line += tmpData;
+ lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
+ lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
+ line += lbh.tmpData;
}
LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
- line.descent.toReal(), line.textWidth.toReal(), spaceData.width.toReal());
+ line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
- if (manualWrap) {
+ if (lbh.manualWrap) {
eng->minWidth = qMax(eng->minWidth, line.textWidth);
eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
} else {
- eng->minWidth = qMax(eng->minWidth, minw);
+ eng->minWidth = qMax(eng->minWidth, lbh.minw);
eng->maxWidth += line.textWidth;
}
if (line.textWidth > 0 && item < eng->layoutData->items.size())
- eng->maxWidth += spaceData.textWidth;
+ eng->maxWidth += lbh.spaceData.textWidth;
if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
- line.textWidth += spaceData.textWidth;
- line.length += spaceData.length;
- if (spaceData.length)
+ line.textWidth += lbh.spaceData.textWidth;
+ line.length += lbh.spaceData.length;
+ if (lbh.spaceData.length)
line.hasTrailingSpaces = true;
line.justified = false;
line.gridfitted = false;
if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
- if ((maxGlyphs != INT_MAX && glyphCount > maxGlyphs)
- || (maxGlyphs == INT_MAX && line.textWidth > line.width)) {
+ if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
+ || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
eng->option.setWrapMode(QTextOption::WrapAnywhere);
line.length = 0;
line.textWidth = 0;
- layout_helper(maxGlyphs);
+ layout_helper(lbh.maxGlyphs);
eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
}
}