sw/qa/filter/ww8/ww8.cxx | 30 ++++++++++++++++++++++++ sw/source/filter/ww8/attributeoutputbase.hxx | 2 - sw/source/filter/ww8/docxattributeoutput.cxx | 33 ++++++++++++++++++--------- sw/source/filter/ww8/docxattributeoutput.hxx | 2 - sw/source/filter/ww8/rtfattributeoutput.cxx | 3 +- sw/source/filter/ww8/rtfattributeoutput.hxx | 3 +- sw/source/filter/ww8/wrtw8nds.cxx | 31 ++++++++++++++++++++++--- sw/source/filter/ww8/ww8atr.cxx | 2 - sw/source/filter/ww8/ww8attributeoutput.hxx | 2 - 9 files changed, 89 insertions(+), 19 deletions(-)
New commits: commit 87e82f80e87bb4a216ea83383864d494f3e92eea Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Tue Nov 29 10:09:10 2022 +0100 Commit: Szymon Kłos <szymon.k...@collabora.com> CommitDate: Tue Jan 3 15:01:05 2023 +0000 docx: export symbol characters correctly Previously we had: after save: <w:t></w:t> original content: <w:sym w:font="Wingdings" w:char="F0E0"/> This patch checks if paragraph has symbol font used and exports content using w:sym mark in that case Change-Id: I74f4bb0d249cbf5dfc930e931f7d91bd0d2e9821 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143455 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144949 Tested-by: Jenkins Reviewed-by: Szymon Kłos <szymon.k...@collabora.com> diff --git a/sw/qa/filter/ww8/ww8.cxx b/sw/qa/filter/ww8/ww8.cxx index 8a2f97438d7b..611d63259ae8 100644 --- a/sw/qa/filter/ww8/ww8.cxx +++ b/sw/qa/filter/ww8/ww8.cxx @@ -9,6 +9,7 @@ #include <swmodeltestbase.hxx> +#include <com/sun/star/awt/CharSet.hpp> #include <com/sun/star/text/XTextDocument.hpp> #include <docsh.hxx> @@ -159,6 +160,35 @@ CPPUNIT_TEST_FIXTURE(Test, testDocxContentControlDropdownEmptyDisplayText) // i.e. we wrote an empty attribute instead of omitting it. assertXPathNoAttribute(pXmlDoc, "//w:sdt/w:sdtPr/w:dropDownList/w:listItem", "displayText"); } + +CPPUNIT_TEST_FIXTURE(Test, testDocxSymbolFontExport) +{ + // Create document with symbol character and font Wingdings + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + + xText->insertString(xCursor, u"", true); + + uno::Reference<text::XTextRange> xRange = xCursor; + uno::Reference<beans::XPropertySet> xTextProps(xRange, uno::UNO_QUERY); + xTextProps->setPropertyValue("CharFontName", uno::Any(OUString("Wingdings"))); + xTextProps->setPropertyValue("CharFontNameAsian", uno::Any(OUString("Wingdings"))); + xTextProps->setPropertyValue("CharFontNameComplex", uno::Any(OUString("Wingdings"))); + xTextProps->setPropertyValue("CharFontCharSet", uno::Any(awt::CharSet::SYMBOL)); + + // When exporting to DOCX: + save("Office Open XML Text"); + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + + assertXPath(pXmlDoc, "//w:p/w:r/w:sym", 1); + assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "font", "Wingdings"); + assertXPath(pXmlDoc, "//w:p/w:r/w:sym[1]", "char", "f0e0"); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index ac3164ae2de6..99cae10812cc 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -191,7 +191,7 @@ public: virtual void WritePostitFieldReference() {}; /// Output text (inside a run). - virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) = 0; + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) = 0; /// Output text (without markup). virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) = 0; diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 8aec04ef8df0..03dd2aafce71 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -3478,7 +3478,8 @@ bool DocxAttributeOutput::FootnoteEndnoteRefTag() the switch in DocxAttributeOutput::RunText() nicer ;-) */ static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken, - const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true ) + const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true, + const OUString& rSymbolFont = OUString() ) { const sal_Unicode *pBegin = rBegin; @@ -3489,22 +3490,34 @@ static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextT if ( pBegin >= pEnd ) return false; // we want to write at least one character - // we have to add 'preserve' when starting/ending with space - if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' ) + bool bIsSymbol = !rSymbolFont.isEmpty(); + + std::u16string_view aView( pBegin, pEnd - pBegin ); + if (bIsSymbol) { - pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); + for (char16_t aChar : aView) + { + pSerializer->singleElementNS(XML_w, XML_sym, + FSNS(XML_w, XML_font), rSymbolFont, + FSNS(XML_w, XML_char), OString::number(aChar, 16)); + } } else - pSerializer->startElementNS(XML_w, nTextToken); - - pSerializer->writeEscaped( std::u16string_view( pBegin, pEnd - pBegin ) ); + { + // we have to add 'preserve' when starting/ending with space + if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' ) + pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); + else + pSerializer->startElementNS(XML_w, nTextToken); - pSerializer->endElementNS( XML_w, nTextToken ); + pSerializer->writeEscaped( aView ); + pSerializer->endElementNS( XML_w, nTextToken ); + } return true; } -void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ ) +void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/, const OUString& rSymbolFont ) { if( m_closeHyperlinkInThisRun ) { @@ -3569,7 +3582,7 @@ void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCh } } - impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false ); + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false, rSymbolFont ); } void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/) diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index e3f0e39733bd..989a76268e81 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -207,7 +207,7 @@ public: virtual void WritePostitFieldReference() override; /// Output text (inside a run). - virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) override; + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) override; /// Output text (without markup). virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override; diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index 6f777b148407..262d67ff4c1e 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -515,7 +515,8 @@ OString RtfAttributeOutput::MoveCharacterProperties(bool aAutoWriteRtlLtr) return aBuf.makeStringAndClear(); } -void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/) +void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/, + const OUString& /*rSymbolFont*/) { SAL_INFO("sw.rtf", __func__ << ", rText: " << rText); RawText(rText, m_rExport.GetCurrentEncoding()); diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx index 0ecf103a74fb..5042b523ba1a 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.hxx +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -85,7 +85,8 @@ public: void EndRunProperties(const SwRedlineData* pRedlineData) override; /// Output text (inside a run). - void RunText(const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8) override; + void RunText(const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, + const OUString& rSymbolFont = OUString()) override; // Access to (anyway) private buffers, used by the sdr exporter OStringBuffer& RunText(); diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 8b2f09d88023..3b6f4c2b47ed 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -2224,6 +2224,25 @@ bool MSWordExportBase::NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreak return pList.size() > 2 && NeedSectionBreak( rNd ); } +namespace { +OUString lcl_GetSymbolFont(SwAttrPool& rPool, const SwTextNode* pTextNode, int nStart, int nEnd) +{ + SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONT> aSet( rPool ); + if ( pTextNode && pTextNode->GetParaAttr(aSet, nStart, nEnd) ) + { + SfxPoolItem const* pPoolItem = aSet.GetItem(RES_CHRATR_FONT); + if (pPoolItem) + { + const SvxFontItem* pFontItem = static_cast<const SvxFontItem*>(pPoolItem); + if (pFontItem->GetCharSet() == RTL_TEXTENCODING_SYMBOL) + return pFontItem->GetFamilyName(); + } + } + + return OUString(); +} +} + void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) { SAL_INFO( "sw.ww8", "<OutWW8_SwTextNode>" ); @@ -2447,6 +2466,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nCurrentPos ); + OUString aSymbolFont; sal_Int32 nLen = nNextAttr - nCurrentPos; if ( !bTextAtr && nLen ) { @@ -2613,12 +2633,13 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) assert(0 <= nLen); OUString aSnippet( aAttrIter.GetSnippet( aStr, nCurrentPos + ofs, nLen ) ); + const SwTextNode* pTextNode( rNode.GetTextNode() ); if ( ( m_nTextTyp == TXT_EDN || m_nTextTyp == TXT_FTN ) && nCurrentPos == 0 && nLen > 0 ) { // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent sal_Int32 nFirstLineIndent=0; SfxItemSetFixed<RES_LR_SPACE, RES_LR_SPACE> aSet( m_rDoc.GetAttrPool() ); - const SwTextNode* pTextNode( rNode.GetTextNode() ); + if ( pTextNode && pTextNode->GetAttr(aSet) ) { const SvxLRSpaceItem* pLRSpace = aSet.GetItem<SvxLRSpaceItem>(RES_LR_SPACE); @@ -2632,6 +2653,8 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) m_bAddFootnoteTab = false; } + aSymbolFont = lcl_GetSymbolFont(m_rDoc.GetAttrPool(), pTextNode, nCurrentPos + ofs, nCurrentPos + ofs + nLen); + if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) ) { aSavedSnippet = aSnippet ; @@ -2639,7 +2662,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) else { bPostponeWritingText = false ; - AttrOutput().RunText( aSnippet, eChrSet ); + AttrOutput().RunText( aSnippet, eChrSet, aSymbolFont ); } if (ofs == 1 && nNextAttr == nEnd) @@ -2764,6 +2787,8 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) AttrOutput().WritePostitFieldReference(); + aSymbolFont = lcl_GetSymbolFont(m_rDoc.GetAttrPool(), &rNode, nCurrentPos, nCurrentPos + nLen); + if (bPostponeWritingText) { if (FLY_PROCESSED == nStateOfFlyFrame || FLY_NONE == nStateOfFlyFrame) @@ -2785,7 +2810,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) // OutAttr may have introduced new comments, so write them out now AttrOutput().WritePostitFieldReference(); } - AttrOutput().RunText( aSavedSnippet, eChrSet ); + AttrOutput().RunText( aSavedSnippet, eChrSet, aSymbolFont ); AttrOutput().EndRun(&rNode, nCurrentPos, nLen, nNextAttr == nEnd); } else diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 70ce371185f8..d0bed9c5c5bf 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -1136,7 +1136,7 @@ void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) m_rWW8Export.m_pO->clear(); } -void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet ) +void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet, const OUString& /*rSymbolFont*/ ) { RawText(rText, eCharSet); } diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx index 3b78c33317d7..e139ae71f906 100644 --- a/sw/source/filter/ww8/ww8attributeoutput.hxx +++ b/sw/source/filter/ww8/ww8attributeoutput.hxx @@ -67,7 +67,7 @@ public: virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override; /// Output text. - virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) override; + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) override; /// Output text (without markup). virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override;