From c1d58c46eec5081576979f584151c7e9a4f67fe0 Mon Sep 17 00:00:00 2001 From: Tamas Bunth Date: Fri, 1 Dec 2017 14:58:17 +0100 Subject: tdf#41650 DOCX export: split para on section break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given two pages with entirely different style (resp. not plausible for a single section) and a paragraph which starts on the first page and has a follow on the second. Those page styles are represented in doc/docx structure with sections. Doc/Docx does not support having a paragraph in more than one sections. Workaround is to split the paragraph into more paragraphs and put the section break in between. Change-Id: I4014c45f97e18132470d0d0647bce84831b2460a Reviewed-on: https://gerrit.libreoffice.org/45666 Reviewed-by: Tamás Bunth Tested-by: Tamás Bunth --- sw/source/filter/ww8/wrtw8nds.cxx | 1209 +++++++++++++++++++------------------ sw/source/filter/ww8/wrtww8.cxx | 9 +- sw/source/filter/ww8/wrtww8.hxx | 4 + 3 files changed, 643 insertions(+), 579 deletions(-) diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index c2dbacad1f55..f54181585fb7 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -2169,6 +2169,32 @@ void MSWordExportBase::GetSortedBookmarks( const SwTextNode& rNode, sal_Int32 nA } } +bool MSWordExportBase::NeedSectionBreak( const SwNode& rNd ) const +{ + if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs || m_pAktPageDesc == nullptr ) + return false; + + const SwPageDesc * pPageDesc = rNd.FindPageDesc()->GetFollow(); + + if (m_pAktPageDesc != pPageDesc) + { + if (!sw::util::IsPlausableSingleWordSection(m_pAktPageDesc->GetFirstMaster(), pPageDesc->GetMaster())) + { + return true; + } + } + + return false; +} + +bool MSWordExportBase::NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreakList& pList ) const +{ + rNd.fillSoftPageBreakList( pList ); + pList.insert(0); + pList.insert( rNd.GetText().getLength() ); + return pList.size() > 2 && NeedSectionBreak( rNd ); +} + void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) { SAL_INFO( "sw.ww8", "" ); @@ -2190,7 +2216,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) SwWW8AttrIter aWatermarkAttrIter( *this, rNode ); if (( TXT_HDFT != m_nTextTyp) && aWatermarkAttrIter.IsWatermarkFrame()) { - return; + return; } bool bFlyInTable = m_pParentFrame && IsInTable(); @@ -2246,741 +2272,768 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) } } - AttrOutput().StartParagraph( pTextNodeInfo ); + SwSoftPageBreakList softBreakList; + // Let's decide if we need to split the paragraph because of a section break + bool bNeedParaSplit = NeedTextNodeSplit( rNode, softBreakList ); - const SwSection* pTOXSect = nullptr; - if( m_bInWriteTOX ) + auto aBreakIt = softBreakList.begin(); + // iterate through portions on different pages + do { - // check for end of TOX - SwNodeIndex aIdx( rNode, 1 ); - if( !aIdx.GetNode().IsTextNode() ) + sal_Int32 nAktPos = *aBreakIt; + ++aBreakIt; + + AttrOutput().StartParagraph( pTextNodeInfo ); + + const SwSection* pTOXSect = nullptr; + if( m_bInWriteTOX ) { - const SwSectionNode* pTOXSectNd = rNode.FindSectionNode(); - if ( pTOXSectNd ) + // check for end of TOX + SwNodeIndex aIdx( rNode, 1 ); + if( !aIdx.GetNode().IsTextNode() ) { - pTOXSect = &pTOXSectNd->GetSection(); + const SwSectionNode* pTOXSectNd = rNode.FindSectionNode(); + if ( pTOXSectNd ) + { + pTOXSect = &pTOXSectNd->GetSection(); - const SwNode* pNxt = rNode.GetNodes().GoNext( &aIdx ); - if( pNxt && pNxt->FindSectionNode() == pTOXSectNd ) - pTOXSect = nullptr; + const SwNode* pNxt = rNode.GetNodes().GoNext( &aIdx ); + if( pNxt && pNxt->FindSectionNode() == pTOXSectNd ) + pTOXSect = nullptr; + } } } - } - if ( aAttrIter.RequiresImplicitBookmark() ) - { - OUString sBkmkName = "_toc" + OUString::number( rNode.GetIndex() ); - // Add a bookmark converted to a Word name. - AppendBookmark( BookmarkToWord( sBkmkName ) ); - } + if ( aAttrIter.RequiresImplicitBookmark() ) + { + OUString sBkmkName = "_toc" + OUString::number( rNode.GetIndex() ); + // Add a bookmark converted to a Word name. + AppendBookmark( BookmarkToWord( sBkmkName ) ); + } - // Call this before write out fields and runs - AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); + // Call this before write out fields and runs + AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); - const OUString& aStr( rNode.GetText() ); + const OUString& aStr( rNode.GetText() ); - sal_Int32 nAktPos = 0; - sal_Int32 const nEnd = aStr.getLength(); - bool bIncludeEndOfParaCRInRedlineProperties = false; - sal_Int32 nOpenAttrWithRange = 0; + sal_Int32 const nEnd = aStr.getLength(); + bool bIncludeEndOfParaCRInRedlineProperties = false; + sal_Int32 nOpenAttrWithRange = 0; - ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner; - if ( pTextNodeInfo.get() != nullptr ) - { - pTextNodeInfoInner = pTextNodeInfo->getFirstInner(); - } + ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner; + if ( pTextNodeInfo.get() != nullptr ) + { + pTextNodeInfoInner = pTextNodeInfo->getFirstInner(); + } - do { + do { - const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( nAktPos ); - FlyProcessingState nStateOfFlyFrame = FLY_PROCESSED; - bool bPostponeWritingText = false ; - OUString aSavedSnippet ; + const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( nAktPos ); + FlyProcessingState nStateOfFlyFrame = FLY_PROCESSED; + bool bPostponeWritingText = false ; + OUString aSavedSnippet ; - sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nAktPos ); + sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nAktPos ); - // Skip un-exportable attributes. - if (!aAttrIter.IsExportableAttr(nAktPos)) - { - nAktPos = nNextAttr; - UpdatePosition(&aAttrIter, nAktPos); - eChrSet = aAttrIter.GetCharSet(); - continue; - } + // Skip un-exportable attributes. + if (!aAttrIter.IsExportableAttr(nAktPos)) + { + nAktPos = nNextAttr; + UpdatePosition(&aAttrIter, nAktPos); + eChrSet = aAttrIter.GetCharSet(); + continue; + } - // Is this the only run in this paragraph and it's empty? - bool bSingleEmptyRun = nAktPos == 0 && nNextAttr == 0; - AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); + // Is this the only run in this paragraph and it's empty? + bool bSingleEmptyRun = nAktPos == 0 && nNextAttr == 0; + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); - if( nNextAttr > nEnd ) - nNextAttr = nEnd; + if( nNextAttr > nEnd ) + nNextAttr = nEnd; - if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) - { - if( AttrOutput().FootnoteEndnoteRefTag() ) + if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) { - AttrOutput().EndRun( &rNode, nAktPos, nNextAttr == nEnd ); - AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); + if( AttrOutput().FootnoteEndnoteRefTag() ) + { + AttrOutput().EndRun( &rNode, nAktPos, nNextAttr == nEnd ); + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); + } } - } - - /* - 1) If there is a text node and an overlapping anchor, then write them in two different - runs and not as part of the same run. - 2) Ensure that it is a text node and not in a fly. - 3) If the anchor is associated with a text node with empty text then we ignore. - */ - if( rNode.IsTextNode() - && aStr != "\001" && !aStr.isEmpty() - && !rNode.GetFlyFormat() - && !(IsInTable() && !AllowPostponedTextInTable()) - && aAttrIter.IsAnchorLinkedToThisNode(rNode.GetIndex()) ) - { - bPostponeWritingText = true ; - } - nStateOfFlyFrame = aAttrIter.OutFlys( nAktPos ); - AttrOutput().SetStateOfFlyFrame( nStateOfFlyFrame ); - AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText && (FLY_POSTPONED != nStateOfFlyFrame) ); - // Append bookmarks in this range after flys, exclusive of final - // position of this range - AppendBookmarks( rNode, nAktPos, nNextAttr - nAktPos ); - AppendAnnotationMarks( rNode, nAktPos, nNextAttr - nAktPos ); + /* + 1) If there is a text node and an overlapping anchor, then write them in two different + runs and not as part of the same run. + 2) Ensure that it is a text node and not in a fly. + 3) If the anchor is associated with a text node with empty text then we ignore. + */ + if( rNode.IsTextNode() + && aStr != "\001" && !aStr.isEmpty() + && !rNode.GetFlyFormat() + && !(IsInTable() && !AllowPostponedTextInTable()) + && aAttrIter.IsAnchorLinkedToThisNode(rNode.GetIndex()) ) + { + bPostponeWritingText = true ; + } - // At the moment smarttags are only written for paragraphs, at the - // beginning of the paragraph. - if (nAktPos == 0) - AppendSmartTags(rNode); + nStateOfFlyFrame = aAttrIter.OutFlys( nAktPos ); + AttrOutput().SetStateOfFlyFrame( nStateOfFlyFrame ); + AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText && (FLY_POSTPONED != nStateOfFlyFrame) ); + // Append bookmarks in this range after flys, exclusive of final + // position of this range + AppendBookmarks( rNode, nAktPos, nNextAttr - nAktPos ); + AppendAnnotationMarks( rNode, nAktPos, nNextAttr - nAktPos ); - bool bTextAtr = aAttrIter.IsTextAttr( nAktPos ); - nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nAktPos ); + // At the moment smarttags are only written for paragraphs, at the + // beginning of the paragraph. + if (nAktPos == 0) + AppendSmartTags(rNode); - sal_Int32 nLen = nNextAttr - nAktPos; - if ( !bTextAtr && nLen ) - { - sal_Unicode ch = aStr[nAktPos]; - const sal_Int32 ofs = ( ch == CH_TXT_ATR_FIELDSTART || ch == CH_TXT_ATR_FIELDEND || ch == CH_TXT_ATR_FORMELEMENT? 1 : 0 ); + bool bTextAtr = aAttrIter.IsTextAttr( nAktPos ); + nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nAktPos ); - IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); - if ( ch == CH_TXT_ATR_FIELDSTART ) + sal_Int32 nLen = nNextAttr - nAktPos; + if ( !bTextAtr && nLen ) { - SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); - ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); - OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDSTART??" ); - - if ( pFieldmark && pFieldmark->GetFieldname() == ODF_FORMTEXT ) - AppendBookmark( pFieldmark->GetName() ); - ww::eField eFieldId = lcl_getFieldId( pFieldmark ); - OUString sCode = lcl_getFieldCode( pFieldmark ); - if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) + sal_Unicode ch = aStr[nAktPos]; + const sal_Int32 ofs = ( ch == CH_TXT_ATR_FIELDSTART || ch == CH_TXT_ATR_FIELDEND || ch == CH_TXT_ATR_FORMELEMENT? 1 : 0 ); + + IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + if ( ch == CH_TXT_ATR_FIELDSTART ) { - IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); - if ( it != pFieldmark->GetParameters()->end() ) + SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); + ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); + OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDSTART??" ); + + if ( pFieldmark && pFieldmark->GetFieldname() == ODF_FORMTEXT ) + AppendBookmark( pFieldmark->GetName() ); + ww::eField eFieldId = lcl_getFieldId( pFieldmark ); + OUString sCode = lcl_getFieldCode( pFieldmark ); + if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) { - OUString sFieldId; - it->second >>= sFieldId; - eFieldId = (ww::eField)sFieldId.toInt32(); - } + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sFieldId; + it->second >>= sFieldId; + eFieldId = (ww::eField)sFieldId.toInt32(); + } - it = pFieldmark->GetParameters()->find( ODF_CODE_PARAM ); - if ( it != pFieldmark->GetParameters()->end() ) - { - it->second >>= sCode; + it = pFieldmark->GetParameters()->find( ODF_CODE_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + it->second >>= sCode; + } } - } - OutputField( nullptr, eFieldId, sCode, FieldFlags::Start | FieldFlags::CmdStart ); + OutputField( nullptr, eFieldId, sCode, FieldFlags::Start | FieldFlags::CmdStart ); - if ( pFieldmark && pFieldmark->GetFieldname( ) == ODF_FORMTEXT ) - WriteFormData( *pFieldmark ); - else if ( pFieldmark && pFieldmark->GetFieldname( ) == ODF_HYPERLINK ) - WriteHyperlinkData( *pFieldmark ); - OutputField( nullptr, lcl_getFieldId( pFieldmark ), OUString(), FieldFlags::CmdEnd ); + if ( pFieldmark && pFieldmark->GetFieldname( ) == ODF_FORMTEXT ) + WriteFormData( *pFieldmark ); + else if ( pFieldmark && pFieldmark->GetFieldname( ) == ODF_HYPERLINK ) + WriteHyperlinkData( *pFieldmark ); + OutputField( nullptr, lcl_getFieldId( pFieldmark ), OUString(), FieldFlags::CmdEnd ); - if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) - { - // Check for the presence of a linked OLE object - IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_OLE_PARAM ); - if ( it != pFieldmark->GetParameters()->end() ) + if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) { - OUString sOleId; - uno::Any aValue = it->second; - aValue >>= sOleId; - if ( !sOleId.isEmpty() ) - OutputLinkedOLE( sOleId ); + // Check for the presence of a linked OLE object + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_OLE_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sOleId; + uno::Any aValue = it->second; + aValue >>= sOleId; + if ( !sOleId.isEmpty() ) + OutputLinkedOLE( sOleId ); + } } } - } - else if ( ch == CH_TXT_ATR_FIELDEND ) - { - SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); - ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); + else if ( ch == CH_TXT_ATR_FIELDEND ) + { + SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); + ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); - OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDEND??" ); + OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDEND??" ); - ww::eField eFieldId = lcl_getFieldId( pFieldmark ); - if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) - { - IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); - if ( it != pFieldmark->GetParameters()->end() ) + ww::eField eFieldId = lcl_getFieldId( pFieldmark ); + if ( pFieldmark && pFieldmark->GetFieldname() == ODF_UNHANDLED ) { - OUString sFieldId; - it->second >>= sFieldId; - eFieldId = (ww::eField)sFieldId.toInt32(); + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sFieldId; + it->second >>= sFieldId; + eFieldId = (ww::eField)sFieldId.toInt32(); + } } + + OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close ); + + if ( pFieldmark && pFieldmark->GetFieldname() == ODF_FORMTEXT ) + AppendBookmark( pFieldmark->GetName() ); + } + else if ( ch == CH_TXT_ATR_FORMELEMENT ) + { + SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); + ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); + OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDSTART??" ); + + bool isDropdownOrCheckbox = pFieldmark && (pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN || + pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX ); + + if ( isDropdownOrCheckbox ) + AppendBookmark( pFieldmark->GetName() ); + OutputField( nullptr, lcl_getFieldId( pFieldmark ), + lcl_getFieldCode( pFieldmark ), + FieldFlags::Start | FieldFlags::CmdStart ); + if ( isDropdownOrCheckbox ) + WriteFormData( *pFieldmark ); + OutputField( nullptr, lcl_getFieldId( pFieldmark ), OUString(), FieldFlags::Close ); + if ( isDropdownOrCheckbox ) + AppendBookmark( pFieldmark->GetName() ); } + nLen -= ofs; - OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close ); + // if paragraph needs to be split, write only until split postition + if( bNeedParaSplit && nAktPos + ofs + nLen > *aBreakIt) + nLen = *aBreakIt - nAktPos - ofs; - if ( pFieldmark && pFieldmark->GetFieldname() == ODF_FORMTEXT ) - AppendBookmark( pFieldmark->GetName() ); - } - else if ( ch == CH_TXT_ATR_FORMELEMENT ) - { - SwPosition aPosition( rNode, SwIndex( &rNode, nAktPos ) ); - ::sw::mark::IFieldmark const * const pFieldmark = pMarkAccess->getFieldmarkFor( aPosition ); - OSL_ENSURE( pFieldmark, "Looks like this doc is broken...; where is the Fieldmark for the FIELDSTART??" ); - - bool isDropdownOrCheckbox = pFieldmark && (pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN || - pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX ); - - if ( isDropdownOrCheckbox ) - AppendBookmark( pFieldmark->GetName() ); - OutputField( nullptr, lcl_getFieldId( pFieldmark ), - lcl_getFieldCode( pFieldmark ), - FieldFlags::Start | FieldFlags::CmdStart ); - if ( isDropdownOrCheckbox ) - WriteFormData( *pFieldmark ); - OutputField( nullptr, lcl_getFieldId( pFieldmark ), OUString(), FieldFlags::Close ); - if ( isDropdownOrCheckbox ) - AppendBookmark( pFieldmark->GetName() ); + OUString aSnippet( aAttrIter.GetSnippet( aStr, nAktPos + ofs, nLen ) ); + if ( ( m_nTextTyp == TXT_EDN || m_nTextTyp == TXT_FTN ) && nAktPos == 0 && nLen > 0 ) + { + // Insert tab for aesthetic purposes #i24762# + if ( aSnippet[0] != 0x09 ) + aSnippet = "\x09" + aSnippet; + } + + if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) ) + { + bPostponeWritingText = true ; + aSavedSnippet = aSnippet ; + } + else + { + bPostponeWritingText = false ; + AttrOutput().RunText( aSnippet, eChrSet ); + } } - nLen -= ofs; - OUString aSnippet( aAttrIter.GetSnippet( aStr, nAktPos + ofs, nLen ) ); - if ( ( m_nTextTyp == TXT_EDN || m_nTextTyp == TXT_FTN ) && nAktPos == 0 && nLen > 0 ) + if ( aAttrIter.IsDropCap( nNextAttr ) ) + AttrOutput().FormatDrop( rNode, aAttrIter.GetSwFormatDrop(), nStyle, pTextNodeInfo, pTextNodeInfoInner ); + + // Only output character attributes if this is not a postponed text run. + if (0 != nEnd && !(bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame)) { - // Insert tab for aesthetic purposes #i24762# - if ( aSnippet[0] != 0x09 ) - aSnippet = "\x09" + aSnippet; + // Output the character attributes + // #i51277# do this before writing flys at end of paragraph + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nAktPos ); + AttrOutput().EndRunProperties( pRedlineData ); } - if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) ) + // At the end of line, output the attributes until the CR. + // Exception: footnotes at the end of line + if ( nNextAttr == nEnd ) { - bPostponeWritingText = true ; - aSavedSnippet = aSnippet ; + OSL_ENSURE( nOpenAttrWithRange >= 0, "odd to see this happening, expected >= 0" ); + if ( !bTextAtr && nOpenAttrWithRange <= 0 ) + { + if ( aAttrIter.IncludeEndOfParaCRInRedlineProperties( nEnd ) ) + bIncludeEndOfParaCRInRedlineProperties = true; + else + { + // insert final graphic anchors if any before CR + nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); + // insert final bookmarks if any before CR and after flys + AppendBookmarks( rNode, nEnd, 1 ); + AppendAnnotationMarks( rNode, nEnd, 1 ); + if ( pTOXSect ) + { + m_aCurrentCharPropStarts.pop(); + AttrOutput().EndTOX( *pTOXSect ,false); + } + //For i120928,the position of the bullet's graphic is at end of doc + if (bLastCR && (!bExported)) + { + ExportGrfBullet(rNode); + bExported = true; + } + + WriteCR( pTextNodeInfoInner ); + } + } } - else + + if (0 == nEnd) { - bPostponeWritingText = false ; - AttrOutput().RunText( aSnippet, eChrSet ); + // Output the character attributes + // do it after WriteCR for an empty paragraph (otherwise + // WW8_WrFkp::Append throws SPRMs away...) + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nAktPos ); + AttrOutput().EndRunProperties( pRedlineData ); } - } - if ( aAttrIter.IsDropCap( nNextAttr ) ) - AttrOutput().FormatDrop( rNode, aAttrIter.GetSwFormatDrop(), nStyle, pTextNodeInfo, pTextNodeInfoInner ); + // Exception: footnotes at the end of line + if ( nNextAttr == nEnd ) + { + OSL_ENSURE(nOpenAttrWithRange >= 0, + "odd to see this happening, expected >= 0"); + bool bAttrWithRange = (nOpenAttrWithRange > 0); + if ( nAktPos != nEnd ) + { + nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nEnd ); + OSL_ENSURE(nOpenAttrWithRange == 0, + "odd to see this happening, expected 0"); + } - // Only output character attributes if this is not a postponed text run. - if (0 != nEnd && !(bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame)) - { - // Output the character attributes - // #i51277# do this before writing flys at end of paragraph - AttrOutput().StartRunProperties(); - aAttrIter.OutAttr( nAktPos ); - AttrOutput().EndRunProperties( pRedlineData ); - } + // !bIncludeEndOfParaCRInRedlineProperties implies we have just + // emitted a CR, in which case we want to pass force=true to + // OutputFKP to ensure that an FKP entry for direct character + // formatting is written even if empty, so that the next one will + // start after the CR. + AttrOutput().OutputFKP(!bIncludeEndOfParaCRInRedlineProperties); - // At the end of line, output the attributes until the CR. - // Exception: footnotes at the end of line - if ( nNextAttr == nEnd ) - { - OSL_ENSURE( nOpenAttrWithRange >= 0, "odd to see this happening, expected >= 0" ); - if ( !bTextAtr && nOpenAttrWithRange <= 0 ) - { - if ( aAttrIter.IncludeEndOfParaCRInRedlineProperties( nEnd ) ) - bIncludeEndOfParaCRInRedlineProperties = true; - else + if (bTextAtr || bAttrWithRange || bIncludeEndOfParaCRInRedlineProperties) { // insert final graphic anchors if any before CR nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); // insert final bookmarks if any before CR and after flys AppendBookmarks( rNode, nEnd, 1 ); AppendAnnotationMarks( rNode, nEnd, 1 ); - if ( pTOXSect ) - { - m_aCurrentCharPropStarts.pop(); - AttrOutput().EndTOX( *pTOXSect ,false); - } - //For i120928,the position of the bullet's graphic is at end of doc + WriteCR( pTextNodeInfoInner ); + // #i120928 - position of the bullet's graphic is at end of doc if (bLastCR && (!bExported)) { ExportGrfBullet(rNode); bExported = true; } - WriteCR( pTextNodeInfoInner ); - } - } - } - - if (0 == nEnd) - { - // Output the character attributes - // do it after WriteCR for an empty paragraph (otherwise - // WW8_WrFkp::Append throws SPRMs away...) - AttrOutput().StartRunProperties(); - aAttrIter.OutAttr( nAktPos ); - AttrOutput().EndRunProperties( pRedlineData ); - } + if ( pTOXSect ) + { + m_aCurrentCharPropStarts.pop(); + AttrOutput().EndTOX( *pTOXSect ); + } - // Exception: footnotes at the end of line - if ( nNextAttr == nEnd ) - { - OSL_ENSURE(nOpenAttrWithRange >= 0, - "odd to see this happening, expected >= 0"); - bool bAttrWithRange = (nOpenAttrWithRange > 0); - if ( nAktPos != nEnd ) - { - nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nEnd ); - OSL_ENSURE(nOpenAttrWithRange == 0, - "odd to see this happening, expected 0"); + if (bIncludeEndOfParaCRInRedlineProperties) + { + AttrOutput().Redline( aAttrIter.GetRunLevelRedline( nEnd ) ); + //If there was no redline property emitted, force adding + //another entry for the CR so that in the case that this + //has no redline, but the next para does, then this one is + //not merged with the next + AttrOutput().OutputFKP(true); + } + } } - // !bIncludeEndOfParaCRInRedlineProperties implies we have just - // emitted a CR, in which case we want to pass force=true to - // OutputFKP to ensure that an FKP entry for direct character - // formatting is written even if empty, so that the next one will - // start after the CR. - AttrOutput().OutputFKP(!bIncludeEndOfParaCRInRedlineProperties); + AttrOutput().WritePostitFieldReference(); - if (bTextAtr || bAttrWithRange || bIncludeEndOfParaCRInRedlineProperties) + if( bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame ) { - // insert final graphic anchors if any before CR - nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); - // insert final bookmarks if any before CR and after flys - AppendBookmarks( rNode, nEnd, 1 ); - AppendAnnotationMarks( rNode, nEnd, 1 ); - WriteCR( pTextNodeInfoInner ); - // #i120928 - position of the bullet's graphic is at end of doc - if (bLastCR && (!bExported)) - { - ExportGrfBullet(rNode); - bExported = true; - } - - if ( pTOXSect ) - { - m_aCurrentCharPropStarts.pop(); - AttrOutput().EndTOX( *pTOXSect ); - } - - if (bIncludeEndOfParaCRInRedlineProperties) + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); + //write the postponed text run + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); + AttrOutput().SetAnchorIsLinkedToNode( false ); + AttrOutput().ResetFlyProcessingFlag(); + if (0 != nEnd) { - AttrOutput().Redline( aAttrIter.GetRunLevelRedline( nEnd ) ); - //If there was no redline property emitted, force adding - //another entry for the CR so that in the case that this - //has no redline, but the next para does, then this one is - //not merged with the next - AttrOutput().OutputFKP(true); + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nAktPos ); + AttrOutput().EndRunProperties( pRedlineData ); } + AttrOutput().RunText( aSavedSnippet, eChrSet ); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); } - } + else if( bPostponeWritingText && !aSavedSnippet.isEmpty() ) + { + //write the postponed text run + AttrOutput().RunText( aSavedSnippet, eChrSet ); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); + } + else + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); - AttrOutput().WritePostitFieldReference(); + nAktPos = nNextAttr; + UpdatePosition( &aAttrIter, nAktPos ); + eChrSet = aAttrIter.GetCharSet(); + } + while ( nAktPos < nEnd ); - if( bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame ) + // if paragraph is split, put the section break between the parts + // else check if section break needed after the paragraph + if( !bNeedParaSplit || *aBreakIt != rNode.GetText().getLength() ) { - AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); - //write the postponed text run - AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); - AttrOutput().SetAnchorIsLinkedToNode( false ); - AttrOutput().ResetFlyProcessingFlag(); - if (0 != nEnd) + AttrOutput().SectionBreaks(rNode); + SwNodeIndex aNextIndex( rNode, 1 ); + const SwNode& pNextNode = aNextIndex.GetNode(); + if( pNextNode.IsTextNode() && bNeedParaSplit ) { - AttrOutput().StartRunProperties(); - aAttrIter.OutAttr( nAktPos ); - AttrOutput().EndRunProperties( pRedlineData ); + SectionBreaksAndFrames( *static_cast( + &aNextIndex.GetNode() )); } - AttrOutput().RunText( aSavedSnippet, eChrSet ); - AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); } - else if( bPostponeWritingText && !aSavedSnippet.isEmpty() ) - { - //write the postponed text run - AttrOutput().RunText( aSavedSnippet, eChrSet ); - AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); - } - else - AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); - - nAktPos = nNextAttr; - UpdatePosition( &aAttrIter, nAktPos ); - eChrSet = aAttrIter.GetCharSet(); - } - while ( nAktPos < nEnd ); - - AttrOutput().SectionBreaks(rNode); - AttrOutput().StartParagraphProperties(); + AttrOutput().StartParagraphProperties(); - AttrOutput().ParagraphStyle( nStyle ); + AttrOutput().ParagraphStyle( nStyle ); - if ( m_pParentFrame && IsInTable() ) // Fly-Attrs - OutputFormat( m_pParentFrame->GetFrameFormat(), false, false, true ); + if ( m_pParentFrame && IsInTable() ) // Fly-Attrs + OutputFormat( m_pParentFrame->GetFrameFormat(), false, false, true ); - if ( pTextNodeInfo.get() != nullptr ) - { + if ( pTextNodeInfo.get() != nullptr ) + { #ifdef DBG_UTIL - SAL_INFO( "sw.ww8", pTextNodeInfo->toString()); + SAL_INFO( "sw.ww8", pTextNodeInfo->toString()); #endif - AttrOutput().TableInfoCell( pTextNodeInfoInner ); - if (pTextNodeInfoInner->isFirstInTable()) - { - const SwTable * pTable = pTextNodeInfoInner->getTable(); - - const SwTableFormat* pTabFormat = pTable->GetFrameFormat(); - if (pTabFormat != nullptr) + AttrOutput().TableInfoCell( pTextNodeInfoInner ); + if (pTextNodeInfoInner->isFirstInTable()) { - if (pTabFormat->GetBreak().GetBreak() == SvxBreak::PageBefore) - AttrOutput().PageBreakBefore(true); - } - } - } + const SwTable * pTable = pTextNodeInfoInner->getTable(); - if ( !bFlyInTable ) - { - SfxItemSet* pTmpSet = nullptr; - const sal_uInt8 nPrvNxtNd = rNode.HasPrevNextLayNode(); - - if( (ND_HAS_PREV_LAYNODE|ND_HAS_NEXT_LAYNODE ) != nPrvNxtNd ) - { - const SfxPoolItem* pItem; - if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( - RES_UL_SPACE, true, &pItem ) && - ( ( !( ND_HAS_PREV_LAYNODE & nPrvNxtNd ) && - static_cast(pItem)->GetUpper()) || - ( !( ND_HAS_NEXT_LAYNODE & nPrvNxtNd ) && - static_cast(pItem)->GetLower()) )) - { - pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); - SvxULSpaceItem aUL( *static_cast(pItem) ); - // #i25901#- consider compatibility option - if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES)) + const SwTableFormat* pTabFormat = pTable->GetFrameFormat(); + if (pTabFormat != nullptr) { - if( !(ND_HAS_PREV_LAYNODE & nPrvNxtNd )) - aUL.SetUpper( 0 ); + if (pTabFormat->GetBreak().GetBreak() == SvxBreak::PageBefore) + AttrOutput().PageBreakBefore(true); } - // #i25901# - consider compatibility option - if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS)) - { - if( !(ND_HAS_NEXT_LAYNODE & nPrvNxtNd )) - aUL.SetLower( 0 ); - } - pTmpSet->Put( aUL ); } } - bool bParaRTL = aAttrIter.IsParaRTL(); - - int nNumberLevel = -1; - if (rNode.IsNumbered()) - nNumberLevel = rNode.GetActualListLevel(); - if (nNumberLevel >= 0 && nNumberLevel < MAXLEVEL) + if ( !bFlyInTable ) { - const SwNumRule* pRule = rNode.GetNumRule(); - sal_uInt8 nLvl = static_cast< sal_uInt8 >(nNumberLevel); - const SwNumFormat* pFormat = pRule->GetNumFormat( nLvl ); - if( !pFormat ) - pFormat = &pRule->Get( nLvl ); + SfxItemSet* pTmpSet = nullptr; + const sal_uInt8 nPrvNxtNd = rNode.HasPrevNextLayNode(); - if( !pTmpSet ) - pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); - - SvxLRSpaceItem aLR(ItemGet(*pTmpSet, RES_LR_SPACE)); - // #i86652# - if ( pFormat->GetPositionAndSpaceMode() == - SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + if( (ND_HAS_PREV_LAYNODE|ND_HAS_NEXT_LAYNODE ) != nPrvNxtNd ) { - aLR.SetTextLeft( aLR.GetTextLeft() + pFormat->GetAbsLSpace() ); + const SfxPoolItem* pItem; + if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( + RES_UL_SPACE, true, &pItem ) && + ( ( !( ND_HAS_PREV_LAYNODE & nPrvNxtNd ) && + static_cast(pItem)->GetUpper()) || + ( !( ND_HAS_NEXT_LAYNODE & nPrvNxtNd ) && + static_cast(pItem)->GetLower()) )) + { + pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); + SvxULSpaceItem aUL( *static_cast(pItem) ); + // #i25901#- consider compatibility option + if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES)) + { + if( !(ND_HAS_PREV_LAYNODE & nPrvNxtNd )) + aUL.SetUpper( 0 ); + } + // #i25901# - consider compatibility option + if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS)) + { + if( !(ND_HAS_NEXT_LAYNODE & nPrvNxtNd )) + aUL.SetLower( 0 ); + } + pTmpSet->Put( aUL ); + } } - if( rNode.IsNumbered() && rNode.IsCountedInList() ) + bool bParaRTL = aAttrIter.IsParaRTL(); + + int nNumberLevel = -1; + if (rNode.IsNumbered()) + nNumberLevel = rNode.GetActualListLevel(); + if (nNumberLevel >= 0 && nNumberLevel < MAXLEVEL) { + const SwNumRule* pRule = rNode.GetNumRule(); + sal_uInt8 nLvl = static_cast< sal_uInt8 >(nNumberLevel); + const SwNumFormat* pFormat = pRule->GetNumFormat( nLvl ); + if( !pFormat ) + pFormat = &pRule->Get( nLvl ); + + if( !pTmpSet ) + pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); + + SvxLRSpaceItem aLR(ItemGet(*pTmpSet, RES_LR_SPACE)); // #i86652# if ( pFormat->GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { - if (bParaRTL) - aLR.SetTextFirstLineOfstValue(pFormat->GetAbsLSpace() - pFormat->GetFirstLineOffset()); - else - aLR.SetTextFirstLineOfst(GetWordFirstLineOffset(*pFormat)); + aLR.SetTextLeft( aLR.GetTextLeft() + pFormat->GetAbsLSpace() ); } - // correct fix for issue i94187 - if (SfxItemState::SET != - pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) + if( rNode.IsNumbered() && rNode.IsCountedInList() ) { - // List style set via paragraph style - then put it into the itemset. - // This is needed to get list level and list id exported for - // the paragraph. - pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); - - // Put indent values into the itemset in case that the list - // style is applied via paragraph style and the list level - // indent values are not applicable. + // #i86652# if ( pFormat->GetPositionAndSpaceMode() == - SvxNumberFormat::LABEL_ALIGNMENT && - !rNode.AreListLevelIndentsApplicable() ) + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + if (bParaRTL) + aLR.SetTextFirstLineOfstValue(pFormat->GetAbsLSpace() - pFormat->GetFirstLineOffset()); + else + aLR.SetTextFirstLineOfst(GetWordFirstLineOffset(*pFormat)); + } + + // correct fix for issue i94187 + if (SfxItemState::SET != + pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) { - pTmpSet->Put( aLR ); + // List style set via paragraph style - then put it into the itemset. + // This is needed to get list level and list id exported for + // the paragraph. + pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); + + // Put indent values into the itemset in case that the list + // style is applied via paragraph style and the list level + // indent values are not applicable. + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_ALIGNMENT && + !rNode.AreListLevelIndentsApplicable() ) + { + pTmpSet->Put( aLR ); + } } } - } - else - pTmpSet->ClearItem(RES_PARATR_NUMRULE); + else + pTmpSet->ClearItem(RES_PARATR_NUMRULE); - // #i86652# - if ( pFormat->GetPositionAndSpaceMode() == - SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) - { - pTmpSet->Put(aLR); + // #i86652# + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + pTmpSet->Put(aLR); - //#i21847# - SvxTabStopItem aItem( - ItemGet(*pTmpSet, RES_PARATR_TABSTOP)); - SvxTabStop aTabStop(pFormat->GetAbsLSpace()); - aItem.Insert(aTabStop); - pTmpSet->Put(aItem); + //#i21847# + SvxTabStopItem aItem( + ItemGet(*pTmpSet, RES_PARATR_TABSTOP)); + SvxTabStop aTabStop(pFormat->GetAbsLSpace()); + aItem.Insert(aTabStop); + pTmpSet->Put(aItem); - MSWordExportBase::CorrectTabStopInSet(*pTmpSet, pFormat->GetAbsLSpace()); + MSWordExportBase::CorrectTabStopInSet(*pTmpSet, pFormat->GetAbsLSpace()); + } } - } - /* - If a given para is using the SvxFrameDirection::Environment direction we - cannot export that, if it's ltr then that's ok as that is word's - default. Otherwise we must add a RTL attribute to our export list - */ - const SvxFrameDirectionItem* pItem = static_cast( - rNode.GetSwAttrSet().GetItem(RES_FRAMEDIR)); - if ( - (!pItem || pItem->GetValue() == SvxFrameDirection::Environment) && - aAttrIter.IsParaRTL() - ) - { - if ( !pTmpSet ) - pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); - - pTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_RL_TB, RES_FRAMEDIR)); - } - // move code for handling of numbered, - // but not counted paragraphs to this place. Otherwise, the paragraph - // isn't exported as numbered, but not counted, if no other attribute - // is found in - // #i44815# adjust numbering/indents for numbered paragraphs - // without number (NO_NUMLEVEL) - // #i47013# need to check rNode.GetNumRule()!=NULL as well. - if ( ! rNode.IsCountedInList() && rNode.GetNumRule()!=nullptr ) - { - // WW8 does not know numbered paragraphs without number - // (NO_NUMLEVEL). In WW8AttributeOutput::ParaNumRule(), we will export - // the RES_PARATR_NUMRULE as list-id 0, which in WW8 means - // no numbering. Here, we will adjust the indents to match - // visually. - - if ( !pTmpSet ) - pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); - - // create new LRSpace item, based on the current (if present) - const SfxPoolItem* pPoolItem = nullptr; - pTmpSet->GetItemState(RES_LR_SPACE, true, &pPoolItem); - SvxLRSpaceItem aLRSpace( - ( pPoolItem == nullptr ) - ? SvxLRSpaceItem(0, 0, 0, 0, RES_LR_SPACE) - : *static_cast( pPoolItem ) ); - - // new left margin = old left + label space - const SwNumRule* pRule = rNode.GetNumRule(); - int nLevel = rNode.GetActualListLevel(); - - if (nLevel < 0) - nLevel = 0; - - if (nLevel >= MAXLEVEL) - nLevel = MAXLEVEL - 1; - - const SwNumFormat& rNumFormat = pRule->Get( static_cast< sal_uInt16 >(nLevel) ); - - // #i86652# - if ( rNumFormat.GetPositionAndSpaceMode() == - SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + /* + If a given para is using the SvxFrameDirection::Environment direction we + cannot export that, if it's ltr then that's ok as that is word's + default. Otherwise we must add a RTL attribute to our export list + */ + const SvxFrameDirectionItem* pItem = static_cast( + rNode.GetSwAttrSet().GetItem(RES_FRAMEDIR)); + if ( + (!pItem || pItem->GetValue() == SvxFrameDirection::Environment) && + aAttrIter.IsParaRTL() + ) { - aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetAbsLSpace() ); + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + + pTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_RL_TB, RES_FRAMEDIR)); } - else + // move code for handling of numbered, + // but not counted paragraphs to this place. Otherwise, the paragraph + // isn't exported as numbered, but not counted, if no other attribute + // is found in + // #i44815# adjust numbering/indents for numbered paragraphs + // without number (NO_NUMLEVEL) + // #i47013# need to check rNode.GetNumRule()!=NULL as well. + if ( ! rNode.IsCountedInList() && rNode.GetNumRule()!=nullptr ) { - aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetIndentAt() ); - } + // WW8 does not know numbered paragraphs without number + // (NO_NUMLEVEL). In WW8AttributeOutput::ParaNumRule(), we will export + // the RES_PARATR_NUMRULE as list-id 0, which in WW8 means + // no numbering. Here, we will adjust the indents to match + // visually. - // new first line indent = 0 - // (first line indent is ignored for NO_NUMLEVEL) - if (!bParaRTL) - aLRSpace.SetTextFirstLineOfst( 0 ); + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); - // put back the new item - pTmpSet->Put( aLRSpace ); + // create new LRSpace item, based on the current (if present) + const SfxPoolItem* pPoolItem = nullptr; + pTmpSet->GetItemState(RES_LR_SPACE, true, &pPoolItem); + SvxLRSpaceItem aLRSpace( + ( pPoolItem == nullptr ) + ? SvxLRSpaceItem(0, 0, 0, 0, RES_LR_SPACE) + : *static_cast( pPoolItem ) ); - // assure that numbering rule is in - if (SfxItemState::SET != pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) - { - pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); + // new left margin = old left + label space + const SwNumRule* pRule = rNode.GetNumRule(); + int nLevel = rNode.GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& rNumFormat = pRule->Get( static_cast< sal_uInt16 >(nLevel) ); + + // #i86652# + if ( rNumFormat.GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetAbsLSpace() ); + } + else + { + aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetIndentAt() ); + } + + // new first line indent = 0 + // (first line indent is ignored for NO_NUMLEVEL) + if (!bParaRTL) + aLRSpace.SetTextFirstLineOfst( 0 ); + + // put back the new item + pTmpSet->Put( aLRSpace ); + + // assure that numbering rule is in + if (SfxItemState::SET != pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) + { + pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); + } } - } - // #i75457# - // Export page break after attribute from paragraph style. - // If page break attribute at the text node exist, an existing page - // break after at the paragraph style hasn't got to be considered. - if ( !rNode.GetpSwAttrSet() || - SfxItemState::SET != rNode.GetpSwAttrSet()->GetItemState(RES_BREAK, false) ) - { - const SvxFormatBreakItem* pBreakAtParaStyle = - &(ItemGet(rNode.GetSwAttrSet(), RES_BREAK)); - if ( pBreakAtParaStyle && - pBreakAtParaStyle->GetBreak() == SvxBreak::PageAfter ) + // #i75457# + // Export page break after attribute from paragraph style. + // If page break attribute at the text node exist, an existing page + // break after at the paragraph style hasn't got to be considered. + if ( !rNode.GetpSwAttrSet() || + SfxItemState::SET != rNode.GetpSwAttrSet()->GetItemState(RES_BREAK, false) ) { - if ( !pTmpSet ) + const SvxFormatBreakItem* pBreakAtParaStyle = + &(ItemGet(rNode.GetSwAttrSet(), RES_BREAK)); + if ( pBreakAtParaStyle && + pBreakAtParaStyle->GetBreak() == SvxBreak::PageAfter ) { - pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + if ( !pTmpSet ) + { + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + } + pTmpSet->Put( *pBreakAtParaStyle ); + } + else if( pTmpSet ) + { // Even a pagedesc item is set, the break item can be set 'NONE', + // this has to be overruled. + const SwFormatPageDesc& rPageDescAtParaStyle = + ItemGet( rNode, RES_PAGEDESC ); + if( rPageDescAtParaStyle.KnowsPageDesc() ) + pTmpSet->ClearItem( RES_BREAK ); } - pTmpSet->Put( *pBreakAtParaStyle ); - } - else if( pTmpSet ) - { // Even a pagedesc item is set, the break item can be set 'NONE', - // this has to be overruled. - const SwFormatPageDesc& rPageDescAtParaStyle = - ItemGet( rNode, RES_PAGEDESC ); - if( rPageDescAtParaStyle.KnowsPageDesc() ) - pTmpSet->ClearItem( RES_BREAK ); } - } - - // #i76520# Emulate non-splitting tables - if ( IsInTable() ) - { - const SwTableNode* pTableNode = rNode.FindTableNode(); - if ( pTableNode ) + // #i76520# Emulate non-splitting tables + if ( IsInTable() ) { - const SwTable& rTable = pTableNode->GetTable(); - const SvxFormatKeepItem& rKeep = rTable.GetFrameFormat()->GetKeep(); - const bool bKeep = rKeep.GetValue(); - const bool bDontSplit = !(bKeep || - rTable.GetFrameFormat()->GetLayoutSplit().GetValue()); + const SwTableNode* pTableNode = rNode.FindTableNode(); - if ( bKeep || bDontSplit ) + if ( pTableNode ) { - // bKeep: set keep at first paragraphs in all lines - // bDontSplit : set keep at first paragraphs in all lines except from last line - // but only for non-complex tables - const SwTableBox* pBox = rNode.GetTableBox(); - const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr; + const SwTable& rTable = pTableNode->GetTable(); + const SvxFormatKeepItem& rKeep = rTable.GetFrameFormat()->GetKeep(); + const bool bKeep = rKeep.GetValue(); + const bool bDontSplit = !(bKeep || + rTable.GetFrameFormat()->GetLayoutSplit().GetValue()); - if ( pLine && !pLine->GetUpper() ) + if ( bKeep || bDontSplit ) { - // check if box is first in that line: - if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() ) + // bKeep: set keep at first paragraphs in all lines + // bDontSplit : set keep at first paragraphs in all lines except from last line + // but only for non-complex tables + const SwTableBox* pBox = rNode.GetTableBox(); + const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr; + + if ( pLine && !pLine->GetUpper() ) { - // check if paragraph is first in that line: - if ( 1 == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) ) + // check if box is first in that line: + if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() ) { - bool bSetAtPara = false; - if ( bKeep ) - bSetAtPara = true; - else if ( bDontSplit ) + // check if paragraph is first in that line: + if ( 1 == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) ) { - // check if pLine isn't last line in table - if ( rTable.GetTabLines().size() - rTable.GetTabLines().GetPos( pLine ) != 1 ) + bool bSetAtPara = false; + if ( bKeep ) bSetAtPara = true; - } - - if ( bSetAtPara ) - { - if ( !pTmpSet ) - pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); - - const SvxFormatKeepItem aKeepItem( true, RES_KEEP ); - pTmpSet->Put( aKeepItem ); + else if ( bDontSplit ) + { + // check if pLine isn't last line in table + if ( rTable.GetTabLines().size() - rTable.GetTabLines().GetPos( pLine ) != 1 ) + bSetAtPara = true; + } + + if ( bSetAtPara ) + { + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + + const SvxFormatKeepItem aKeepItem( true, RES_KEEP ); + pTmpSet->Put( aKeepItem ); + } } } } } } } - } - const SfxItemSet* pNewSet = pTmpSet ? pTmpSet : rNode.GetpSwAttrSet(); - if( pNewSet ) - { // Para-Attrs - m_pStyAttr = &rNode.GetAnyFormatColl().GetAttrSet(); + const SfxItemSet* pNewSet = pTmpSet ? pTmpSet : rNode.GetpSwAttrSet(); + if( pNewSet ) + { // Para-Attrs + m_pStyAttr = &rNode.GetAnyFormatColl().GetAttrSet(); - const SwModify* pOldMod = m_pOutFormatNode; - m_pOutFormatNode = &rNode; + const SwModify* pOldMod = m_pOutFormatNode; + m_pOutFormatNode = &rNode; - // Pap-Attrs, so script is not necessary - OutputItemSet( *pNewSet, true, false, i18n::ScriptType::LATIN, false); + // Pap-Attrs, so script is not necessary + OutputItemSet( *pNewSet, true, false, i18n::ScriptType::LATIN, false); - m_pStyAttr = nullptr; - m_pOutFormatNode = pOldMod; + m_pStyAttr = nullptr; + m_pOutFormatNode = pOldMod; - if( pNewSet != rNode.GetpSwAttrSet() ) - delete pNewSet; + if( pNewSet != rNode.GetpSwAttrSet() ) + delete pNewSet; + } } - } - // The formatting of the paragraph marker has two sources: - // 1) If there are hints at the end of the paragraph, then use that. - // 2) Else use the RES_CHRATR_BEGIN..RES_TXTATR_END range of the paragraph - // properties. - // - // Exception: if there is a character style hint at the end of the - // paragraph only, then still go with 2), as RES_TXTATR_CHARFMT is always - // set as a hint. - SfxItemSet aParagraphMarkerProperties(m_pDoc->GetAttrPool(), svl::Items{}); - bool bCharFormatOnly = true; - if(const SwpHints* pTextAttrs = rNode.GetpSwpHints()) - { - for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + // The formatting of the paragraph marker has two sources: + // 1) If there are hints at the end of the paragraph, then use that. + // 2) Else use the RES_CHRATR_BEGIN..RES_TXTATR_END range of the paragraph + // properties. + // + // Exception: if there is a character style hint at the end of the + // paragraph only, then still go with 2), as RES_TXTATR_CHARFMT is always + // set as a hint. + SfxItemSet aParagraphMarkerProperties(m_pDoc->GetAttrPool(), svl::Items{}); + bool bCharFormatOnly = true; + if(const SwpHints* pTextAttrs = rNode.GetpSwpHints()) { - const SwTextAttr* pHt = pTextAttrs->Get(i); - const sal_Int32 startPos = pHt->GetStart(); // first Attr characters - const sal_Int32* endPos = pHt->End(); // end Attr characters - // Check if these attributes are for the last character in the paragraph - // - which means the paragraph marker. If a paragraph has 7 characters, - // then properties on character 8 are for the paragraph marker - if( endPos && (startPos == *endPos ) && (*endPos == rNode.GetText().getLength()) ) + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) { - SAL_INFO( "sw.ww8", startPos << "startPos == endPos" << *endPos); - sal_uInt16 nWhich = pHt->GetAttr().Which(); - SAL_INFO( "sw.ww8", "nWhich" << nWhich); - if (nWhich == RES_TXTATR_AUTOFMT || nWhich == RES_TXTATR_CHARFMT) - aParagraphMarkerProperties.Put(pHt->GetAttr()); - if (nWhich != RES_TXTATR_CHARFMT) - bCharFormatOnly = false; + const SwTextAttr* pHt = pTextAttrs->Get(i); + const sal_Int32 startPos = pHt->GetStart(); // first Attr characters + const sal_Int32* endPos = pHt->End(); // end Attr characters + // Check if these attributes are for the last character in the paragraph + // - which means the paragraph marker. If a paragraph has 7 characters, + // then properties on character 8 are for the paragraph marker + if( endPos && (startPos == *endPos ) && (*endPos == rNode.GetText().getLength()) ) + { + SAL_INFO( "sw.ww8", startPos << "startPos == endPos" << *endPos); + sal_uInt16 nWhich = pHt->GetAttr().Which(); + SAL_INFO( "sw.ww8", "nWhich" << nWhich); + if (nWhich == RES_TXTATR_AUTOFMT || nWhich == RES_TXTATR_CHARFMT) + aParagraphMarkerProperties.Put(pHt->GetAttr()); + if (nWhich != RES_TXTATR_CHARFMT) + bCharFormatOnly = false; + } } } - } - if (rNode.GetpSwAttrSet() && bCharFormatOnly) - { - aParagraphMarkerProperties.Put(*rNode.GetpSwAttrSet()); - } - const SwRedlineData* pRedlineParagraphMarkerDelete = AttrOutput().GetParagraphMarkerRedline( rNode, nsRedlineType_t::REDLINE_DELETE ); - const SwRedlineData* pRedlineParagraphMarkerInsert = AttrOutput().GetParagraphMarkerRedline( rNode, nsRedlineType_t::REDLINE_INSERT ); - const SwRedlineData* pParagraphRedlineData = aAttrIter.GetParagraphLevelRedline( ); - AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, pParagraphRedlineData, pRedlineParagraphMarkerDelete, pRedlineParagraphMarkerInsert); + if (rNode.GetpSwAttrSet() && bCharFormatOnly) + { + aParagraphMarkerProperties.Put(*rNode.GetpSwAttrSet()); + } + const SwRedlineData* pRedlineParagraphMarkerDelete = AttrOutput().GetParagraphMarkerRedline( rNode, nsRedlineType_t::REDLINE_DELETE ); + const SwRedlineData* pRedlineParagraphMarkerInsert = AttrOutput().GetParagraphMarkerRedline( rNode, nsRedlineType_t::REDLINE_INSERT ); + const SwRedlineData* pParagraphRedlineData = aAttrIter.GetParagraphLevelRedline( ); + AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, pParagraphRedlineData, pRedlineParagraphMarkerDelete, pRedlineParagraphMarkerInsert); - AttrOutput().EndParagraph( pTextNodeInfoInner ); + AttrOutput().EndParagraph( pTextNodeInfoInner ); + }while(*aBreakIt != rNode.GetText().getLength() && bNeedParaSplit ); SAL_INFO( "sw.ww8", "" ); } diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 8c6df1deafee..63adb9e6983d 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -2704,7 +2704,14 @@ void MSWordExportBase::WriteText() // no section breaks exported for Endnotes if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN ) - SectionBreaksAndFrames( *rNd.GetTextNode() ); + { + SwSoftPageBreakList breakList; + // if paragraph need to be split than handle section break somewhere + // else. + if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) ) + SectionBreaksAndFrames( *rNd.GetTextNode() ); + } + // output the various types of nodes if ( rNd.IsContentNode() ) diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index 22613158a7ea..57a2eb756b14 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -871,6 +871,10 @@ protected: int CollectGrfsOfBullets(); /// Write the numbering picture bullets. void BulletDefinitions(); + + bool NeedSectionBreak( const SwNode& rNd ) const; + bool NeedTextNodeSplit( const SwTextNode& rNd, std::set< sal_Int32 >& pList ) const; + std::vector m_vecBulletPic; ///< Vector to record all the graphics of bullets public: -- cgit v1.2.3