sw/qa/extras/htmlexport/htmlexport.cxx  |   42 +++++++++++++++++++++++++++++
 sw/source/filter/html/htmlnumwriter.cxx |   46 ++++++++++++++++++--------------
 2 files changed, 69 insertions(+), 19 deletions(-)

New commits:
commit 8c2607ae3ce143586e623532b8ae5288277ec3ac
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Feb 21 16:38:51 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Feb 21 19:14:41 2022 +0100

    sw HTML export, XHTML mode: fix lost </li> when last list item is not 
numbered
    
    This went wrong in 013a4f1f5c9ea5fb511568c53a7e76d1b365a65d (sw XHTML
    export: fix handling of list labels, 2021-05-13), where we started to
    assume that in case the last paragraph of a list is not numbered, then
    the entire list is not numbered. This lead to loosing the </li> for an
    <li> in case the list starts with a numbered paragraph, but ends with a
    non-numbered one.
    
    Fix the problem by (if necessary) looking back till the start of the
    list to see if any paragraph is numbered: that ensures that the <li> and
    </li> is always in sync.
    
    This fixes the new problem and keeps the old problem fixed at the same
    time.
    
    Change-Id: I7d347b37a40dcc727c2080bf8279e33c3ad147e9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130287
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index d83750951544..cf138cf56fd5 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1491,6 +1491,48 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testListHeading)
     assertXPathContent(pXmlDoc, 
"/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "list header");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testPartiallyNumberedList)
+{
+    // Given a document with a list, first para is numbered, second is not:
+    loadURL("private:factory/swriter", nullptr);
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->Insert("list header");
+    SwDoc* pDoc = pWrtShell->GetDoc();
+    sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+    SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+    {
+        SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+        SwTextNode& rTextNode = *rNode.GetTextNode();
+        rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+    }
+    pWrtShell->Insert2("numbered");
+    pWrtShell->SplitNode();
+    pWrtShell->Insert2("not numbered");
+    {
+        SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+        SwTextNode& rTextNode = *rNode.GetTextNode();
+        rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+        rTextNode.SetCountedInList(false);
+    }
+
+    // When exporting to ReqIF:
+    ExportToReqif();
+
+    // Then make sure the output is well-formed xhtml:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed:
+    // - expected: <li><p>...</p><p>...</p></li>
+    // - actual  : <li><p>...</p><p>...</p>
+    // because a <li> without a matching </li> is not well-formed, and the 
</li> was omitted because
+    // the second para was not numbered.
+    assertXPath(pXmlDoc,
+                
"/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
 2);
+}
+
 CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testBlockQuoteNoMargin)
 {
     // Given a document with some text, para style set to Quotations, no 
bottom margin:
diff --git a/sw/source/filter/html/htmlnumwriter.cxx 
b/sw/source/filter/html/htmlnumwriter.cxx
index 7df3402dda35..df15619cd85e 100644
--- a/sw/source/filter/html/htmlnumwriter.cxx
+++ b/sw/source/filter/html/htmlnumwriter.cxx
@@ -321,25 +321,10 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
     bool bSameRule = rNextInfo.GetNumRule() == rInfo.GetNumRule();
     bool bListEnd = !bSameRule || rNextInfo.GetDepth() < rInfo.GetDepth() || 
rNextInfo.IsRestart();
 
-    if (rWrt.mbXHTML)
-    {
-        // XHTML </li> for the list item content.
-        if ((bListEnd && rInfo.IsNumbered()) || (!bListEnd && 
rNextInfo.IsNumbered()))
-        {
-            HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(),
-                                       
OStringConcatenation(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li), false);
-        }
-    }
-
-    if (!bListEnd)
-    {
-        return rWrt;
-    }
-
+    std::optional<bool> oAtLeastOneNumbered;
     if (rWrt.mbXHTML && !rInfo.IsNumbered())
     {
-        // If the list only consisted of non-numbered text nodes, then don't 
end the list.
-        bool bAtLeastOneNumbered = false;
+        oAtLeastOneNumbered = false;
         SwNodeOffset nPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() - 
1;
         SwNumRule* pNumRule = nullptr;
         while (true)
@@ -360,13 +345,36 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
             pNumRule = pTextNode->GetNumRule();
             if (pTextNode->IsNumbered())
             {
-                bAtLeastOneNumbered = true;
+                oAtLeastOneNumbered = true;
                 break;
             }
             --nPos;
         }
+    }
 
-        if (!bAtLeastOneNumbered)
+    if (rWrt.mbXHTML)
+    {
+        // The list is numbered if the previous text node is numbered or any 
other previous text
+        // node is numbered.
+        bool bPrevIsNumbered = rInfo.IsNumbered() || *oAtLeastOneNumbered;
+        // XHTML </li> for the list item content, if there is an open <li>.
+        if ((bListEnd && bPrevIsNumbered) || (!bListEnd && 
rNextInfo.IsNumbered()))
+        {
+            HTMLOutFuncs::Out_AsciiTag(
+                rWrt.Strm(), OStringConcatenation(rWrt.GetNamespace() + 
OOO_STRING_SVTOOLS_HTML_li),
+                false);
+        }
+    }
+
+    if (!bListEnd)
+    {
+        return rWrt;
+    }
+
+    if (rWrt.mbXHTML && !rInfo.IsNumbered())
+    {
+        // If the list only consisted of non-numbered text nodes, then don't 
end the list.
+        if (!*oAtLeastOneNumbered)
         {
             return rWrt;
         }

Reply via email to