editeng/qa/unit/core-test.cxx                               |   61 ++++
 editeng/source/editeng/editeng.cxx                          |    7 
 editeng/source/editeng/impedit2.cxx                         |   33 ++
 sc/CppunitTest_sc_tiledrendering2.mk                        |   75 +++++
 sc/Module_sc.mk                                             |    1 
 sc/qa/unit/tiledrendering/tiledrendering.cxx                |   26 -
 sc/qa/unit/tiledrendering2/tiledrendering2.cxx              |  166 ++++++++++++
 sw/qa/core/layout/data/floattable-not-wrapped-by-table.docx |binary
 sw/qa/core/layout/tabfrm.cxx                                |   22 +
 sw/source/core/layout/tabfrm.cxx                            |   15 -
 10 files changed, 375 insertions(+), 31 deletions(-)

New commits:
commit 26a45d7809ca5a90e36a90ee14bf56d555adf278
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 23 15:34:07 2024 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Jan 25 08:34:21 2024 +0100

    tdf#159017 sw floattable: only shift to the right when needed
    
    Regression from commit 868140fcc1311259b9d5f666637b33d226511a53
    (tdf#60558 sw floattable: allow wrap of table on the right of a
    floattable, 2023-12-05), the document had an inline table, followed by a
    floating table, and we moved the inline table to the right even if the
    left hand side of the floating table already had enough space.
    
    What happens here is that nominally the inline table's original position
    overlaps, but because the table width is small enough, such an overlap
    doesn't actually happen. In this case, it's not needed to shift the
    inline table to the right.
    
    Fix the problem by making the check that decides whether it's necessary
    to increment the left margin of the table more strict: it's not enough
    to have enough space on the right of the fly, it's also needed to *not*
    have enough space on the left side.
    
    This keeps the original "floating table wrapped by inline table on the
    right" use-case working, but restores the ~no left margin for the inline
    table for the new bugdoc.
    
    (cherry picked from commit 5df7c66193d55af944b3269f11374e60da437c0b)
    
    Change-Id: Ifb30d90ca6dba7cc4a402d8a4445251120b575ae
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162464
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/core/layout/data/floattable-not-wrapped-by-table.docx 
b/sw/qa/core/layout/data/floattable-not-wrapped-by-table.docx
new file mode 100644
index 000000000000..2c255148d351
Binary files /dev/null and 
b/sw/qa/core/layout/data/floattable-not-wrapped-by-table.docx differ
diff --git a/sw/qa/core/layout/tabfrm.cxx b/sw/qa/core/layout/tabfrm.cxx
index 9357fc2df133..61b1a25109f5 100644
--- a/sw/qa/core/layout/tabfrm.cxx
+++ b/sw/qa/core/layout/tabfrm.cxx
@@ -199,6 +199,28 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWrappedByTable)
     // i.e. the inline table was under the floating one, not on the right of 
it.
     CPPUNIT_ASSERT_LESS(nFloatingBottom, nInlineTop);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testInlineTableThenSplitFly)
+{
+    // Given a document with a floating table ("right") and an inline table 
("left"):
+    // When laying out the document:
+    createSwDoc("floattable-not-wrapped-by-table.docx");
+
+    // Then make sure the inline table is on the left (small negative offset):
+    SwDoc* pDoc = getSwDoc();
+    SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+    auto pPage = pLayout->Lower()->DynCastPageFrame();
+    CPPUNIT_ASSERT(pPage);
+    SwFrame* pBody = pPage->FindBodyCont();
+    auto pTab = pBody->GetLower()->GetNext()->DynCastTabFrame();
+    SwTwips nInlineLeft = pTab->getFramePrintArea().Left();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected less than: 0
+    // - Actual  : 6958
+    // i.e. "left" was on the right, its horizontal margin was not a small 
negative value but a
+    // large positive one.
+    CPPUNIT_ASSERT_LESS(static_cast<SwTwips>(0), nInlineLeft);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index 033f692f47cc..f75782456181 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -3296,6 +3296,7 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
 
         bool bShiftDown = css::text::WrapTextMode_NONE == nSurround;
         bool bSplitFly = pFly->IsFlySplitAllowed();
+        const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
         if (!bShiftDown && bAddVerticalFlyOffsets)
         {
             if (nSurround == text::WrapTextMode_PARALLEL && 
isHoriOrientShiftDown)
@@ -3310,7 +3311,6 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
 
                 // Ignore spacing when determining the left/right edge of the 
fly, like
                 // Word does.
-                const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
                 basegfx::B1DRange 
aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
                                             
aRectFnSet.GetRight(aFlyRectWithoutSpaces));
 
@@ -3373,9 +3373,16 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
         bool bFlyHoriOrientLeft = text::HoriOrientation::LEFT == 
rHori.GetHoriOrient();
         if (bSplitFly && !bFlyHoriOrientLeft)
         {
-            // If a split fly is oriented "from left", we already checked if 
it has enough space on
-            // the right, so from-left and left means the same here.
-            bFlyHoriOrientLeft = rHori.GetHoriOrient() == 
text::HoriOrientation::NONE;
+            // Only shift to the right if we don't have enough space on the 
left.
+            SwTwips nTabWidth = getFramePrintArea().Width();
+            SwTwips nWidthDeadline = aFlyRectWithoutSpaces.Left()
+                                     - 
pFly->GetAnchorFrame()->GetUpper()->getFrameArea().Left();
+            if (nTabWidth > nWidthDeadline)
+            {
+                // If a split fly is oriented "from left", we already checked 
if it has enough space on
+                // the right, so from-left and left means the same here.
+                bFlyHoriOrientLeft = rHori.GetHoriOrient() == 
text::HoriOrientation::NONE;
+            }
         }
         if ((css::text::WrapTextMode_RIGHT == nSurround
              || css::text::WrapTextMode_PARALLEL == nSurround)
commit 1991b50908c5a53642e7ad086fd1319ec7710dd5
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 23 17:08:44 2024 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Jan 25 08:31:43 2024 +0100

    CppunitTest_sc_tiledrendering2: extract one test from the old, large suite
    
    Not a split, to avoid too many conflicts while cherry-picking, but at
    least create a new suite where next tests can be added without too much
    build time after source code edits.
    
    (cherry picked from commit a2763fe7b6f22e3e65d7e0db62b6ada967ccdd61)
    
    Change-Id: I494df6e23941845a433aab6ec5ecef040feab30e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162473
    Tested-by: Jenkins

diff --git a/sc/CppunitTest_sc_tiledrendering2.mk 
b/sc/CppunitTest_sc_tiledrendering2.mk
new file mode 100644
index 000000000000..d360780428a6
--- /dev/null
+++ b/sc/CppunitTest_sc_tiledrendering2.mk
@@ -0,0 +1,75 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sc_tiledrendering2))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sc_tiledrendering2))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sc_tiledrendering2, \
+    sc/qa/unit/tiledrendering2/tiledrendering2 \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sc_tiledrendering2, \
+    comphelper \
+    cppu \
+    cppuhelper \
+    editeng \
+    sal \
+    sfx \
+    sot \
+    svl \
+    svt \
+    svxcore \
+    sc \
+    scfilt \
+    scui \
+    subsequenttest \
+    test \
+    unotest \
+    $(call gb_Helper_optional,SCRIPTING, \
+        vbahelper) \
+    vcl \
+    tl \
+    utl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sc_tiledrendering2,\
+    boost_headers \
+    libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sc_tiledrendering2,\
+    -I$(SRCDIR)/sc/source/ui/inc \
+    -I$(SRCDIR)/sc/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sc_tiledrendering2))
+$(eval $(call gb_CppunitTest_use_api,sc_tiledrendering2,oovbaapi))
+
+$(eval $(call gb_CppunitTest_use_ure,sc_tiledrendering2))
+$(eval $(call gb_CppunitTest_use_vcl,sc_tiledrendering2))
+
+$(eval $(call gb_CppunitTest_use_rdb,sc_tiledrendering2,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sc_tiledrendering2))
+
+$(eval $(call gb_CppunitTest_add_arguments,sc_tiledrendering2, \
+    
-env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}"
 \
+))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sc_tiledrendering2, \
+    modules/scalc \
+    sfx \
+    svt \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk
index 0c2178b7fea1..74abee25c4b6 100644
--- a/sc/Module_sc.mk
+++ b/sc/Module_sc.mk
@@ -70,6 +70,7 @@ ifneq ($(DISABLE_GUI),TRUE)
 ifeq ($(OS),LINUX)
 $(eval $(call gb_Module_add_check_targets,sc,\
        CppunitTest_sc_tiledrendering \
+       CppunitTest_sc_tiledrendering2 \
 ))
 endif
 endif
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index e70cbfb93f1b..b7676f70761e 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -3594,32 +3594,6 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testStatusBarLocale)
     CPPUNIT_ASSERT_EQUAL(std::string("de-DE"), aLocale);
 }
 
-CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testSidebarLocale)
-{
-    ScModelObj* pModelObj = createDoc("chart.ods");
-    int nView1 = SfxLokHelper::getView();
-    ViewCallback aView1;
-    SfxViewShell* pView1 = SfxViewShell::Current();
-    pView1->SetLOKLocale("en-US");
-    SfxLokHelper::createView();
-    ViewCallback aView2;
-    SfxViewShell* pView2 = SfxViewShell::Current();
-    pView2->SetLOKLocale("de-DE");
-    TestLokCallbackWrapper::InitializeSidebar();
-    Scheduler::ProcessEventsToIdle();
-    aView2.m_aStateChanges.clear();
-
-    pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 
/*x=*/1,/*y=*/1,/*count=*/2, /*buttons=*/1, /*modifier=*/0);
-    pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/1, /*y=*/1, 
/*count=*/2, /*buttons=*/1, /*modifier=*/0);
-    SfxLokHelper::setView(nView1);
-    Scheduler::ProcessEventsToIdle();
-
-    auto it = aView2.m_aStateChanges.find(".uno:Sidebar");
-    CPPUNIT_ASSERT(it != aView2.m_aStateChanges.end());
-    std::string aLocale = it->second.get<std::string>("locale");
-    CPPUNIT_ASSERT_EQUAL(std::string("de-DE"), aLocale);
-}
-
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testLongFirstColumnMouseClick)
 {
     // Document has a long first column. We want to mouse-click on the column 
and
diff --git a/sc/qa/unit/tiledrendering/data/chart.ods 
b/sc/qa/unit/tiledrendering2/data/chart.ods
similarity index 100%
rename from sc/qa/unit/tiledrendering/data/chart.ods
rename to sc/qa/unit/tiledrendering2/data/chart.ods
diff --git a/sc/qa/unit/tiledrendering2/tiledrendering2.cxx 
b/sc/qa/unit/tiledrendering2/tiledrendering2.cxx
new file mode 100644
index 000000000000..058e7deb0883
--- /dev/null
+++ b/sc/qa/unit/tiledrendering2/tiledrendering2.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/unoapixml_test.hxx>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <test/lokcallback.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <docuno.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+class Test : public UnoApiXmlTest
+{
+public:
+    Test();
+    void setUp() override;
+    void tearDown() override;
+
+    ScModelObj* createDoc(const char* pName);
+};
+
+Test::Test()
+    : UnoApiXmlTest("/sc/qa/unit/tiledrendering2/data/")
+{
+}
+
+void Test::setUp()
+{
+    UnoApiXmlTest::setUp();
+
+    comphelper::LibreOfficeKit::setActive(true);
+}
+
+void Test::tearDown()
+{
+    if (mxComponent.is())
+    {
+        mxComponent->dispose();
+        mxComponent.clear();
+    }
+
+    comphelper::LibreOfficeKit::resetCompatFlag();
+
+    comphelper::LibreOfficeKit::setActive(false);
+
+    UnoApiXmlTest::tearDown();
+}
+
+ScModelObj* Test::createDoc(const char* pName)
+{
+    loadFromFile(OUString::createFromAscii(pName));
+
+    ScModelObj* pModelObj = 
comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
+    CPPUNIT_ASSERT(pModelObj);
+    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    return pModelObj;
+}
+
+/// A view callback tracks callbacks invoked on one specific view.
+class ViewCallback final
+{
+    SfxViewShell* mpViewShell;
+    int mnView;
+
+public:
+    std::map<std::string, boost::property_tree::ptree> m_aStateChanges;
+    TestLokCallbackWrapper m_callbackWrapper;
+
+    ViewCallback()
+        : m_callbackWrapper(&callback, this)
+    {
+        mpViewShell = SfxViewShell::Current();
+        mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+        mnView = SfxLokHelper::getView();
+        m_callbackWrapper.setLOKViewId(mnView);
+    }
+
+    ~ViewCallback()
+    {
+        if (mpViewShell)
+        {
+            SfxLokHelper::setView(mnView);
+            mpViewShell->setLibreOfficeKitViewCallback(nullptr);
+        }
+    }
+
+    static void callback(int nType, const char* pPayload, void* pData)
+    {
+        static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
+    }
+
+    void callbackImpl(int nType, const char* pPayload)
+    {
+        switch (nType)
+        {
+            case LOK_CALLBACK_STATE_CHANGED:
+            {
+                std::stringstream aStream(pPayload);
+                if (!aStream.str().starts_with("{"))
+                {
+                    break;
+                }
+
+                boost::property_tree::ptree aTree;
+                boost::property_tree::read_json(aStream, aTree);
+                auto it = aTree.find("commandName");
+                if (it == aTree.not_found())
+                {
+                    break;
+                }
+
+                std::string aCommandName = it->second.get_value<std::string>();
+                m_aStateChanges[aCommandName] = aTree;
+            }
+            break;
+        }
+    }
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testSidebarLocale)
+{
+    ScModelObj* pModelObj = createDoc("chart.ods");
+    int nView1 = SfxLokHelper::getView();
+    ViewCallback aView1;
+    SfxViewShell* pView1 = SfxViewShell::Current();
+    pView1->SetLOKLocale("en-US");
+    SfxLokHelper::createView();
+    ViewCallback aView2;
+    SfxViewShell* pView2 = SfxViewShell::Current();
+    pView2->SetLOKLocale("de-DE");
+    TestLokCallbackWrapper::InitializeSidebar();
+    Scheduler::ProcessEventsToIdle();
+    aView2.m_aStateChanges.clear();
+
+    pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/1, 
/*y=*/1, /*count=*/2,
+                              /*buttons=*/1, /*modifier=*/0);
+    pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/1, /*y=*/1, 
/*count=*/2,
+                              /*buttons=*/1, /*modifier=*/0);
+    SfxLokHelper::setView(nView1);
+    Scheduler::ProcessEventsToIdle();
+
+    auto it = aView2.m_aStateChanges.find(".uno:Sidebar");
+    CPPUNIT_ASSERT(it != aView2.m_aStateChanges.end());
+    std::string aLocale = it->second.get<std::string>("locale");
+    CPPUNIT_ASSERT_EQUAL(std::string("de-DE"), aLocale);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 57d7e73789dcc20a67da19766a27e71fddbdb991
Author:     Miklos Vajna <[email protected]>
AuthorDate: Wed Jan 24 12:17:41 2024 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Jan 25 08:31:09 2024 +0100

    cool#8023 editeng: support HTML paste
    
    editeng text (e.g. Writer shape text or Calc cell text edit) had working
    plain text and RTF paste, but HTML paste was not working.
    
    This is typically not noticed because desktop paste usually goes via
    RTF, but it can be visible when a LOK client just puts the best format
    on the clipboard, i.e. HTML is provided, but RTF is unavailable in the
    browser and plain text is also not written to the LOK clipboard.
    
    Fix the problem by connecting the existing ImpEditEngine::ReadHTML() to
    the generic ImpEditEngine::PasteText(): this already worked for plain
    text and RTF, but not for HTML.
    
    Note that "SIMPLE_HTML" was already supported, but that's not really
    HTML but some custom format that contains HTML, and it's claimed that MS
    IE 4.0 produced this.
    
    (cherry picked from commit ce53519f025158f8f64a4e8603c8c6e0dc35473a)
    
    Change-Id: Ib41529c66d9bda30cc4ed5faca4a99274ae594d7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162449
    Tested-by: Jenkins

diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx
index b5320f90e448..348bb031d13c 100644
--- a/editeng/qa/unit/core-test.cxx
+++ b/editeng/qa/unit/core-test.cxx
@@ -34,6 +34,7 @@
 #include <editeng/fhgtitem.hxx>
 
 #include <com/sun/star/text/textfield/Type.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
 
 #include <memory>
 #include <editeng/outliner.hxx>
@@ -79,6 +80,9 @@ public:
     /// Test Copy/Paste using Legacy Format
     void testCopyPaste();
 
+    /// Test Paste using HTML
+    void testHTMLPaste();
+
     /// Test Copy/Paste with selective selection over multiple paragraphs
     void testMultiParaSelCopyPaste();
 
@@ -125,6 +129,7 @@ public:
     CPPUNIT_TEST(testAutocorrect);
     CPPUNIT_TEST(testHyperlinkCopyPaste);
     CPPUNIT_TEST(testCopyPaste);
+    CPPUNIT_TEST(testHTMLPaste);
     CPPUNIT_TEST(testMultiParaSelCopyPaste);
     CPPUNIT_TEST(testTabsCopyPaste);
     CPPUNIT_TEST(testHyperlinkSearch);
@@ -708,6 +713,62 @@ void Test::testCopyPaste()
     CPPUNIT_ASSERT_EQUAL( OUString(aText + aText), 
rDoc.GetParaAsString(sal_Int32(0)) );
 }
 
+/// XTransferable implementation that provides simple HTML content.
+class TestHTMLTransferable : public 
cppu::WeakImplHelper<datatransfer::XTransferable>
+{
+public:
+    uno::Any SAL_CALL getTransferData(const datatransfer::DataFlavor& rFlavor) 
override;
+    uno::Sequence<datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() 
override;
+    sal_Bool SAL_CALL isDataFlavorSupported(const datatransfer::DataFlavor& 
rFlavor) override;
+};
+
+uno::Any TestHTMLTransferable::getTransferData(const datatransfer::DataFlavor& 
rFlavor)
+{
+    if (rFlavor.MimeType != "text/html")
+    {
+        return {};
+    }
+
+    uno::Any aRet;
+    SvMemoryStream aStream;
+    aStream.WriteOString("<!DOCTYPE html>
<html><body>test</body></html>");
+    aRet <<= uno::Sequence<sal_Int8>(static_cast<const 
sal_Int8*>(aStream.GetData()), aStream.GetSize());
+    return aRet;
+}
+
+uno::Sequence<datatransfer::DataFlavor> 
TestHTMLTransferable::getTransferDataFlavors()
+{
+    datatransfer::DataFlavor aFlavor;
+    aFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get();
+    aFlavor.MimeType = "text/html";
+    aFlavor.HumanPresentableName = aFlavor.MimeType;
+    return { aFlavor };
+}
+
+sal_Bool TestHTMLTransferable::isDataFlavorSupported(const 
datatransfer::DataFlavor& rFlavor)
+{
+    return rFlavor.MimeType == "text/html"
+           && rFlavor.DataType == 
cppu::UnoType<uno::Sequence<sal_Int8>>::get();
+}
+
+void Test::testHTMLPaste()
+{
+    // Given an empty editeng document:
+    EditEngine aEditEngine(mpItemPool.get());
+    EditDoc &rDoc = aEditEngine.GetEditDoc();
+    uno::Reference< datatransfer::XTransferable > xData(new 
TestHTMLTransferable);
+
+    // When trying to paste HTML:
+    aEditEngine.InsertText(xData, OUString(), rDoc.GetEndPaM(), true);
+
+    // Then make sure the text gets pasted:
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: test
+    // - Actual  :
+    // i.e. RTF and plain text paste worked, but not HTML.
+    CPPUNIT_ASSERT_EQUAL(OUString("test"), 
rDoc.GetParaAsString(static_cast<sal_Int32>(0)));
+}
+
 void Test::testMultiParaSelCopyPaste()
 {
     // Create EditEngine's instance
diff --git a/editeng/source/editeng/editeng.cxx 
b/editeng/source/editeng/editeng.cxx
index 4dbb93ce2c94..260e8dfb5038 100644
--- a/editeng/source/editeng/editeng.cxx
+++ b/editeng/source/editeng/editeng.cxx
@@ -2795,6 +2795,13 @@ bool EditEngine::HasValidData( const 
css::uno::Reference< css::datatransfer::XTr
         datatransfer::DataFlavor aFlavor;
         SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, 
aFlavor );
         bValidData = rTransferable->isDataFlavorSupported( aFlavor );
+
+        if (!bValidData)
+        {
+            // Allow HTML-only clipboard, i.e. without plain text.
+            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::HTML, 
aFlavor);
+            bValidData = rTransferable->isDataFlavorSupported(aFlavor);
+        }
     }
 
     return bValidData;
