diff options
Diffstat (limited to 'sw/source/filter/ww8/docxattributeoutput.cxx')
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 379 |
1 files changed, 272 insertions, 107 deletions
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index e74ea397c40a..2d6ed05c3247 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -137,6 +137,7 @@ #include <frmatr.hxx> #include <txtatr.hxx> #include <frameformats.hxx> +#include <textcontentcontrol.hxx> #include <o3tl/string_view.hxx> #include <o3tl/unit_conversion.hxx> @@ -363,16 +364,6 @@ void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame) m_rExport.SetFloatingTableFrame(nullptr); } -void DocxAttributeOutput::StartContentControl(const SwFormatContentControl& rFormatContentControl) -{ - m_pContentControl = rFormatContentControl.GetContentControl(); -} - -void DocxAttributeOutput::EndContentControl() -{ - ++m_nCloseContentControlInThisRun; -} - static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput) { const auto& rExport = rDocxAttributeOutput.GetExport(); @@ -624,16 +615,24 @@ void SdtBlockHelper::DeleteAndResetTheLists() m_pTextAttrs.clear(); if (!m_aAlias.isEmpty()) m_aAlias.clear(); + if (!m_aTag.isEmpty()) + m_aTag.clear(); + if (!m_aLock.isEmpty()) + m_aLock.clear(); if (!m_aPlaceHolderDocPart.isEmpty()) m_aPlaceHolderDocPart.clear(); if (!m_aColor.isEmpty()) m_aColor.clear(); - m_bHasId = false; + if (!m_aAppearance.isEmpty()) + m_aAppearance.clear(); + m_bShowingPlaceHolder = false; + m_nId = 0; + m_nTabIndex = 0; } void SdtBlockHelper::WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing) { - if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId) + if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_nId) return; // sdt start mark @@ -688,19 +687,15 @@ void SdtBlockHelper::WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, b // clear sdt status m_nSdtPrToken = 0; - m_pTokenChildren.clear(); - m_pDataBindingAttrs.clear(); - m_pTextAttrs.clear(); - m_aAlias.clear(); - m_bHasId = false; + DeleteAndResetTheLists(); } void SdtBlockHelper::WriteExtraParams(::sax_fastparser::FSHelperPtr& pSerializer) { - if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId) - //Word won't open a document with an empty id tag, we fill it with a random number - pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), - OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()))); + if (m_nId) + { + pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), OString::number(m_nId)); + } if (m_pDataBindingAttrs.is()) { @@ -720,13 +715,32 @@ void SdtBlockHelper::WriteExtraParams(::sax_fastparser::FSHelperPtr& pSerializer pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), m_aPlaceHolderDocPart); pSerializer->endElementNS(XML_w, XML_placeholder); } + + if (m_bShowingPlaceHolder) + pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); + if (!m_aColor.isEmpty()) { pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), m_aColor); } + if (!m_aAppearance.isEmpty()) + { + pSerializer->singleElementNS(XML_w15, XML_appearance, FSNS(XML_w15, XML_val), m_aAppearance); + } + if (!m_aAlias.isEmpty()) pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), m_aAlias); + + if (!m_aTag.isEmpty()) + pSerializer->singleElementNS(XML_w, XML_tag, FSNS(XML_w, XML_val), m_aTag); + + if (m_nTabIndex) + pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, XML_val), + OString::number(m_nTabIndex)); + + if (!m_aLock.isEmpty()) + pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), m_aLock); } void SdtBlockHelper::EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer) @@ -826,13 +840,41 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property m_aColor = sValue; } } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_appearance") + { + if (!(aPropertyValue.Value >>= m_aAppearance)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt appearance value"); + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_showingPlcHdr") + { + if (!(aPropertyValue.Value >>= m_bShowingPlaceHolder)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt ShowingPlcHdr"); + } else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aAlias.isEmpty()) { if (!(aPropertyValue.Value >>= m_aAlias)) SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt alias value"); } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_tag" && m_aTag.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aTag)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt tag value"); + } else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") - m_bHasId = true; + { + if (!(aPropertyValue.Value >>= m_nId)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt id value"); + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_tabIndex" && !m_nTabIndex) + { + if (!(aPropertyValue.Value >>= m_nTabIndex)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt tabIndex value"); + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_lock" && m_aLock.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aLock)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt lock value"); + } else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") m_nSdtPrToken = FSNS(XML_w, XML_citation); else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" || @@ -1094,7 +1136,6 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT //These should be written out to the actual Node and not to the anchor. //Clear them as they will be repopulated when the node is processed. m_aParagraphSdt.m_nSdtPrToken = 0; - m_aParagraphSdt.m_bHasId = false; m_aParagraphSdt.DeleteAndResetTheLists(); } @@ -1466,13 +1507,13 @@ void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMar if ( pRedlineParagraphMarkerDeleted ) { - StartRedline( pRedlineParagraphMarkerDeleted ); - EndRedline( pRedlineParagraphMarkerDeleted ); + StartRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true ); + EndRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true ); } if ( pRedlineParagraphMarkerInserted ) { - StartRedline( pRedlineParagraphMarkerInserted ); - EndRedline( pRedlineParagraphMarkerInserted ); + StartRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true ); + EndRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true ); } // mergeTopMarks() after paragraph mark properties child elements. @@ -1510,7 +1551,8 @@ void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMar m_pSerializer->endElementNS(XML_w, XML_smartTag); } - if ( m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE ) + if ((m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE) + && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) { m_pSerializer->startElementNS(XML_w, XML_r); m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column"); @@ -1522,7 +1564,8 @@ void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMar m_nColBreakStatus = COLBRK_NONE; } - if ( m_bPostponedPageBreak && !m_bWritingHeaderFooter ) + if (m_bPostponedPageBreak && !m_bWritingHeaderFooter + && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) { m_pSerializer->startElementNS(XML_w, XML_r); m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); @@ -1575,7 +1618,7 @@ void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text" } -void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/) +void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun) { int nFieldsInPrevHyperlink = m_nFieldsInHyperlink; // Reset m_nFieldsInHyperlink if a new hyperlink is about to start @@ -1632,12 +1675,6 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / m_bEndCharSdt = false; } - for (; m_nCloseContentControlInPreviousRun > 0; --m_nCloseContentControlInPreviousRun) - { - // Not the last run of this paragraph. - WriteContentControlEnd(); - } - if ( m_closeHyperlinkInPreviousRun ) { if ( m_startedHyperlink ) @@ -1665,9 +1702,9 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / // InputField with extra grabbag params - it is sdt field (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))) { - StartRedline( m_pRedlineData ); + StartRedline( m_pRedlineData, bLastRun ); StartField_Impl( pNode, nPos, *pIt, true ); - EndRedline( m_pRedlineData ); + EndRedline( m_pRedlineData, bLastRun ); if (m_startedHyperlink) ++m_nFieldsInHyperlink; @@ -1735,10 +1772,8 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / m_nHyperLinkCount++; } - WriteContentControlStart(); - // if there is some redlining in the document, output it - StartRedline( m_pRedlineData ); + StartRedline( m_pRedlineData, bLastRun ); // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks. // The same is applied for permission ranges. @@ -1782,6 +1817,17 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / DoWriteBookmarkStartIfExist(nPos); + if (nLen != -1) + { + SwTextAttr* pAttr = pNode->GetTextAttrAt(nPos, RES_TXTATR_CONTENTCONTROL, SwTextNode::DEFAULT); + if (pAttr && pAttr->GetStart() == nPos) + { + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + m_pContentControl = pTextContentControl->GetContentControl().GetContentControl(); + WriteContentControlStart(); + } + } + m_pSerializer->startElementNS(XML_w, XML_r); if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is()) { @@ -1800,9 +1846,19 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / // append the actual run end m_pSerializer->endElementNS( XML_w, XML_r ); + if (nLen != -1) + { + sal_Int32 nEnd = nPos + nLen; + SwTextAttr* pAttr = pNode->GetTextAttrAt(nPos, RES_TXTATR_CONTENTCONTROL, SwTextNode::DEFAULT); + if (pAttr && *pAttr->GetEnd() == nEnd) + { + WriteContentControlEnd(); + } + } + // if there is some redlining in the document, output it // (except in the case of fields with multiple runs) - EndRedline( m_pRedlineData ); + EndRedline( m_pRedlineData, bLastRun ); // enclose in a sdt block, if necessary: if one is already started, then don't do it for now // (so on export sdt blocks are never nested ATM) @@ -1847,12 +1903,6 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / m_pRedlineData = nullptr; } - for (; m_nCloseContentControlInThisRun > 0; --m_nCloseContentControlInThisRun) - { - // Last run of this paragraph. - WriteContentControlEnd(); - } - if ( m_closeHyperlinkInThisRun ) { if ( m_startedHyperlink ) @@ -1912,7 +1962,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / if ( m_pRedlineData ) { - EndRedline( m_pRedlineData ); + EndRedline( m_pRedlineData, bLastRun ); m_pRedlineData = nullptr; } @@ -2218,11 +2268,9 @@ void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos ) } else if ( rInfos.eType == ww::eFORMCHECKBOX ) { - OUString sName; + const OUString sName = params.getName(); bool bChecked = false; - params.extractParam( ODF_FORMCHECKBOX_NAME, sName ); - const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark); if ( pCheckboxFm && pCheckboxFm->IsChecked() ) bChecked = true; @@ -2336,6 +2384,11 @@ void DocxAttributeOutput::WriteContentControlStart() return; } + if (m_bAnchorLinkedToNode) + { + return; + } + m_pSerializer->startElementNS(XML_w, XML_sdt); m_pSerializer->startElementNS(XML_w, XML_sdtPr); if (!m_pContentControl->GetPlaceholderDocPart().isEmpty()) @@ -2360,6 +2413,44 @@ void DocxAttributeOutput::WriteContentControlStart() m_pContentControl->GetColor()); } + if (!m_pContentControl->GetAppearance().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w15, XML_appearance, FSNS(XML_w15, XML_val), + m_pContentControl->GetAppearance()); + } + + if (!m_pContentControl->GetAlias().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), + m_pContentControl->GetAlias()); + } + + if (!m_pContentControl->GetTag().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w, XML_tag, FSNS(XML_w, XML_val), + m_pContentControl->GetTag()); + } + + if (m_pContentControl->GetId()) + { + m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), + OString::number(m_pContentControl->GetId())); + } + + if (m_pContentControl->GetTabIndex()) + { + // write the unsigned value as as if it were signed since that is all we can import + const sal_Int32 nTabIndex = static_cast<sal_Int32>(m_pContentControl->GetTabIndex()); + m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, XML_val), + OString::number(nTabIndex)); + } + + if (!m_pContentControl->GetLock().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), + m_pContentControl->GetLock()); + } + if (m_pContentControl->GetShowingPlaceHolder()) { m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); @@ -2390,16 +2481,35 @@ void DocxAttributeOutput::WriteContentControlStart() m_pSerializer->endElementNS(XML_w14, XML_checkbox); } - if (m_pContentControl->HasListItems()) + if (m_pContentControl->GetComboBox() || m_pContentControl->GetDropDown()) { - m_pSerializer->startElementNS(XML_w, XML_dropDownList); + if (m_pContentControl->GetComboBox()) + { + m_pSerializer->startElementNS(XML_w, XML_comboBox); + } + else + { + m_pSerializer->startElementNS(XML_w, XML_dropDownList); + } for (const auto& rItem : m_pContentControl->GetListItems()) { - m_pSerializer->singleElementNS(XML_w, XML_listItem, - FSNS(XML_w, XML_displayText), rItem.m_aDisplayText, - FSNS(XML_w, XML_value), rItem.m_aValue); + rtl::Reference<FastAttributeList> xAttributes = FastSerializerHelper::createAttrList(); + if (!rItem.m_aDisplayText.isEmpty()) + { + // If there is no display text, need to omit the attribute, not write an empty one. + xAttributes->add(FSNS(XML_w, XML_displayText), rItem.m_aDisplayText); + } + xAttributes->add(FSNS(XML_w, XML_value), rItem.m_aValue); + m_pSerializer->singleElementNS(XML_w, XML_listItem, xAttributes); + } + if (m_pContentControl->GetComboBox()) + { + m_pSerializer->endElementNS(XML_w, XML_comboBox); + } + else + { + m_pSerializer->endElementNS(XML_w, XML_dropDownList); } - m_pSerializer->endElementNS(XML_w, XML_dropDownList); } if (m_pContentControl->GetDate()) @@ -2428,8 +2538,33 @@ void DocxAttributeOutput::WriteContentControlStart() m_pSerializer->endElementNS(XML_w, XML_date); } + if (m_pContentControl->GetPlainText()) + { + m_pSerializer->singleElementNS(XML_w, XML_text); + } + m_pSerializer->endElementNS(XML_w, XML_sdtPr); m_pSerializer->startElementNS(XML_w, XML_sdtContent); + + const OUString& rPrefixMapping = m_pContentControl->GetDataBindingPrefixMappings(); + const OUString& rXpath = m_pContentControl->GetDataBindingXpath(); + if (!rXpath.isEmpty()) + { + // This content control has a data binding, update the data source. + SwTextContentControl* pTextAttr = m_pContentControl->GetTextAttr(); + SwTextNode* pTextNode = m_pContentControl->GetTextNode(); + SwPosition aPoint(*pTextNode, pTextAttr->GetStart()); + SwPosition aMark(*pTextNode, *pTextAttr->GetEnd()); + SwPaM aPam(aMark, aPoint); + OUString aSnippet = aPam.GetText(); + static sal_Unicode const aForbidden[] = { + CH_TXTATR_BREAKWORD, + 0 + }; + aSnippet = comphelper::string::removeAny(aSnippet, aForbidden); + m_rExport.AddSdtData(rPrefixMapping, rXpath, aSnippet); + } + m_pContentControl = nullptr; } @@ -3337,7 +3472,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; @@ -3348,32 +3484,39 @@ 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 ) { m_closeHyperlinkInPreviousRun = true; } - if (m_nCloseContentControlInThisRun > 0) - { - ++m_nCloseContentControlInPreviousRun; - --m_nCloseContentControlInThisRun; - } m_bRunTextIsOn = true; // one text can be split into more <w:t>blah</w:t>'s by line breaks etc. const sal_Unicode *pBegin = rText.getStr(); @@ -3428,7 +3571,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*/) @@ -3440,7 +3583,7 @@ void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, co { WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() ); SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" ); - EndRun( &rNode, nPos ); // end run before starting ruby to avoid nested runs, and overlap + EndRun( &rNode, nPos, -1 ); // end run before starting ruby to avoid nested runs, and overlap assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby assert(!m_closeHyperlinkInPreviousRun); m_pSerializer->startElementNS(XML_w, XML_r); @@ -3484,7 +3627,7 @@ void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, co EndRunProperties( nullptr ); RunText( rRuby.GetText( ) ); - EndRun( &rNode, nPos ); + EndRun( &rNode, nPos, -1 ); m_pSerializer->endElementNS( XML_w, XML_rt ); m_pSerializer->startElementNS(XML_w, XML_rubyBase); @@ -3494,7 +3637,7 @@ void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, co void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) { SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" ); - EndRun( &rNode, nPos ); + EndRun( &rNode, nPos, -1 ); m_pSerializer->endElementNS( XML_w, XML_rubyBase ); m_pSerializer->endElementNS( XML_w, XML_ruby ); m_pSerializer->endElementNS( XML_w, XML_r ); @@ -3725,7 +3868,12 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) m_pSerializer->startElementNS(XML_w, XML_pPr); - OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName ); + OString sStyleName; + if (auto format = m_rExport.m_rDoc.FindTextFormatCollByName(sParaStyleName)) + if (auto slot = m_rExport.m_pStyles->GetSlot(format); slot != 0xfff) + sStyleName = m_rExport.m_pStyles->GetStyleId(slot); + if (sStyleName.isEmpty()) + sStyleName = MSWordStyles::CreateStyleId(sParaStyleName); if ( !sStyleName.isEmpty() ) m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName); @@ -3768,13 +3916,14 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) -void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) +void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData, bool bLastRun ) { if ( !pRedlineData ) return; // write out stack of this redline recursively (first the oldest) - StartRedline( pRedlineData->Next() ); + if ( !bLastRun ) + StartRedline( pRedlineData->Next(), false ); OString aId( OString::number( m_nRedlineId++ ) ); @@ -3817,7 +3966,7 @@ void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) } } -void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData ) +void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData, bool bLastRun ) { if ( !pRedlineData || m_bWritingField ) return; @@ -3841,7 +3990,8 @@ void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData ) } // write out stack of this redline recursively (first the newest) - EndRedline( pRedlineData->Next() ); + if ( !bLastRun ) + EndRedline( pRedlineData->Next(), false ); } void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t ) @@ -6879,6 +7029,11 @@ void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rCont rContext.m_bStartedParaSdt = m_aParagraphSdt.m_bStartedSdt; m_aParagraphSdt.m_bStartedSdt = false; + rContext.m_bStartedRunSdt = m_aRunSdt.m_bStartedSdt; + m_aRunSdt.m_bStartedSdt = false; + + rContext.m_nHyperLinkCount = m_nHyperLinkCount; + m_nHyperLinkCount = 0; } void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext) @@ -6887,6 +7042,8 @@ void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen; m_tableReference->m_nTableDepth = rContext.m_nTableDepth; m_aParagraphSdt.m_bStartedSdt = rContext.m_bStartedParaSdt; + m_aRunSdt.m_bStartedSdt = rContext.m_bStartedRunSdt; + m_nHyperLinkCount = rContext.m_nHyperLinkCount; } void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape) @@ -7021,7 +7178,7 @@ static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId) } void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, - sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate ) + sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot, bool bAutoUpdate ) { bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false; OUString aRsid, aUiPriority; @@ -7029,12 +7186,12 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, uno::Any aAny; if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR) { - const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nId); + const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nSlot); pFormat->GetGrabBagItem(aAny); } else { - const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nId); + const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nSlot); pRule->GetGrabBagItem(aAny); } const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >(); @@ -7061,28 +7218,25 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, SAL_WARN("sw.ww8", "Unhandled style property: " << rProp.Name); } - // MSO exports English names and writerfilter only recognize them. - const char *pEnglishName = nullptr; const char* pType = nullptr; switch (eType) { case STYLE_TYPE_PARA: pType = "paragraph"; - if ( nWwId < ww::stiMax) - pEnglishName = ww::GetEnglishNameFromSti( static_cast<ww::sti>(nWwId ) ); break; - case STYLE_TYPE_CHAR: pType = "character"; break; + case STYLE_TYPE_CHAR: + pType = "character"; + break; case STYLE_TYPE_LIST: pType = "numbering"; break; } pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType); - pStyleAttributeList->add(FSNS(XML_w, XML_styleId), m_rExport.m_pStyles->GetStyleId(nId)); + pStyleAttributeList->add(FSNS(XML_w, XML_styleId), m_rExport.m_pStyles->GetStyleId(nSlot)); if (bDefault) pStyleAttributeList->add(FSNS(XML_w, XML_default), "1"); if (bCustomStyle) pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1"); m_pSerializer->startElementNS( XML_w, XML_style, pStyleAttributeList); - m_pSerializer->singleElementNS( XML_w, XML_name, - FSNS( XML_w, XML_val ), pEnglishName ? pEnglishName : rName.toUtf8() ); + m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName); if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST) { @@ -7090,7 +7244,7 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase) ); } - if ( nNext != nId && eType != STYLE_TYPE_LIST) + if ( nNext != nSlot && eType != STYLE_TYPE_LIST) { m_pSerializer->singleElementNS( XML_w, XML_next, FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext) ); @@ -7703,10 +7857,16 @@ void DocxAttributeOutput::EmbedFont( std::u16string_view name, FontFamily family { if( !m_rExport.m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) return; // no font embedding with this document - EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch ); - EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch ); - EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch ); - EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch ); + bool foundFont + = EmbedFontStyle(name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch); + foundFont + = EmbedFontStyle(name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch) || foundFont; + foundFont = EmbedFontStyle(name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch) + || foundFont; + foundFont = EmbedFontStyle(name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch) + || foundFont; + if (!foundFont) + EmbedFontStyle(name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_DONTKNOW, pitch); } static char toHexChar( int value ) @@ -7714,21 +7874,21 @@ static char toHexChar( int value ) return value >= 10 ? value + 'A' - 10 : value + '0'; } -void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, FontFamily family, FontItalic italic, - FontWeight weight, FontPitch pitch ) +bool DocxAttributeOutput::EmbedFontStyle(std::u16string_view name, int tag, FontFamily family, + FontItalic italic, FontWeight weight, FontPitch pitch) { // Embed font if at least viewing is allowed (in which case the opening app must check // the font license rights too and open either read-only or not use the font for editing). OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch, EmbeddedFontsHelper::FontRights::ViewingAllowed ); if( fontUrl.isEmpty()) - return; + return false; // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS if( !fontFilesMap.count( fontUrl )) { osl::File file( fontUrl ); if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None ) - return; + return false; uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream( "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf", "application/vnd.openxmlformats-officedocument.obfuscatedFont" ); @@ -7747,7 +7907,7 @@ void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, Fon { SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" ); xOutStream->closeOutput(); - return; + return false; } for( int i = 0; i < 16; @@ -7764,7 +7924,7 @@ void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, Fon { SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); xOutStream->closeOutput(); - return; + return false; } if( eof ) break; @@ -7772,7 +7932,7 @@ void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, Fon { SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); xOutStream->closeOutput(); - return; + return false; } if( readSize == 0 ) break; @@ -7792,6 +7952,7 @@ void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, Fon m_pSerializer->singleElementNS( XML_w, tag, FSNS( XML_r, XML_id ), fontFilesMap[ fontUrl ].relId, FSNS( XML_w, XML_fontKey ), fontFilesMap[ fontUrl ].fontKey ); + return true; } OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco ) @@ -8589,9 +8750,13 @@ void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight ) void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink ) { - OString aStyleId = MSWordStyles::CreateStyleId(rLink.GetINetFormat()); - if (!aStyleId.isEmpty() && !aStyleId.equalsIgnoreAsciiCase("DefaultStyle")) - m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); + const SwCharFormat* pFormat = m_rExport.m_rDoc.FindCharFormatByName(rLink.GetINetFormat()); + if (pFormat) + { + OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pFormat))); + if (!aStyleId.equalsIgnoreAsciiCase("DefaultStyle")) + m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); + } } void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat ) |