diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2024-01-24 12:17:41 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2024-01-24 22:21:04 +0100 |
commit | ce53519f025158f8f64a4e8603c8c6e0dc35473a (patch) | |
tree | f078474ed25361c812aa1b2f69dbc0f558695711 /editeng | |
parent | 6cad8d3a96c5425fe787765e5664463242958a78 (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.
Change-Id: Ib41529c66d9bda30cc4ed5faca4a99274ae594d7
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162449
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'editeng')
-rw-r--r-- | editeng/qa/unit/core-test.cxx | 60 | ||||
-rw-r--r-- | editeng/source/editeng/editeng.cxx | 7 | ||||
-rw-r--r-- | editeng/source/editeng/impedit2.cxx | 33 |
3 files changed, 99 insertions, 1 deletions
diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx index 097602ec6b4c..c70fd56a1d29 100644 --- a/editeng/qa/unit/core-test.cxx +++ b/editeng/qa/unit/core-test.cxx @@ -80,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(); @@ -128,6 +131,7 @@ public: CPPUNIT_TEST(testAutocorrect); CPPUNIT_TEST(testHyperlinkCopyPaste); CPPUNIT_TEST(testCopyPaste); + CPPUNIT_TEST(testHTMLPaste); CPPUNIT_TEST(testMultiParaSelCopyPaste); CPPUNIT_TEST(testTabsCopyPaste); CPPUNIT_TEST(testHyperlinkSearch); @@ -712,6 +716,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 26e8fe3099b4..d4bcfe337c86 100644 --- a/editeng/source/editeng/editeng.cxx +++ b/editeng/source/editeng/editeng.cxx @@ -2797,6 +2797,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 9733f9fa638c..0ff7c9c477c1 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -3958,7 +3958,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)) { @@ -3982,6 +3982,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 ) { |