summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2024-01-24 12:17:41 +0100
committerCaolán McNamara <caolan.mcnamara@collabora.com>2024-01-25 12:01:04 +0100
commit280084ceb61d4329e2f0f1a503601ef6e9156ca9 (patch)
tree5a8bb65f49bc57e4e3341334aa9e13aa1f46d883
parent2f2231d35a925daf8a54041e09846529add9e434 (diff)
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 57d7e73789dcc20a67da19766a27e71fddbdb991) Change-Id: Ib41529c66d9bda30cc4ed5faca4a99274ae594d7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162555 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Tested-by: Caolán McNamara <caolan.mcnamara@collabora.com> Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
-rw-r--r--editeng/qa/unit/core-test.cxx61
-rw-r--r--editeng/source/editeng/editeng.cxx7
-rw-r--r--editeng/source/editeng/impedit2.cxx33
3 files changed, 100 insertions, 1 deletions
diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx
index 6b3734d7c1cd..79a0673770d1 100644
--- a/editeng/qa/unit/core-test.cxx
+++ b/editeng/qa/unit/core-test.cxx
@@ -33,6 +33,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>
@@ -68,6 +69,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();
@@ -114,6 +118,7 @@ public:
CPPUNIT_TEST(testAutocorrect);
CPPUNIT_TEST(testHyperlinkCopyPaste);
CPPUNIT_TEST(testCopyPaste);
+ CPPUNIT_TEST(testHTMLPaste);
CPPUNIT_TEST(testMultiParaSelCopyPaste);
CPPUNIT_TEST(testTabsCopyPaste);
CPPUNIT_TEST(testHyperlinkSearch);
@@ -714,6 +719,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>\n<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 f3ebd8c4f806..e01a339af82f 100644
--- a/editeng/source/editeng/editeng.cxx
+++ b/editeng/source/editeng/editeng.cxx
@@ -2801,6 +2801,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 7d82d90707db..0984a5ecac63 100644
--- a/editeng/source/editeng/impedit2.cxx
+++ b/editeng/source/editeng/impedit2.cxx
@@ -3941,7 +3941,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)) {
@@ -3965,6 +3965,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);
+ OUString aExpectedPrefix = u"<!DOCTYPE html>";
+ 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 )
{