diff --git a/editeng/source/editeng/impedit2.cxx 
b/editeng/source/editeng/impedit2.cxx
index 4b8f0a63799a..7a5897196715 100644
--- a/editeng/source/editeng/impedit2.cxx
+++ b/editeng/source/editeng/impedit2.cxx
@@ -3939,7 +3939,7 @@ EditSelection ImpEditEngine::PasteText( uno::Reference< 
datatransfer::XTransfera
             }
         }
         if (!bDone) {
-            // HTML
+            // HTML_SIMPLE
             
SotExchange::GetFormatDataFlavor(SotClipboardFormatId::HTML_SIMPLE, aFlavor);
             bool bHtmlSupported = rxDataObj->isDataFlavorSupported(aFlavor);
             if (bHtmlSupported && (SotClipboardFormatId::NONE == format || 
SotClipboardFormatId::HTML_SIMPLE == format)) {
@@ -3963,6 +3963,37 @@ EditSelection ImpEditEngine::PasteText( uno::Reference< 
datatransfer::XTransfera
                 }
             }
         }
+
+        if (!bDone)
+        {
+            // HTML
+            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::HTML, 
aFlavor);
+            bool bHtmlSupported = rxDataObj->isDataFlavorSupported(aFlavor);
+            if (bHtmlSupported
+                && (format == SotClipboardFormatId::NONE || format == 
SotClipboardFormatId::HTML))
+            {
+                try
+                {
+                    uno::Any aData = rxDataObj->getTransferData(aFlavor);
+                    uno::Sequence<sal_Int8> aSeq;
+                    aData >>= aSeq;
+                    SvMemoryStream aHtmlStream(aSeq.getArray(), 
aSeq.getLength(), StreamMode::READ);
+                    static constexpr OUString aExpectedPrefix = u"<!DOCTYPE 
html>"_ustr;
+                    OUString aActualPrefix;
+                    aHtmlStream.ReadByteStringLine(aActualPrefix, 
RTL_TEXTENCODING_UTF8,
+                                                   
aExpectedPrefix.getLength());
+                    if (aActualPrefix == aExpectedPrefix)
+                    {
+                        aNewSelection = Read(aHtmlStream, rBaseURL, 
EETextFormat::Html, rPaM);
+                    }
+                    bDone = true;
+                }
+                catch (const css::uno::Exception&)
+                {
+                    TOOLS_WARN_EXCEPTION("editeng", "HTML paste failed");
+                }
+            }
+        }
     }
     if ( !bDone )
     {

Reply via email to