0004-QTextLayout-fix-maximumWidth-for-a-text-containing-l.patch 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. From b58616bff6715a6c66bdc9019d008b7918d3ccc8 Mon Sep 17 00:00:00 2001
  2. From: Vladimir Belyavsky <belyavskyv@gmail.com>
  3. Date: Tue, 27 Sep 2022 16:57:08 +0300
  4. Subject: [PATCH] QTextLayout: fix maximumWidth() for a text containing line
  5. separator
  6. This is improved version of previous fix
  7. 013c346a8dcbd618febb07884c64c740daf9754d that was reverted because it
  8. broke some tests for Quick Text. The problem was that it did not work
  9. correctly in the case the text was wrapped to a fixed width.
  10. To deal with this we'll accumulate current line full width (as if it
  11. hadn't been wrapped) in layout data (layoutData->currentMaxWidth).
  12. Then when the next line is explicitly wrapped by line or paragraph
  13. separator, this accumulated width will be used to adjust layout's
  14. maximum width.
  15. Change-Id: Iad7119d9808e1db15fe1fbc5db049c3db928529f
  16. Fixes: QTBUG-89557
  17. Fixes: QTBUG-104986
  18. Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
  19. Upstream: https://github.com/qt/qtbase/commit/991c056438b311566bc4ea543af0f33dfd5dffbb
  20. [Thomas: Needed to backport fix for
  21. https://security-tracker.debian.org/tracker/CVE-2023-32763]
  22. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
  23. ---
  24. src/gui/text/qtextengine.cpp | 3 +++
  25. src/gui/text/qtextengine_p.h | 1 +
  26. src/gui/text/qtextlayout.cpp | 16 +++++++++++-----
  27. .../gui/text/qtextlayout/tst_qtextlayout.cpp | 16 +++++++++++++++-
  28. 4 files changed, 30 insertions(+), 6 deletions(-)
  29. diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
  30. index ec528760e3b..ef3cdcbaaa7 100644
  31. --- a/src/gui/text/qtextengine.cpp
  32. +++ b/src/gui/text/qtextengine.cpp
  33. @@ -2619,6 +2619,7 @@ QTextEngine::LayoutData::LayoutData()
  34. haveCharAttributes = false;
  35. logClustersPtr = nullptr;
  36. available_glyphs = 0;
  37. + currentMaxWidth = 0;
  38. }
  39. QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
  40. @@ -2651,6 +2652,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
  41. hasBidi = false;
  42. layoutState = LayoutEmpty;
  43. haveCharAttributes = false;
  44. + currentMaxWidth = 0;
  45. }
  46. QTextEngine::LayoutData::~LayoutData()
  47. @@ -2736,6 +2738,7 @@ void QTextEngine::freeMemory()
  48. layoutData->hasBidi = false;
  49. layoutData->layoutState = LayoutEmpty;
  50. layoutData->haveCharAttributes = false;
  51. + layoutData->currentMaxWidth = 0;
  52. layoutData->items.clear();
  53. }
  54. if (specialData)
  55. diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
  56. index 925dafe04e2..59e332c64ae 100644
  57. --- a/src/gui/text/qtextengine_p.h
  58. +++ b/src/gui/text/qtextengine_p.h
  59. @@ -385,6 +385,7 @@ public:
  60. uint layoutState : 2;
  61. uint memory_on_stack : 1;
  62. uint haveCharAttributes : 1;
  63. + QFixed currentMaxWidth;
  64. QString string;
  65. bool reallocate(int totalGlyphs);
  66. };
  67. diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
  68. index 9ae6bee2de3..2009dd3d0bb 100644
  69. --- a/src/gui/text/qtextlayout.cpp
  70. +++ b/src/gui/text/qtextlayout.cpp
  71. @@ -1808,6 +1808,7 @@ void QTextLine::layout_helper(int maxGlyphs)
  72. lbh.logClusters = eng->layoutData->logClustersPtr;
  73. lbh.previousGlyph = 0;
  74. + bool manuallyWrapped = false;
  75. bool hasInlineObject = false;
  76. QFixed maxInlineObjectHeight = 0;
  77. @@ -1883,6 +1884,7 @@ void QTextLine::layout_helper(int maxGlyphs)
  78. lbh.calculateRightBearingForPreviousGlyph();
  79. }
  80. line += lbh.tmpData;
  81. + manuallyWrapped = true;
  82. goto found;
  83. } else if (current.analysis.flags == QScriptAnalysis::Object) {
  84. lbh.whiteSpaceOrObject = true;
  85. @@ -1917,11 +1919,10 @@ void QTextLine::layout_helper(int maxGlyphs)
  86. addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
  87. current, lbh.logClusters, lbh.glyphs);
  88. }
  89. -
  90. - if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
  91. - goto found;
  92. - }
  93. } else {
  94. + if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
  95. + goto found;
  96. +
  97. lbh.whiteSpaceOrObject = false;
  98. bool sb_or_ws = false;
  99. lbh.saveCurrentGlyph();
  100. @@ -2104,7 +2105,12 @@ found:
  101. eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
  102. } else {
  103. eng->minWidth = qMax(eng->minWidth, lbh.minw);
  104. - eng->maxWidth += line.textWidth + lbh.spaceData.textWidth;
  105. + eng->layoutData->currentMaxWidth += line.textWidth;
  106. + if (!manuallyWrapped)
  107. + eng->layoutData->currentMaxWidth += lbh.spaceData.textWidth;
  108. + eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
  109. + if (manuallyWrapped)
  110. + eng->layoutData->currentMaxWidth = 0;
  111. }
  112. line.textWidth += trailingSpace;
  113. diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
  114. index 680c62e9825..5b14c4e1491 100644
  115. --- a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
  116. +++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
  117. @@ -2670,17 +2670,28 @@ void tst_QTextLayout::min_maximumWidth_data()
  118. QTest::newRow("long string") << QStringLiteral("lmong_long_crazy_87235982735_23857239682376923876923876-fuwhfhfw-names-AAAA-deeaois2019-03-03.and.more");
  119. QTest::newRow("QTBUG-106947") << QStringLiteral("text text");
  120. QTest::newRow("spaces") << QStringLiteral(" text text ");
  121. + QTest::newRow("QTBUG-104986") << QStringLiteral("text\ntext\ntext");
  122. + QTest::newRow("spaces + line breaks") << QStringLiteral(" \n text\n \ntext \n ");
  123. }
  124. void tst_QTextLayout::min_maximumWidth()
  125. {
  126. QFETCH(QString, text);
  127. + text.replace('\n', QChar::LineSeparator);
  128. QTextLayout layout(text, testFont);
  129. layout.setCacheEnabled(true);
  130. + QTextOption opt;
  131. + opt.setWrapMode(QTextOption::NoWrap);
  132. + layout.setTextOption(opt);
  133. + layout.beginLayout();
  134. + while (layout.createLine().isValid()) { }
  135. + layout.endLayout();
  136. +
  137. + const qreal nonWrappedMaxWidth = layout.maximumWidth();
  138. +
  139. for (int wrapMode = QTextOption::NoWrap; wrapMode <= QTextOption::WrapAtWordBoundaryOrAnywhere; ++wrapMode) {
  140. - QTextOption opt;
  141. opt.setWrapMode((QTextOption::WrapMode)wrapMode);
  142. layout.setTextOption(opt);
  143. layout.beginLayout();
  144. @@ -2689,6 +2700,9 @@ void tst_QTextLayout::min_maximumWidth()
  145. const qreal minWidth = layout.minimumWidth();
  146. const qreal maxWidth = layout.maximumWidth();
  147. + QCOMPARE_LE(minWidth, maxWidth);
  148. + QCOMPARE_LE(maxWidth, nonWrappedMaxWidth); // maxWidth for wrapped text shouldn't exceed maxWidth for the text without wrapping.
  149. +
  150. // Try the layout from slightly wider than the widest (maxWidth)
  151. // and narrow it down to slighly narrower than minWidth
  152. // layout.maximumWidth() should return the same regardless
  153. --
  154. 2.46.0