include/svtools/htmlkywd.hxx | 1 include/svtools/htmltokn.h | 1 include/svtools/parhtml.hxx | 4 + svtools/source/svhtml/htmlkywd.cxx | 1 svtools/source/svhtml/parhtml.cxx | 59 ++++++++++--------- sw/qa/extras/htmlexport/htmlexport.cxx | 66 +++++++++++++++++++++ sw/source/filter/html/css1atr.cxx | 20 +++++- sw/source/filter/html/css1kywd.cxx | 2 sw/source/filter/html/css1kywd.hxx | 2 sw/source/filter/html/htmlatr.cxx | 97 ++++++++++++++++++++++---------- sw/source/filter/html/htmlfldw.cxx | 4 - sw/source/filter/html/htmlflywriter.cxx | 28 ++++----- sw/source/filter/html/htmlforw.cxx | 16 ++--- sw/source/filter/html/htmlftn.cxx | 8 +- sw/source/filter/html/htmlnumwriter.cxx | 4 - sw/source/filter/html/htmlplug.cxx | 6 - sw/source/filter/html/htmltabw.cxx | 18 ++--- sw/source/filter/html/svxcss1.cxx | 14 ++++ sw/source/filter/html/svxcss1.hxx | 1 sw/source/filter/html/swhtml.cxx | 9 ++ sw/source/filter/html/wrthtml.cxx | 24 +++++-- sw/source/filter/html/wrthtml.hxx | 15 +++- 22 files changed, 289 insertions(+), 111 deletions(-)
New commits: commit 2e3f3bcf3f243918d79aa97545bcd30627b458a4 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Mon Oct 23 19:52:14 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Tue Oct 24 08:42:16 2023 +0300 Implement PreserveSpaces boolean HTML/ReqIF export filter option This option changes how HTML/ReqIF export handles paragraphs with leading/trailing spaces, or multiple sequential spaces. Normally export may insert newlines every ~256 characters, in place of normal space characters; this relies on default processing of spaces, where leading/trailing spaces are trimmed, and runs of spaces are reduced to a single space. When PreserveSpaces is true, HTML/ReqIF export takes care to not alter spaces inside paragraphs. For that, it checks if paragraphs contain sequences of spaces that normally would be reduced; and for those paragraphs, it adds "white-space: pre-wrap" to style (in HTML), or 'xml::space="preserve"' attribute (in ReqIF). Import of 'xml::space' attribute and "white-space: pre-wrap" style is implemented; when paragraph has these, it keeps the spaces read from HTML/ReqIF intact. Import does not currently support this attribute/style in elements other than 'p'. Change-Id: I62dba5eaf313b965bf37d8fa5e3f5bbb8f5e8357 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158362 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/include/svtools/htmlkywd.hxx b/include/svtools/htmlkywd.hxx index 9a84cddd37bf..6f06c74f3ee0 100644 --- a/include/svtools/htmlkywd.hxx +++ b/include/svtools/htmlkywd.hxx @@ -522,6 +522,7 @@ #define OOO_STRING_SVTOOLS_HTML_O_valign "valign" #define OOO_STRING_SVTOOLS_HTML_O_valuetype "valuetype" #define OOO_STRING_SVTOOLS_HTML_O_wrap "wrap" +#define OOO_STRING_SVTOOLS_XHTML_O_xml_space "xml:space" // attributes with script code as value #define OOO_STRING_SVTOOLS_HTML_O_onblur "onblur" diff --git a/include/svtools/htmltokn.h b/include/svtools/htmltokn.h index 9dca8a8f3ea7..4a333ee2f6d9 100644 --- a/include/svtools/htmltokn.h +++ b/include/svtools/htmltokn.h @@ -429,6 +429,7 @@ ENUM_START = NUMBER_END, VALIGN, VALUETYPE, WRAP, + XML_SPACE, ENUM_END, // attributes with script code as value diff --git a/include/svtools/parhtml.hxx b/include/svtools/parhtml.hxx index b4fee63e311a..a6a15b93e3c1 100644 --- a/include/svtools/parhtml.hxx +++ b/include/svtools/parhtml.hxx @@ -162,6 +162,8 @@ private: bool bReadNextChar : 1; // true: read NextChar again(JavaScript!) bool bReadComment : 1; // true: read NextChar again (JavaScript!) + bool m_bPreserveSpaces = false; + sal_uInt32 nPre_LinePos; // Pos in the line in the PRE-Tag HtmlTokenId mnPendingOffToken; ///< OFF token pending for a <XX.../> ON/OFF ON token @@ -187,6 +189,8 @@ protected: void SetNamespace(std::u16string_view rNamespace); + void SetPreserveSpaces(bool val) { m_bPreserveSpaces = val; } + public: HTMLParser( SvStream& rIn, bool bReadNewDoc = true ); diff --git a/svtools/source/svhtml/htmlkywd.cxx b/svtools/source/svhtml/htmlkywd.cxx index 584322fac8bc..b4c0492fbec3 100644 --- a/svtools/source/svhtml/htmlkywd.cxx +++ b/svtools/source/svhtml/htmlkywd.cxx @@ -598,6 +598,7 @@ static HTML_OptionEntry aHTMLOptionTab[] = { {std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_O_valign), HtmlOptionId::VALIGN}, {std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_O_valuetype), HtmlOptionId::VALUETYPE}, {std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_O_wrap), HtmlOptionId::WRAP}, + {std::u16string_view(u"" OOO_STRING_SVTOOLS_XHTML_O_xml_space), HtmlOptionId::XML_SPACE}, // Attributes with script code value {std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_O_onblur), HtmlOptionId::ONBLUR}, // JavaScript diff --git a/svtools/source/svhtml/parhtml.cxx b/svtools/source/svhtml/parhtml.cxx index e705c98013e4..16313f226a54 100644 --- a/svtools/source/svhtml/parhtml.cxx +++ b/svtools/source/svhtml/parhtml.cxx @@ -377,9 +377,14 @@ namespace { constexpr bool HTML_ISPRINTABLE(sal_Unicode c) { return c >= 32 && c != 127; } +constexpr bool HTML_ISSPACE(sal_uInt32 c) +{ + return ' ' == c || '\t' == c || '\r' == c || '\n' == c || '\x0b' == c; +} + } -HtmlTokenId HTMLParser::ScanText( const sal_Unicode cBreak ) +HtmlTokenId HTMLParser::ScanText(const sal_Unicode cBreak) { OUStringBuffer sTmpBuffer( MAX_LEN ); bool bContinue = true; @@ -705,37 +710,39 @@ HtmlTokenId HTMLParser::ScanText( const sal_Unicode cBreak ) { break; } - nNextCh = ' '; + if (!m_bPreserveSpaces) + nNextCh = ' '; [[fallthrough]]; case ' ': - sTmpBuffer.appendUtf32( nNextCh ); - if( '>'!=cBreak && (!bReadListing && !bReadXMP && - !bReadPRE && !bReadTextArea) ) + if (!m_bPreserveSpaces) { - // Reduce sequences of Blanks/Tabs/CR/LF to a single blank - do { - nNextCh = GetNextChar(); - if( sal_Unicode(EOF) == nNextCh && rInput.eof() ) + sTmpBuffer.appendUtf32(nNextCh); + if ('>' != cBreak && (!bReadListing && !bReadXMP && !bReadPRE && !bReadTextArea)) + { + // Reduce sequences of Blanks/Tabs/CR/LF to a single blank + do { - if( !aToken.isEmpty() || sTmpBuffer.getLength() > 1 ) + nNextCh = GetNextChar(); + if (sal_Unicode(EOF) == nNextCh && rInput.eof()) { - // Have seen s.th. aside from blanks? - aToken.append( sTmpBuffer ); - sTmpBuffer.setLength(0); - return HtmlTokenId::TEXTTOKEN; + if (!aToken.isEmpty() || sTmpBuffer.getLength() > 1) + { + // Have seen s.th. aside from blanks? + aToken.append(sTmpBuffer); + sTmpBuffer.setLength(0); + return HtmlTokenId::TEXTTOKEN; + } + else + // Only read blanks: no text must be returned + // and GetNextToken_ has to read until EOF + return HtmlTokenId::NONE; } - else - // Only read blanks: no text must be returned - // and GetNextToken_ has to read until EOF - return HtmlTokenId::NONE; - } - } while ( ' ' == nNextCh || '\t' == nNextCh || - '\r' == nNextCh || '\n' == nNextCh || - '\x0b' == nNextCh ); - bNextCh = false; + } while (HTML_ISSPACE(nNextCh)); + bNextCh = false; + } + break; } - break; - + [[fallthrough]]; default: bEqSignFound = false; if (nNextCh == cBreak && !cQuote) @@ -743,7 +750,7 @@ HtmlTokenId HTMLParser::ScanText( const sal_Unicode cBreak ) else { do { - if (!linguistic::IsControlChar(nNextCh)) + if (!linguistic::IsControlChar(nNextCh) || HTML_ISSPACE(nNextCh)) { // All remaining characters make their way into the text. sTmpBuffer.appendUtf32( nNextCh ); diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx index 59997f03ae7e..7c74a27161ee 100644 --- a/sw/qa/extras/htmlexport/htmlexport.cxx +++ b/sw/qa/extras/htmlexport/htmlexport.cxx @@ -2764,6 +2764,72 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf157643_WideHBorder) assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2); } +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_PreserveSpaces) +{ + // Given a document with leading, trailing, and repeating intermediate spaces: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + const OUString paraText = u"\t test \t more text \t"; + pWrtShell->Insert(paraText); + + // When exporting to plain HTML, using PreserveSpaces: + uno::Reference<css::frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY_THROW); + css::uno::Sequence<css::beans::PropertyValue> aStoreProperties = { + comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")), + comphelper::makePropertyValue("PreserveSpaces", true), + }; + xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties); + + // Then make sure that "white-space: pre-wrap" is written into the paragraph's style: + htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile); + CPPUNIT_ASSERT(pHtmlDoc); + const OUString style = getXPath(pHtmlDoc, "/html/body/p", "style"); + CPPUNIT_ASSERT(style.indexOf("white-space: pre-wrap") >= 0); + // Also check that the paragraph text is correct, without modifications in whitespace + assertXPathContent(pHtmlDoc, "/html/body/p", paraText); + + // Test import + + setImportFilterName("HTML (StarWriter)"); + UnoApiTest::load(maTempFile.GetURL()); + CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString()); +} + +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_PreserveSpaces) +{ + // Given a document with leading, trailing, and repeating intermediate spaces: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + const OUString paraText = u"\t test \t more text \t"; + pWrtShell->Insert(paraText); + + // When exporting to ReqIF, using PreserveSpaces: + uno::Reference<css::frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY_THROW); + css::uno::Sequence<css::beans::PropertyValue> aStoreProperties = { + comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")), + comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")), + comphelper::makePropertyValue("PreserveSpaces", true), + }; + xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties); + + // Then make sure that xml:space="preserve" attribute exists in the paragraph element: + SvMemoryStream aStream; + WrapReqifFromTempFile(aStream); + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "space", u"preserve"); + // Also check that the paragraph text is correct, without modifications in whitespace + assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", paraText); + + // Test import + + setImportFilterOptions("xhtmlns=reqif-xhtml"); + setImportFilterName("HTML (StarWriter)"); + UnoApiTest::load(maTempFile.GetURL()); + CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx index c435cc66b050..27d0c645ad56 100644 --- a/sw/source/filter/html/css1atr.cxx +++ b/sw/source/filter/html/css1atr.cxx @@ -1791,13 +1791,13 @@ Writer& OutCSS1_BodyTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ) return rWrt; } -Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ) +Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet, std::string_view rAdd ) { SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); SwCSS1OutMode aMode( rHTMLWrt, rHTMLWrt.m_nCSS1Script|CSS1_OUTMODE_STYLE_OPT | CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr ); - rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false, rAdd ); return rWrt; } @@ -3599,7 +3599,7 @@ SwAttrFnTab const aCSS1AttrFnTab = { static_assert(SAL_N_ELEMENTS(aCSS1AttrFnTab) == RES_BOXATR_END); void SwHTMLWriter::OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, - bool bDeep ) + bool bDeep, std::string_view rAdd ) { // print ItemSet, including all attributes Out_SfxItemSet( aCSS1AttrFnTab, *this, rItemSet, bDeep ); @@ -3631,6 +3631,20 @@ void SwHTMLWriter::OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, bDeep ); } + if (!rAdd.empty()) + { + for (std::size_t index = 0; index != std::string_view::npos;) + { + OString attr(o3tl::trim(o3tl::getToken(rAdd, ':', index))); + assert(!attr.isEmpty()); + assert(index != std::string_view::npos); + + std::string_view val = o3tl::trim(o3tl::getToken(rAdd, ':', index)); + assert(!val.empty()); + OutCSS1_PropertyAscii(attr.getStr(), val); + } + } + if( m_bFirstCSS1Property ) return; diff --git a/sw/source/filter/html/css1kywd.cxx b/sw/source/filter/html/css1kywd.cxx index 12d70903ad39..56285cfe890e 100644 --- a/sw/source/filter/html/css1kywd.cxx +++ b/sw/source/filter/html/css1kywd.cxx @@ -212,4 +212,6 @@ const char* const sCSS1_PV_inherit = "inherit"; const char* const sCSS1_P_display = "display"; +const char* const sCSS1_P_white_space = "white-space"; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1kywd.hxx b/sw/source/filter/html/css1kywd.hxx index 67d2c9e2802c..7e70961069bd 100644 --- a/sw/source/filter/html/css1kywd.hxx +++ b/sw/source/filter/html/css1kywd.hxx @@ -212,6 +212,8 @@ extern const char* const sCSS1_PV_inherit; extern const char* const sCSS1_P_display; +extern const char* const sCSS1_P_white_space; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index fdf53cb9f84a..114d6de16b3a 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -133,12 +133,12 @@ void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl ) // write according to the level difference for( sal_uInt16 i=m_nDefListLvl; i<nNewLvl; i++ ) { - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist) ); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd) ); IncIndentLevel(); - m_bLFPossible = true; + SetLFPossible(true); } } else if( m_nDefListLvl > nNewLvl ) @@ -146,11 +146,11 @@ void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl ) for( sal_uInt16 i=nNewLvl ; i < m_nDefListLvl; i++ ) { DecIndentLevel(); - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd), false ); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist), false ); - m_bLFPossible = true; + SetLFPossible(true); } } @@ -162,7 +162,7 @@ void SwHTMLWriter::ChangeParaToken( HtmlTokenId nNew ) if( nNew != m_nLastParaToken && HtmlTokenId::PREFORMTXT_ON == m_nLastParaToken ) { HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_preformtxt), false ); - m_bLFPossible = true; + SetLFPossible(true); } m_nLastParaToken = nNew; } @@ -744,7 +744,7 @@ static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat, (rHWrt.m_nDefListLvl-1) * rHWrt.m_nDefListMargin; } - if( rHWrt.m_bLFPossible && !rHWrt.m_bFirstLine ) + if (rHWrt.IsLFPossible() && !rHWrt.m_bFirstLine) rHWrt.OutNewLine(); // paragraph tag on a new line rInfo.bOutPara = false; @@ -805,7 +805,7 @@ static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat, rHWrt.m_bNoAlign = false; rInfo.bOutDiv = true; rHWrt.IncIndentLevel(); - rHWrt.m_bLFPossible = true; + rHWrt.SetLFPossible(true); rHWrt.OutNewLine(); } @@ -919,6 +919,15 @@ static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat, rWrt.Strm().WriteOString( sOut ); sOut = ""; + std::string_view sStyle; + if (rHWrt.IsSpacePreserve()) + { + if (rHWrt.mbXHTML) + rWrt.Strm().WriteOString(" xml:space=\"preserve\""); + else + sStyle = "white-space: pre-wrap"; + } + // if necessary, output alignment if( !rHWrt.m_bNoAlign && pAdjItem ) OutHTML_SvxAdjust( rWrt, *pAdjItem ); @@ -929,7 +938,7 @@ static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat, // and now, if necessary, the STYLE options if (rHWrt.m_bCfgOutStyles && rInfo.moItemSet) { - OutCSS1_ParaTagStyleOpt( rWrt, *rInfo.moItemSet ); + OutCSS1_ParaTagStyleOpt(rWrt, *rInfo.moItemSet, sStyle); } if (rHWrt.m_bParaDotLeaders) { @@ -1001,7 +1010,7 @@ static void OutHTML_SwFormatOff( Writer& rWrt, const SwHTMLTextCollOutputInfo& r if( rInfo.ShouldOutputToken() ) { - if( rHWrt.m_bLFPossible ) + if (rHWrt.IsLFPossible()) rHWrt.OutNewLine( true ); // if necessary, for BLOCKQUOTE, ADDRESS and DD another paragraph token @@ -1012,18 +1021,18 @@ static void OutHTML_SwFormatOff( Writer& rWrt, const SwHTMLTextCollOutputInfo& r HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_parabreak), false ); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rHWrt.GetNamespace() + rInfo.aToken), false ); - rHWrt.m_bLFPossible = + rHWrt.SetLFPossible( rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dt && rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dd && - rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li; + rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li); } if( rInfo.bOutDiv ) { rHWrt.DecIndentLevel(); - if( rHWrt.m_bLFPossible ) + if (rHWrt.IsLFPossible()) rHWrt.OutNewLine(); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false ); - rHWrt.m_bLFPossible = true; + rHWrt.SetLFPossible(true); } // if necessary, close the list item, then close a bulleted or numbered list @@ -1996,6 +2005,30 @@ void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos ) } } +static bool NeedPreserveWhitespace(std::u16string_view str) +{ + if (str.empty()) + return false; + // leading / trailing spaces + if (o3tl::internal::implIsWhitespace(str.front()) + || o3tl::internal::implIsWhitespace(str.back())) + return true; + bool bWasSpace = false; + for (auto ch : str) + { + if (o3tl::internal::implIsWhitespace(ch)) + { + if (bWasSpace) + return true; // Second whitespace in a row + else + bWasSpace = true; + } + else + bWasSpace = false; + } + return false; +} + /* Output of the nodes*/ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) { @@ -2022,10 +2055,10 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) // Output all the nodes that are anchored to a frame rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any ); - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); // paragraph tag on a new line - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace); aHtml.start(OOO_STRING_SVTOOLS_HTML_horzrule); @@ -2127,11 +2160,11 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) { // ... and it is located before a table or a section rHTMLWrt.OutBookmarks(); - rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE; + rHTMLWrt.SetLFPossible(rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE); // Output all frames that are anchored to this node rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any ); - rHTMLWrt.m_bLFPossible = false; + rHTMLWrt.SetLFPossible(false); return rWrt; } @@ -2202,12 +2235,15 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) // now, output the tag of the paragraph const SwFormat& rFormat = pNd->GetAnyFormatColl(); SwHTMLTextCollOutputInfo aFormatInfo; - bool bOldLFPossible = rHTMLWrt.m_bLFPossible; + bool bOldLFPossible = rHTMLWrt.IsLFPossible(); + bool bOldSpacePreserve = rHTMLWrt.IsSpacePreserve(); + if (rHTMLWrt.IsPreserveSpacesOnWritePrefSet()) + rHTMLWrt.SetSpacePreserve(NeedPreserveWhitespace(rStr)); OutHTML_SwFormat( rWrt, rFormat, pNd->GetpSwAttrSet(), aFormatInfo ); // If we didn't open a new line before the paragraph tag, we do that now - rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE; - if( !bOldLFPossible && rHTMLWrt.m_bLFPossible ) + rHTMLWrt.SetLFPossible(rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE); + if (!bOldLFPossible && rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); // then, the bookmarks (including end tag) @@ -2217,12 +2253,12 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) // now it's a good opportunity again for an LF - if it is still allowed // FIXME: for LOK case we set rHTMLWrt.m_nWishLineLen as -1, for now keep old flow // when LOK side will be fixed - don't insert new line at the beginning - if( rHTMLWrt.m_bLFPossible && + if( rHTMLWrt.IsLFPossible() && rHTMLWrt.GetLineLen() >= (rHTMLWrt.m_nWishLineLen >= 0 ? rHTMLWrt.m_nWishLineLen : 70 ) ) { rHTMLWrt.OutNewLine(); } - rHTMLWrt.m_bLFPossible = false; + rHTMLWrt.SetLFPossible(false); // find text that originates from an outline numbering sal_Int32 nOffset = 0; @@ -2419,16 +2455,16 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) if( pTextHt ) { - rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && + rHTMLWrt.SetLFPossible(rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && nStrPos > 0 && - rStr[nStrPos-1] == ' '; + rStr[nStrPos-1] == ' '); sal_uInt16 nCSS1Script = rHTMLWrt.m_nCSS1Script; rHTMLWrt.m_nCSS1Script = aEndPosLst.GetScriptAtPos( nStrPos + nOffset, nCSS1Script ); HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); Out( aHTMLAttrFnTab, pTextHt->GetAttr(), rHTMLWrt ); rHTMLWrt.m_nCSS1Script = nCSS1Script; - rHTMLWrt.m_bLFPossible = false; + rHTMLWrt.SetLFPossible(false); } if( bOutChar ) @@ -2446,7 +2482,7 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) // try to split a line after about 255 characters // at a space character unless in a PRE-context - if( ' ' == c && rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE ) + if( ' ' == c && rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && !rHTMLWrt.IsSpacePreserve() ) { sal_Int32 nLineLen; nLineLen = rHTMLWrt.GetLineLen(); @@ -2556,7 +2592,7 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) { aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak); } - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } } @@ -2583,15 +2619,15 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) rHTMLWrt.m_bClearLeft = false; rHTMLWrt.m_bClearRight = false; - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } // if an LF is not allowed already, it is allowed once the paragraphs // ends with a ' ' - if( !rHTMLWrt.m_bLFPossible && + if (!rHTMLWrt.IsLFPossible() && rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && nEnd > 0 && ' ' == rStr[nEnd-1] ) - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); // dot leaders: print the skipped page number in a different span element if (nIndexTab > -1) { @@ -2601,6 +2637,7 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) rHTMLWrt.m_bTagOn = false; OutHTML_SwFormatOff( rWrt, aFormatInfo ); + rHTMLWrt.SetSpacePreserve(bOldSpacePreserve); // if necessary, close a form rHTMLWrt.OutForm( false ); diff --git a/sw/source/filter/html/htmlfldw.cxx b/sw/source/filter/html/htmlfldw.cxx index ba3f48d8d96e..c858358e3d5d 100644 --- a/sw/source/filter/html/htmlfldw.cxx +++ b/sw/source/filter/html/htmlfldw.cxx @@ -518,7 +518,7 @@ Writer& OutHTML_SwFormatField( Writer& rWrt, const SfxPoolItem& rHt ) } else if( SwFieldIds::Script == pFieldTyp->Which() ) { - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); bool bURL = static_cast<const SwScriptField *>(pField)->IsCodeURL(); @@ -534,7 +534,7 @@ Writer& OutHTML_SwFormatField( Writer& rWrt, const SfxPoolItem& rHt ) HTMLOutFuncs::OutScript( rWrt.Strm(), rWrt.GetBaseURL(), aContents, rType, JAVASCRIPT, aURL, nullptr, nullptr ); - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); } else diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx index 14f04ff35742..b58d3d9f6c44 100644 --- a/sw/source/filter/html/htmlflywriter.cxx +++ b/sw/source/filter/html/htmlflywriter.cxx @@ -416,7 +416,7 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra if( HtmlContainerFlags::NONE != nCntnrMode ) { - if( m_bLFPossible && HtmlContainerFlags::Div == nCntnrMode ) + if (IsLFPossible() && HtmlContainerFlags::Div == nCntnrMode) OutNewLine(); OStringBuffer sOut; @@ -444,7 +444,7 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra if( HtmlContainerFlags::Div == nCntnrMode ) { IncIndentLevel(); - m_bLFPossible = true; + SetLFPossible(true); } } @@ -496,10 +496,10 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra if( HtmlContainerFlags::Div == nCntnrMode ) { DecIndentLevel(); - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false ); - m_bLFPossible = true; + SetLFPossible(true); } else if( HtmlContainerFlags::Span == nCntnrMode ) HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false ); @@ -1200,7 +1200,7 @@ OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const SfxItemSet& rItemSet, con OString aIndMap, aIndArea; const char *pIndArea = nullptr, *pIndMap = nullptr; - if (rHTMLWrt.m_bLFPossible) + if (rHTMLWrt.IsLFPossible()) { rHTMLWrt.OutNewLine( true ); aIndMap = rHTMLWrt.GetIndentString(); @@ -1291,7 +1291,7 @@ Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer& rWrt, const SwFrameFormat OUString aIMapName = lclWriteOutImap(rHTMLWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem); // put img into new line - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); // <a name=...></a>...<img ...> @@ -1647,7 +1647,7 @@ static Writer & OutHTML_FrameFormatAsMulticol( Writer& rWrt, rHTMLWrt.OutAndSetDefList( 0 ); // output as Multicol - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); OStringBuffer sOut; @@ -1690,7 +1690,7 @@ static Writer & OutHTML_FrameFormatAsMulticol( Writer& rWrt, rWrt.Strm().WriteChar( '>' ); - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); rHTMLWrt.IncIndentLevel(); // indent the content of Multicol const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); @@ -1709,10 +1709,10 @@ static Writer & OutHTML_FrameFormatAsMulticol( Writer& rWrt, } rHTMLWrt.DecIndentLevel(); // indent the content of Multicol; - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol), false ); - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); return rWrt; } @@ -1722,7 +1722,7 @@ static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& r SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); // if possible, output a line break before the graphic - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); OString sOut = @@ -1759,7 +1759,7 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt, aTag = OOO_STRING_SVTOOLS_HTML_span; // output as DIV - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); OStringBuffer sOut; @@ -1775,7 +1775,7 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt, rWrt.Strm().WriteChar( '>' ); rHTMLWrt.IncIndentLevel(); // indent the content - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex(); @@ -1797,7 +1797,7 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt, } rHTMLWrt.DecIndentLevel(); // indent the content of Multicol; - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + aTag), false ); diff --git a/sw/source/filter/html/htmlforw.cxx b/sw/source/filter/html/htmlforw.cxx index 9bad5a4cbc8f..669bcc5686a7 100644 --- a/sw/source/filter/html/htmlforw.cxx +++ b/sw/source/filter/html/htmlforw.cxx @@ -433,16 +433,16 @@ void SwHTMLWriter::OutForm( bool bOn, if( !bOn ) { DecIndentLevel(); // indent content of form - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_form), false ); - m_bLFPossible = true; + SetLFPossible(true); return; } // the new form is opened - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_form; @@ -524,7 +524,7 @@ void SwHTMLWriter::OutForm( bool bOn, Strm().WriteChar( '>' ); IncIndentLevel(); // indent content of form - m_bLFPossible = true; + SetLFPossible(true); } void SwHTMLWriter::OutHiddenControls( @@ -569,7 +569,7 @@ void SwHTMLWriter::OutHiddenControls( if( form::FormComponentType::HIDDENCONTROL == *n ) { - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine( true ); OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_input " " OOO_STRING_SVTOOLS_HTML_O_type "=\"" @@ -770,7 +770,7 @@ Writer& OutHTML_DrawFrameFormatAsControl( Writer& rWrt, break; case form::FormComponentType::LISTBOX: - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); eTag = TAG_SELECT; aTmp = xPropSet->getPropertyValue( "Dropdown" ); @@ -816,7 +816,7 @@ Writer& OutHTML_DrawFrameFormatAsControl( Writer& rWrt, if( bMultiLine ) { - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); eTag = TAG_TEXTAREA; @@ -1257,7 +1257,7 @@ Writer& OutHTML_DrawFrameFormatAsControl( Writer& rWrt, rWrt.Strm().WriteOString( aEndTags ); // Controls aren't bound to a paragraph, therefore don't output LF anymore! - rHTMLWrt.m_bLFPossible = false; + rHTMLWrt.SetLFPossible(false); if( rHTMLWrt.mxFormComps.is() ) rHTMLWrt.OutHiddenControls( rHTMLWrt.mxFormComps, xPropSet ); diff --git a/sw/source/filter/html/htmlftn.cxx b/sw/source/filter/html/htmlftn.cxx index 773fe0b5e06a..74f20368e6d4 100644 --- a/sw/source/filter/html/htmlftn.cxx +++ b/sw/source/filter/html/htmlftn.cxx @@ -358,7 +358,7 @@ void SwHTMLWriter::OutFootEndNotes() sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number(static_cast<sal_Int32>(++m_nFootNote)); } - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division @@ -367,7 +367,7 @@ void SwHTMLWriter::OutFootEndNotes() HTMLOutFuncs::Out_String( Strm(), sFootnoteName ); Strm().WriteCharPtr( "\">" ); - m_bLFPossible = true; + SetLFPossible(true); IncIndentLevel(); // indent content of <DIV> OSL_ENSURE( pTextFootnote, "SwHTMLWriter::OutFootEndNotes: SwTextFootnote is missing" ); @@ -382,10 +382,10 @@ void SwHTMLWriter::OutFootEndNotes() } DecIndentLevel(); // indent content of <DIV> - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false ); - m_bLFPossible = true; + SetLFPossible(true); OSL_ENSURE( !m_pFormatFootnote, "SwHTMLWriter::OutFootEndNotes: Footnote was not output" ); diff --git a/sw/source/filter/html/htmlnumwriter.cxx b/sw/source/filter/html/htmlnumwriter.cxx index 381b146c36e7..05fc0f4e992a 100644 --- a/sw/source/filter/html/htmlnumwriter.cxx +++ b/sw/source/filter/html/htmlnumwriter.cxx @@ -315,7 +315,7 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt, for( sal_uInt16 i=rInfo.GetDepth(); i>nNextDepth; i-- ) { rWrt.DecIndentLevel(); // indent content of <OL> - if( rWrt.m_bLFPossible ) + if (rWrt.IsLFPossible()) rWrt.OutNewLine(); // </OL>/</UL> in a new line // a list is started or ended: @@ -333,7 +333,7 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt, rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li), /*bOn=*/false); } - rWrt.m_bLFPossible = true; + rWrt.SetLFPossible(true); } return rWrt; diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx index 819218527bf2..c4131e1af4f9 100644 --- a/sw/source/filter/html/htmlplug.cxx +++ b/sw/source/filter/html/htmlplug.cxx @@ -1246,7 +1246,7 @@ Writer& OutHTML_FrameFormatOLENode( Writer& rWrt, const SwFrameFormat& rFrameFor HtmlFrmOpts nFrameOpts; // if possible output a line break before the "object" - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine( true ); if( !rFrameFormat.GetName().isEmpty() ) @@ -1643,7 +1643,7 @@ Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrame aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName); // Refer to this data. - if (rHTMLWrt.m_bLFPossible) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); rWrt.Strm().WriteOString(Concat2View("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object)); rWrt.Strm().WriteOString(Concat2View(" data=\"" + aFileName.toUtf8() + "\"")); @@ -1651,7 +1651,7 @@ Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrame rWrt.Strm().WriteOString(Concat2View(" type=\"" + aFileType.toUtf8() + "\"")); rWrt.Strm().WriteOString(">"); bObjectOpened = true; - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } if (!bObjectOpened || bWriteReplacementGraphic) diff --git a/sw/source/filter/html/htmltabw.cxx b/sw/source/filter/html/htmltabw.cxx index 80d1011539d6..ddf7fc6ca7c8 100644 --- a/sw/source/filter/html/htmltabw.cxx +++ b/sw/source/filter/html/htmltabw.cxx @@ -467,7 +467,7 @@ void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt, sOut.append('>'); rWrt.Strm().WriteOString( sOut ); sOut.setLength(0); - rWrt.m_bLFPossible = true; + rWrt.SetLFPossible(true); rWrt.IncIndentLevel(); // indent the content of <TD>...</TD> @@ -504,11 +504,11 @@ void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt, rWrt.DecIndentLevel(); // indent the content of <TD>...</TD> - if( rWrt.m_bLFPossible ) + if (rWrt.IsLFPossible()) rWrt.OutNewLine(); aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false); - rWrt.m_bLFPossible = true; + rWrt.SetLFPossible(true); } // output a line as lines @@ -640,7 +640,7 @@ void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign, // close previous numbering, etc rWrt.ChangeParaToken( HtmlTokenId::NONE ); - if( rWrt.m_bLFPossible ) + if (rWrt.IsLFPossible()) rWrt.OutNewLine(); // <TABLE> in new line OStringBuffer sOut; sOut.append('<').append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table); @@ -1092,7 +1092,7 @@ Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode, if( text::HoriOrientation::NONE!=eDivHoriOri ) { - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); // <CENTER> in new line if( text::HoriOrientation::CENTER==eDivHoriOri ) { @@ -1120,12 +1120,12 @@ Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode, } } rHTMLWrt.IncIndentLevel(); // indent content of <CENTER> - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } // If the table isn't in a frame, then you always can output a LF. if( text::HoriOrientation::NONE==eTabHoriOri ) - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout(); @@ -1155,7 +1155,7 @@ Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode, // If the table wasn't in a frame, then you always can output a LF. if( text::HoriOrientation::NONE==eTabHoriOri ) - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); if( text::HoriOrientation::NONE!=eDivHoriOri ) { @@ -1169,7 +1169,7 @@ Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode, // Not XHTML's css center: end <center>. HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + aTag), false); } - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } // move Pam behind the table diff --git a/sw/source/filter/html/svxcss1.cxx b/sw/source/filter/html/svxcss1.cxx index 9b312e0b3239..e4d06650e9a0 100644 --- a/sw/source/filter/html/svxcss1.cxx +++ b/sw/source/filter/html/svxcss1.cxx @@ -3083,6 +3083,19 @@ static void ParseCSS1_visibility(const CSS1Expression* pExpr, SfxItemSet& /*rIte rPropInfo.m_bVisible = pExpr->GetString() != "hidden"; } +static void ParseCSS1_white_space(const CSS1Expression* pExpr, SfxItemSet& /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, const SvxCSS1Parser& /*rParser*/) +{ + if (pExpr->GetType() == CSS1_IDENT) + { + if (pExpr->GetString().equalsIgnoreAsciiCase("pre") + || pExpr->GetString().equalsIgnoreAsciiCase("pre-wrap")) + { + rPropInfo.m_bPreserveSpace = true; + } + } +} + namespace { // the assignment of property to parsing function @@ -3152,6 +3165,7 @@ CSS1PropEntry const aCSS1PropFnTab[] = CSS1_PROP_ENTRY(text_transform), CSS1_PROP_ENTRY(top), CSS1_PROP_ENTRY(visibility), + CSS1_PROP_ENTRY(white_space), CSS1_PROP_ENTRY(widows), CSS1_PROP_ENTRY(width), }; diff --git a/sw/source/filter/html/svxcss1.hxx b/sw/source/filter/html/svxcss1.hxx index 30c5a7b4accb..5e992b62b08a 100644 --- a/sw/source/filter/html/svxcss1.hxx +++ b/sw/source/filter/html/svxcss1.hxx @@ -115,6 +115,7 @@ public: bool m_bTextIndent : 1; bool m_bNumbering : 1; bool m_bBullet : 1; + bool m_bPreserveSpace = false; SvxAdjust m_eFloat; diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx index 6f9c71494461..2d8f99c44d95 100644 --- a/sw/source/filter/html/swhtml.cxx +++ b/sw/source/filter/html/swhtml.cxx @@ -3976,6 +3976,11 @@ void SwHTMLParser::NewPara() case HtmlOptionId::DIR: aDir = rOption.GetString(); break; + case HtmlOptionId::XML_SPACE: + if (rOption.GetString() == "preserve") + SetPreserveSpaces(true); + break; + default: break; } } @@ -3999,6 +4004,9 @@ void SwHTMLParser::NewPara() "Class is not considered" ); DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); InsertAttrs( aItemSet, aPropInfo, xCntxt.get() ); + + if (aPropInfo.m_bPreserveSpace) + SetPreserveSpaces(true); } } @@ -4064,6 +4072,7 @@ void SwHTMLParser::EndPara( bool bReal ) SetTextCollAttrs(); m_nOpenParaToken = HtmlTokenId::NONE; + SetPreserveSpaces(false); } void SwHTMLParser::NewHeading( HtmlTokenId nToken ) diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx index 63ea138ecf08..03879eb42ff0 100644 --- a/sw/source/filter/html/wrthtml.cxx +++ b/sw/source/filter/html/wrthtml.cxx @@ -146,7 +146,6 @@ SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, std::u16string_view rFilte , m_bNoAlign( false ) , m_bClearLeft( false ) , m_bClearRight( false ) - , m_bLFPossible( false ) , m_bPreserveForm( false ) , m_bCfgNetscape4( false ) , mbSkipImages(false) @@ -337,6 +336,16 @@ void SwHTMLWriter::SetupFilterFromPropertyValues( it->second >>= nVal; m_nLeadingTabWidth.emplace(nVal); } + + it = aStoreMap.find("PreserveSpaces"); + if (it != aStoreMap.end()) + { + // Paragraphs with leading/trailing/repeated whitespace will have "white-space: pre-wrap" + // style; whitespace in content will not be altered (except for "LeadingTabWidth" effects) + bool bVal = false; + it->second >>= bVal; + m_bPreserveSpacesOnWrite = bVal; + } } ErrCode SwHTMLWriter::WriteStream() @@ -444,6 +453,7 @@ ErrCode SwHTMLWriter::WriteStream() m_bPreserveForm = false; m_bClearLeft = m_bClearRight = false; m_bLFPossible = false; + m_bSpacePreserve = false; m_nLeftMargin = m_nDfltLeftMargin = m_nDfltRightMargin = 0; m_nDfltTopMargin = m_nDfltBottomMargin = 0; @@ -550,7 +560,7 @@ ErrCode SwHTMLWriter::WriteStream() sal_uInt16 nHeaderAttrs = 0; m_pCurrPageDesc = MakeHeader( nHeaderAttrs ); - m_bLFPossible = true; + SetLFPossible(true); // output forms which contain only HiddenControls OutHiddenForms(); @@ -590,7 +600,7 @@ ErrCode SwHTMLWriter::WriteStream() OutHTML_HeaderFooter( *this, *pFooterFormat, false ); } - if( m_bLFPossible ) + if (IsLFPossible()) OutNewLine(); if (!mbSkipHeaderFooter) { @@ -710,7 +720,7 @@ static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt, { OSL_ENSURE( pCol || !bContinued, "Continuation of DIV" ); - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); OStringBuffer sOut; @@ -787,7 +797,7 @@ static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt, rHTMLWrt.Strm().WriteChar( '>' ); - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); if( !rName.isEmpty() && !bContinued ) rHTMLWrt.OutImplicitMark( rName, "region" ); @@ -797,10 +807,10 @@ static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt, static void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt ) { rHTMLWrt.DecIndentLevel(); - if( rHTMLWrt.m_bLFPossible ) + if (rHTMLWrt.IsLFPossible()) rHTMLWrt.OutNewLine(); HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false ); - rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.SetLFPossible(true); } static Writer& OutHTML_Section( Writer& rWrt, const SwSectionNode& rSectNd ) diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx index 3914a27f134c..f8f5a111e6eb 100644 --- a/sw/source/filter/html/wrthtml.hxx +++ b/sw/source/filter/html/wrthtml.hxx @@ -278,6 +278,10 @@ class SW_DLLPUBLIC SwHTMLWriter : public Writer FieldUnit m_eCSS1Unit; + bool m_bLFPossible = false; // a line break can be inserted + bool m_bSpacePreserve = false; // Using xml::space="preserve", or "white-space: pre-wrap" style + bool m_bPreserveSpacesOnWrite = false; // If export should use m_bSpacePreserve + sal_uInt16 OutHeaderAttrs(); const SwPageDesc *MakeHeader( sal_uInt16& rHeaderAtrs ); void GetControls(); @@ -392,7 +396,6 @@ public: bool m_bNoAlign : 1; // HTML tag doesn't allow ALIGN=... bool m_bClearLeft : 1; // <BR CLEAR=LEFT> write at end of paragraph bool m_bClearRight : 1; // <BR CLEAR=RIGHT> write at end of paragraph - bool m_bLFPossible : 1; // a line break can be inserted // others @@ -486,7 +489,7 @@ public: const OUString *pSVal, std::optional<sw::Css1Background> oBackground = std::nullopt ); void OutCSS1_UnitProperty( const char *pProp, tools::Long nVal ); void OutCSS1_PixelProperty( const char *pProp, tools::Long nVal, bool bVert ); - void OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, bool bDeep=true ); + void OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, bool bDeep=true, std::string_view rAdd = {} ); // events of BODY tag from SFX configuration void OutBasicBodyEvents(); @@ -616,6 +619,12 @@ public: /// Determines the prefix string needed to respect the requested namespace alias. OString GetNamespace() const; + + bool IsLFPossible() const { return !m_bSpacePreserve && m_bLFPossible; } + void SetLFPossible(bool val) { m_bLFPossible = val; } + bool IsSpacePreserve() const { return m_bSpacePreserve; } + void SetSpacePreserve(bool val) { m_bSpacePreserve = val; } + bool IsPreserveSpacesOnWritePrefSet() const { return m_bPreserveSpacesOnWrite; } }; inline bool SwHTMLWriter::IsCSS1Source( sal_uInt16 n ) const @@ -711,7 +720,7 @@ 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 ); -Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ); +Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet, std::string_view rAdd = {} ); Writer& OutCSS1_HintSpanTag( Writer& rWrt, const SfxPoolItem& rHt ); Writer& OutCSS1_HintStyleOpt( Writer& rWrt, const SfxPoolItem& rHt );