editeng/qa/unit/core-test.cxx       |   61 ++++++++++++++++++++++++++++++++++++
 editeng/source/editeng/editeng.cxx  |    7 ++++
 editeng/source/editeng/impedit2.cxx |   33 ++++++++++++++++++-
 3 files changed, 100 insertions(+), 1 deletion(-)

New commits:
commit 0bb20afe3590838ba8aef204033ac95a047ba710
Author:     Miklos Vajna <[email protected]>
AuthorDate: Wed Jan 24 12:17:41 2024 +0100
Commit:     Xisco Fauli <[email protected]>
CommitDate: Fri Feb 2 12:58:22 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/+/162547
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <[email protected]>

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