sw/qa/extras/htmlexport/data/clearing-break.html | 8 + sw/qa/extras/htmlexport/htmlexport.cxx | 43 ++++++++++ sw/source/filter/html/htmlatr.cxx | 2 sw/source/filter/html/htmlftn.cxx | 27 ++++++ sw/source/filter/html/swhtml.cxx | 97 +++++------------------ sw/source/filter/html/wrthtml.hxx | 1 6 files changed, 105 insertions(+), 73 deletions(-)
New commits: commit c4f7c4b884ab3f47ddebc04c4e31d36e0edcda88 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Mar 22 09:07:09 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Mar 31 08:25:05 2022 +0200 sw clearing breaks: add HTML filter Map between SwLineBreakClear and <br clear="...">. The import-time workaround with the anchor-only wrapping is no longer needed, the layout is now capable of doing this. (cherry picked from commit 66d1ea5915761f3550b39466db5d48eb723f0ccc) Conflicts: sw/qa/extras/htmlexport/htmlexport.cxx Change-Id: I53fa49f11e13fc3338b3cf70d8f87f3633c414c0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132294 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/extras/htmlexport/data/clearing-break.html b/sw/qa/extras/htmlexport/data/clearing-break.html new file mode 100644 index 000000000000..9586cb3af8e8 --- /dev/null +++ b/sw/qa/extras/htmlexport/data/clearing-break.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<body> +<p> +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABlElEQVR4nKWSQasBURiGX91zk5KykUKysPEjlFhIVqNmMSmjUfY2lC1FNrKj1C13pS4rFsL8BvMDpkRZWCnTRN3mOkej696x4a05PdM55/3ezvcRwzDwiggv2l5yIHQZfgyfusyL/NWAajR6gyzLSCQScLvdUFUVHo8HTqfTklMp7ZqAVm82VfT7fSyXS9jtdpzPZ0SjUWy3W2YqiuI/HgxklpzQypVKBdlsFn6/H61WC4fDAbVajf3HYjFMp9NLxZQFKyDdbpdFURQF+/0epVKJJTBFTebz+UMmi8XiZpDP51EulxEKhdgBU4FA4CHfHpFqMpmwz+v1QpIkVKtVOBwOtqfruiWTeDyO2Wx2M/H5fNA0DfV6HZFIBIIgYLPZYDweWzIpFot3Bp1OB8fjEblcDuv1GrvdjrVttVpZMuG4bzQaDdYJqna7DTreNKLL5WKtKxQKrDt/WRDe6SjzrJ/h8Bd6vR7boEomkwgGg8hkMpacTuv3k0iTcJx0IenXs+o4nT4t2RQxZ/pZXUbZsD19+6IfJMHZoeveMPgAAAAASUVORK5CYII=" name="Image1" align="left" width="68" height="68" border="0"/> +foo<br clear="all"/>bar</p> +</body> +</html> diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx index 7f64c835ce2d..9750555bf7f6 100644 --- a/sw/qa/extras/htmlexport/htmlexport.cxx +++ b/sw/qa/extras/htmlexport/htmlexport.cxx @@ -51,6 +51,7 @@ #include <paratr.hxx> #include <docsh.hxx> #include <unotxdoc.hxx> +#include <formatlinebreak.hxx> namespace { @@ -2119,6 +2120,48 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTrailingLineBreak) CPPUNIT_ASSERT_EQUAL(OUString("test\n"), aActual); } +CPPUNIT_TEST_FIXTURE(HtmlExportTest, testClearingBreak) +{ + auto verify = [this]() { + uno::Reference<container::XEnumerationAccess> xParagraph(getParagraph(1), uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xPortions = xParagraph->createEnumeration(); + uno::Reference<beans::XPropertySet> xPortion; + OUString aPortionType; + while (true) + { + // Ignore leading comments. + xPortion.set(xPortions->nextElement(), uno::UNO_QUERY); + xPortion->getPropertyValue("TextPortionType") >>= aPortionType; + if (aPortionType != "Annotation") + { + break; + } + } + // Skip "foo". + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.container.NoSuchElementException + // i.e. the first para was just comments + text portion, the clearing break was lost. + xPortion.set(xPortions->nextElement(), uno::UNO_QUERY); + xPortion->getPropertyValue("TextPortionType") >>= aPortionType; + CPPUNIT_ASSERT_EQUAL(OUString("LineBreak"), aPortionType); + uno::Reference<text::XTextContent> xLineBreak; + xPortion->getPropertyValue("LineBreak") >>= xLineBreak; + sal_Int16 eClear{}; + uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY); + xLineBreakProps->getPropertyValue("Clear") >>= eClear; + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(SwLineBreakClear::ALL), eClear); + }; + + // Given a document with an at-para anchored image + a clearing break: + // When loading that file: + load(mpTestDocumentPath, "clearing-break.html"); + // Then make sure that the clear property of the break is not ignored: + verify(); + reload(mpFilter, "clearing-break.html"); + // Make sure that that the clear property of the break is not ignored during export: + verify(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index 41e5fad5c193..b34eed5bd41b 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -3269,7 +3269,7 @@ SwAttrFnTab aHTMLAttrFnTab = { /* RES_TXTATR_FLYCNT */ OutHTML_SwFlyCnt, /* RES_TXTATR_FTN */ OutHTML_SwFormatFootnote, /* RES_TXTATR_ANNOTATION */ OutHTML_SwFormatField, -/* RES_TXTATR_LINEBREAK */ nullptr, +/* RES_TXTATR_LINEBREAK */ OutHTML_SwFormatLineBreak, /* RES_TXTATR_DUMMY1 */ nullptr, // Dummy: /* RES_TXTATR_DUMMY2 */ nullptr, // Dummy: diff --git a/sw/source/filter/html/htmlftn.cxx b/sw/source/filter/html/htmlftn.cxx index cfb6f59671c3..0020a4166eae 100644 --- a/sw/source/filter/html/htmlftn.cxx +++ b/sw/source/filter/html/htmlftn.cxx @@ -24,6 +24,7 @@ #include <osl/diagnose.h> #include <svtools/htmlout.hxx> #include <svtools/htmlkywd.hxx> +#include <svtools/HtmlWriter.hxx> #include <rtl/strbuf.hxx> #include <ndindex.hxx> #include <fmtftn.hxx> @@ -244,6 +245,32 @@ SwNodeIndex *SwHTMLParser::GetFootEndNoteSection( const OUString& rName ) return pStartNodeIdx; } +Writer& OutHTML_SwFormatLineBreak(Writer& rWrt, const SfxPoolItem& rHt) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + const auto& rLineBreak = static_cast<const SwFormatLineBreak&>(rHt); + + HtmlWriter aWriter(rHTMLWrt.Strm(), rHTMLWrt.maNamespace); + aWriter.start(OOO_STRING_SVTOOLS_HTML_linebreak); + switch (rLineBreak.GetValue()) + { + case SwLineBreakClear::NONE: + aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "none"); + break; + case SwLineBreakClear::LEFT: + aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "left"); + break; + case SwLineBreakClear::RIGHT: + aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "right"); + break; + case SwLineBreakClear::ALL: + aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "all"); + break; + } + aWriter.end(); + return rWrt; +} + Writer& OutHTML_SwFormatFootnote( Writer& rWrt, const SfxPoolItem& rHt ) { SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx index 5212159ae012..f1e929e34ba9 100644 --- a/sw/source/filter/html/swhtml.cxx +++ b/sw/source/filter/html/swhtml.cxx @@ -5116,21 +5116,8 @@ void SwHTMLParser::InsertIDOption() void SwHTMLParser::InsertLineBreak() { - // <BR CLEAR=xxx> is handled as: - // 1.) Only regard the paragraph-bound frames anchored in current paragraph. - // 2.) For left-justified aligned frames, CLEAR=LEFT or ALL, and for right- - // justified aligned frames, CLEAR=RIGHT or ALL, the wrap-through is - // changed as following: - // 3.) If the paragraph contains no text, then the frames don't get a wrapping - // 4.) otherwise a left aligned frame gets a right "only anchor" wrapping - // and a right aligned frame gets a left "only anchor" wrapping. - // 5.) if in a non-empty paragraph the wrapping of a frame is changed, - // then a new paragraph is opened - // 6.) If no wrappings of frames are changed, a hard line break is inserted. - OUString aId, aStyle, aClass; // the id of bookmark - bool bClearLeft = false, bClearRight = false; - bool bCleared = false; // Was a CLEAR executed? + SwLineBreakClear eClear = SwLineBreakClear::NONE; // then we fetch the options const HTMLOptions& rHTMLOptions = GetOptions(); @@ -5144,13 +5131,16 @@ void SwHTMLParser::InsertLineBreak() const OUString &rClear = rOption.GetString(); if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_all ) ) { - bClearLeft = true; - bClearRight = true; + eClear = SwLineBreakClear::ALL; } else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) ) - bClearLeft = true; + { + eClear = SwLineBreakClear::LEFT; + } else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) ) - bClearRight = true; + { + eClear = SwLineBreakClear::LEFT; + } } break; case HtmlOptionId::ID: @@ -5166,57 +5156,6 @@ void SwHTMLParser::InsertLineBreak() } } - // CLEAR is only supported for the current paragraph - if( bClearLeft || bClearRight ) - { - SwNodeIndex& rNodeIdx = m_pPam->GetPoint()->nNode; - SwTextNode* pTextNd = rNodeIdx.GetNode().GetTextNode(); - if( pTextNd ) - { - const SwFrameFormats& rFrameFormatTable = *m_xDoc->GetSpzFrameFormats(); - - for( size_t i=0; i<rFrameFormatTable.size(); i++ ) - { - SwFrameFormat *const pFormat = rFrameFormatTable[i]; - SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); - SwPosition const*const pAPos = pAnchor->GetContentAnchor(); - if (pAPos && - ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) || - (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) && - pAPos->nNode == rNodeIdx && - pFormat->GetSurround().GetSurround() != css::text::WrapTextMode_NONE ) - { - sal_Int16 eHori = RES_DRAWFRMFMT == pFormat->Which() - ? text::HoriOrientation::LEFT - : pFormat->GetHoriOrient().GetHoriOrient(); - - css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL; - if( m_pPam->GetPoint()->nContent.GetIndex() ) - { - if( bClearLeft && text::HoriOrientation::LEFT==eHori ) - eSurround = css::text::WrapTextMode_RIGHT; - else if( bClearRight && text::HoriOrientation::RIGHT==eHori ) - eSurround = css::text::WrapTextMode_LEFT; - } - else if( (bClearLeft && text::HoriOrientation::LEFT==eHori) || - (bClearRight && text::HoriOrientation::RIGHT==eHori) ) - { - eSurround = css::text::WrapTextMode_NONE; - } - - if( css::text::WrapTextMode_PARALLEL != eSurround ) - { - SwFormatSurround aSurround( eSurround ); - if( css::text::WrapTextMode_NONE != eSurround ) - aSurround.SetAnchorOnly( true ); - pFormat->SetFormatAttr( aSurround ); - bCleared = true; - } - } - } - } - } - // parse styles std::shared_ptr<SvxFormatBreakItem> aBreakItem(std::make_shared<SvxFormatBreakItem>(SvxBreak::NONE, RES_BREAK)); bool bBreakItem = false; @@ -5243,10 +5182,24 @@ void SwHTMLParser::InsertLineBreak() EndAttr( m_xAttrTab->pBreak, false ); } - if( !bCleared && !bBreakItem ) + if (!bBreakItem) { - // If no CLEAR could or should be executed, a line break will be inserted - m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, "\x0A" ); + if (eClear == SwLineBreakClear::NONE) + { + // If no CLEAR could or should be executed, a line break will be inserted + m_xDoc->getIDocumentContentOperations().InsertString(*m_pPam, "\x0A"); + } + else + { + // <BR CLEAR=xxx> is mapped an SwFormatLineBreak. + SwTextNode* pTextNode = m_pPam->GetNode().GetTextNode(); + if (pTextNode) + { + SwFormatLineBreak aLineBreak(eClear); + sal_Int32 nPos = m_pPam->GetPoint()->nContent.GetIndex(); + pTextNode->InsertItem(aLineBreak, nPos, nPos); + } + } } else if( m_pPam->GetPoint()->nContent.GetIndex() ) { diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx index bf6806b1ae95..e2821b492762 100644 --- a/sw/source/filter/html/wrthtml.hxx +++ b/sw/source/filter/html/wrthtml.hxx @@ -685,6 +685,7 @@ Writer& OutHTML_BulletImage( Writer& rWrt, const char *pTag, Writer& OutHTML_SwFormatField( Writer& rWrt, const SfxPoolItem& rHt ); Writer& OutHTML_SwFormatFootnote( Writer& rWrt, const SfxPoolItem& rHt ); +Writer& OutHTML_SwFormatLineBreak(Writer& rWrt, const SfxPoolItem& rHt); Writer& OutHTML_INetFormat( Writer&, const SwFormatINetFormat& rINetFormat, bool bOn ); Writer& OutCSS1_BodyTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet );