sw/qa/core/uwriter.cxx         |  126 +++++++++++++++++++++++++++++++++++++++++
 sw/source/core/doc/docruby.cxx |   13 +++-
 2 files changed, 136 insertions(+), 3 deletions(-)

New commits:
commit 63fbb71f362b16eaa7b7d8617ae3b7e482b41492
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Thu Aug 15 15:18:15 2024 -0600
Commit:     Jonathan Clark <jonat...@libreoffice.org>
CommitDate: Fri Aug 16 05:02:08 2024 +0200

    tdf#141466 sw: Fix incorrect output after editing Ruby base text
    
    Previously, the Asian Phonetic Guide dialog could scramble base text
    during editing due to incorrect handling of PaM after calling
    ReplaceRange(). This issue has been fixed. The implementation has also
    been modified to allow base text deletion.
    
    Change-Id: I43350272359c7984459aea1604dae0d3f6428cac
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171934
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>

diff --git a/sw/qa/core/uwriter.cxx b/sw/qa/core/uwriter.cxx
index 861b8b3d272e..3504a11130df 100644
--- a/sw/qa/core/uwriter.cxx
+++ b/sw/qa/core/uwriter.cxx
@@ -72,6 +72,8 @@
 #include <pagedesc.hxx>
 #include <calc.hxx>
 #include <scriptinfo.hxx>
+#include <rubylist.hxx>
+#include <txatbase.hxx>
 
 #include <tblafmt.hxx>
 #include <unotbl.hxx>
@@ -130,6 +132,7 @@ public:
     void testTdf92308();
     void testTableCellComparison();
     void testTdf156211();
+    void testSetRubyList();
 
     CPPUNIT_TEST_SUITE(SwDocTest);
 
@@ -168,6 +171,7 @@ public:
     CPPUNIT_TEST(testTdf92308);
     CPPUNIT_TEST(testTableCellComparison);
     CPPUNIT_TEST(testTdf156211);
+    CPPUNIT_TEST(testSetRubyList);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -1981,6 +1985,128 @@ void SwDocTest::testTdf156211()
     CPPUNIT_ASSERT(!oSI.IsKashidaLine(TextFrameIndex{ 95 }));
 }
 
