From 26fa1ac0d738d2c6de780545848abf10f823a9c0 Mon Sep 17 00:00:00 2001 From: Jonathan Clark Date: Fri, 6 Sep 2024 04:00:26 -0600 Subject: tdf#162803 editeng: Fix invalid kashida array after layout This change updates editeng to clear the kashida array during layout, at the same time it clears other justification data. Previously, editeng could recycle stale kashida arrays and apply them to lines with different lengths. This would result in stray kashida rendered at inappropriate positions in lines, or accessing memory past the end of the kashida array, resulting in assertions or nondeterministic crashes. Change-Id: I9c06239298d630f2d61b5e67a54f4c3e079c1193 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172967 Reviewed-by: Jonathan Clark Tested-by: Jenkins (cherry picked from commit 6eb0523e27473fe2ebe281b2682a5045f3b39312) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173088 Reviewed-by: Xisco Fauli --- editeng/qa/unit/core-test.cxx | 65 +++++++++++++++++++++++++++++++++++++ editeng/source/editeng/impedit3.cxx | 3 ++ 2 files changed, 68 insertions(+) diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx index 75e19df6028d..3987dbe89a66 100644 --- a/editeng/qa/unit/core-test.cxx +++ b/editeng/qa/unit/core-test.cxx @@ -125,6 +125,7 @@ public: void testCreateLines(); void testTdf154248MultilineFieldWrapping(); void testTdf151748StaleKashidaArray(); + void testTdf162803StaleKashidaArray(); DECL_STATIC_LINK( Test, CalcFieldValueHdl, EditFieldInfo*, void ); @@ -157,6 +158,7 @@ public: CPPUNIT_TEST(testCreateLines); CPPUNIT_TEST(testTdf154248MultilineFieldWrapping); CPPUNIT_TEST(testTdf151748StaleKashidaArray); + CPPUNIT_TEST(testTdf162803StaleKashidaArray); CPPUNIT_TEST_SUITE_END(); private: @@ -2296,6 +2298,7 @@ void Test::testTdf154248MultilineFieldWrapping() } } +// tdf#151748: Verify that editeng produces an empty kashida array if the line does not have room void Test::testTdf151748StaleKashidaArray() { ScopedVclPtrInstance pVirtualDevice(DeviceFormat::WITHOUT_ALPHA); @@ -2357,6 +2360,68 @@ void Test::testTdf151748StaleKashidaArray() } } +// tdf#162803: Verify that editeng clears stale kashida data during layout +void Test::testTdf162803StaleKashidaArray() +{ + ScopedVclPtrInstance pVirtualDevice(DeviceFormat::WITHOUT_ALPHA); + + EditEngine aEditEngine(mpItemPool.get()); + aEditEngine.SetRefDevice(pVirtualDevice.get()); + aEditEngine.SetPaperSize(Size(1500, 500)); + aEditEngine.SetDefaultHorizontalTextDirection(EEHorizontalTextDirection::R2L); + aEditEngine.SetText(u"خط تخوردگی و توسط"_ustr); + + CPPUNIT_ASSERT_EQUAL(true, aEditEngine.IsFormatted()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEditEngine.GetParagraphCount()); + + SfxItemSet aSet{ aEditEngine.GetParaAttribs(0) }; + aSet.Put(SvxAdjustItem{ SvxAdjust::Block, EE_PARA_JUST }); + aEditEngine.SetParaAttribs(0, aSet); + + CPPUNIT_ASSERT_EQUAL(SvxAdjust::Block, aEditEngine.GetParaAttrib(0, EE_PARA_JUST).GetAdjust()); + CPPUNIT_ASSERT_EQUAL(true, aEditEngine.IsFormatted()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aEditEngine.GetLineCount(0)); + + // Initial state: Check that a kashida array has been created + { + ParaPortionList& rParagraphPortionList = aEditEngine.GetParaPortions(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), rParagraphPortionList.Count()); + + EditLineList& rLines = rParagraphPortionList.getRef(0).GetLines(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), rLines.Count()); + EditLine const& rLine = rLines[0]; + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), rLine.GetStart()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(11), rLine.GetEnd()); + + std::vector const& rArray = rLine.GetKashidaArray(); + CPPUNIT_ASSERT_EQUAL(size_t(17), rArray.size()); + } + + // Resize the paper so the entire text fits on a single line + aEditEngine.SetPaperSize(Size(4000, 500)); + + // Follow-up state: Check that the kashida array has been cleared + { + ParaPortionList& rParagraphPortionList = aEditEngine.GetParaPortions(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), rParagraphPortionList.Count()); + + EditLineList& rLines = rParagraphPortionList.getRef(0).GetLines(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), rLines.Count()); + EditLine const& rLine = rLines[0]; + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), rLine.GetStart()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(17), rLine.GetEnd()); + + std::vector const& rArray = rLine.GetKashidaArray(); + + // Since there is no room for kashida, the kashida array should be empty. + // Without the bug fix, this will be 17: + CPPUNIT_ASSERT_EQUAL(size_t(0), rArray.size()); + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(Test); } diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 408964373c48..db3b63d6c99b 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -910,6 +910,9 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY ) // =>... pLine->GetCharPosArray().clear(); + // tdf#162803: Stale kashida position data also needs to be cleared on each layout. + pLine->GetKashidaArray().clear(); + sal_Int32 nTmpPos = nIndex; sal_Int32 nTmpPortion = pLine->GetStartPortion(); tools::Long nTmpWidth = 0; -- cgit