+void SwDocTest::testSetRubyList()
+{
+    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
+    SwPaM aPaM(aIdx);
+
+    SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
+    CPPUNIT_ASSERT(pTextNode);
+
+    auto& rOps = m_pDoc->getIDocumentContentOperations();
+
+    auto fnAppendJapanese = [&](const OUString& rText)
+    {
+        rOps.AppendTextNode(*aPaM.GetPoint());
+
+        SvxLanguageItem aCJKLangItem(LANGUAGE_JAPANESE, 
RES_CHRATR_CJK_LANGUAGE);
+        SvxLanguageItem aWestLangItem(LANGUAGE_ENGLISH_US, 
RES_CHRATR_LANGUAGE);
+        rOps.InsertPoolItem(aPaM, aCJKLangItem);
+        rOps.InsertPoolItem(aPaM, aWestLangItem);
+
+        rOps.InsertString(aPaM, rText);
+
+        aPaM.SetMark();
+        aPaM.GetPoint()->nContent = 0;
+
+        CPPUNIT_ASSERT_EQUAL(rText, aPaM.GetText());
+    };
+
+    auto fnAppendRuby = [](SwRubyList* rList, OUString aBase, OUString aRuby)
+    {
+        auto pEnt = std::make_unique<SwRubyListEntry>();
+        pEnt->SetText(std::move(aBase));
+        pEnt->SetRubyAttr(SwFormatRuby{ std::move(aRuby) });
+
+        rList->push_back(std::move(pEnt));
+    };
+
+    auto fnGetCombinedString = [&]
+    {
+        OUStringBuffer aTemp;
+
+        auto* pPos = aPaM.GetPoint();
+        const auto* pTNd = pPos->GetNode().GetTextNode();
+        const auto& rText = pTNd->GetText();
+        const auto* pHts = pTNd->GetpSwpHints();
+
+        for (sal_Int32 i = 0; i < rText.getLength(); ++i)
+        {
+            aTemp.append(rText[i]);
+
+            if (pHts)
+            {
+                for (size_t j = 0; j < pHts->Count(); ++j)
+                {
+                    const auto* pHt = pHts->Get(j);
+                    if (pHt->Which() == RES_TXTATR_CJK_RUBY && 
pHt->GetAnyEnd() == (i + 1))
+                    {
+                        aTemp.append(u"["_ustr + pHt->GetRuby().GetText() + 
u"]"_ustr);
+                    }
+                }
+            }
+        }
+
+        return aTemp.toString();
+    };
+
+    // Trivial characteristic test
+    {
+        fnAppendJapanese(u"学校"_ustr);
+
+        SwRubyList rList;
+        fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr);
+
+        m_pDoc->SetRubyList(aPaM, rList);
+
+        CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]"_ustr, fnGetCombinedString());
+    }
+
+    // tdf#141466: Characteristic test from bug
+    {
+        fnAppendJapanese(u"学校に行きます。"_ustr);
+
+        SwRubyList rList;
+        fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr);
+        fnAppendRuby(&rList, u"に"_ustr, u""_ustr);
+        fnAppendRuby(&rList, u"行"_ustr, u"い"_ustr);
+        fnAppendRuby(&rList, u"きます"_ustr, u""_ustr);
+        fnAppendRuby(&rList, u"。"_ustr, u""_ustr);
+
+        m_pDoc->SetRubyList(aPaM, rList);
+
+        CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に行[い]きます。"_ustr, fnGetCombinedString());
+    }
+
+    // Base text deletion
+    {
+        fnAppendJapanese(u"学校に行きます。"_ustr);
+
+        SwRubyList rList;
+        fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr);
+        fnAppendRuby(&rList, u"に"_ustr, u""_ustr);
+        fnAppendRuby(&rList, u"行"_ustr, u"い"_ustr);
+        fnAppendRuby(&rList, u"きます"_ustr, u""_ustr);
+        fnAppendRuby(&rList, u"。"_ustr, u""_ustr);
+
+        m_pDoc->SetRubyList(aPaM, rList);
+
+        CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に行[い]きます。"_ustr, fnGetCombinedString());
+
+        SwRubyList rList2;
+        fnAppendRuby(&rList2, u"学校"_ustr, u"がっこう"_ustr);
+        fnAppendRuby(&rList2, u"に"_ustr, u""_ustr);
+        fnAppendRuby(&rList2, u""_ustr, u"い"_ustr);
+        fnAppendRuby(&rList2, u"来"_ustr, u"き"_ustr);
+        fnAppendRuby(&rList2, u"ます"_ustr, u""_ustr);
+        fnAppendRuby(&rList2, u"。"_ustr, u""_ustr);
+
+        m_pDoc->SetRubyList(aPaM, rList2);
+
+        CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に来[き]ます。"_ustr, fnGetCombinedString());
+    }
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SwDocTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/doc/docruby.cxx b/sw/source/core/doc/docruby.cxx
index 1a1b84ffd7f4..90f4e771d993 100644
--- a/sw/source/core/doc/docruby.cxx
+++ b/sw/source/core/doc/docruby.cxx
@@ -127,12 +127,19 @@ void SwDoc::SetRubyList( const SwPaM& rPam, const 
SwRubyList& rList )
                         }
                     }
 
-                    if( !pEntry->GetText().isEmpty() &&
-                        aCheckEntry.GetText() != pEntry->GetText() )
+                    if (aCheckEntry.GetText() != pEntry->GetText())
                     {
+                        if (pEntry->GetText().isEmpty())
+                        {
+                            ResetAttrs(aPam, true, aDelArr);
+                        }
+
                         // text is changed, so replace the original
-                        getIDocumentContentOperations().ReplaceRange( aPam, 
pEntry->GetText(), false );
+                        getIDocumentContentOperations().ReplaceRange(aPam, 
pEntry->GetText(),
+                                                                     false);
+                        std::swap(*aPam.GetMark(), *aPam.GetPoint());
                     }
+
                     aPam.DeleteMark();
                 }
                 else

Reply via email to