fdo#78333 : SdtContent and a Shape overlapping causes corruption

- Normally if there is a case where text/shape is overlapped with (another)
   shape then LO used to write the text and the AlternateContent in the same run.
- This is supported in MSO and there is no visual difference.
- But in case if the SdtContent(with text) is overlapped with the Shape then LO
   processes sdtContent as a text and ends up putting the alternateContent and the
   text in a single run. Ultimately it includes the entire run in a SdtContent,
   which is incorrect.
- The fix checks for the aforementioned scenario and puts them in a different run
   and also restricts the sdtContent being written in an invalid AlternateContent.

Change-Id: I36f4cdb1b583523dd8f717ae094bdf09c7a61f62
Reviewed-on: https://gerrit.libreoffice.org/9374
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
This commit is contained in:
Umesh Kadam 2014-05-15 23:02:34 +05:30 committed by Miklos Vajna
parent 4c05911b7e
commit 80fd9fb720
7 changed files with 174 additions and 10 deletions

View File

@ -2052,6 +2052,15 @@ DECLARE_OOXMLEXPORT_TEST(testFileOpenInputOutputError,"floatingtbl_with_formula.
assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:pStyle", "val", "Normal");
}
DECLARE_OOXMLEXPORT_TEST(testSdtAndShapeOverlapping,"ShapeOverlappingWithSdt.docx")
{
xmlDocPtr pXmlDoc = parseExport("word/document.xml");
if (!pXmlDoc)
return;
assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r[1]/mc:AlternateContent");
assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt[1]/w:sdtContent[1]/w:r[1]/w:t[1]");
}
DECLARE_OOXMLEXPORT_TEST(testRelorientation, "relorientation.docx")
{
uno::Reference<drawing::XShape> xShape = getShape(1);

View File

@ -309,6 +309,15 @@ public:
/// Has different headers/footers for the title page.
virtual void SectionTitlePage() = 0;
/// If the node has an anchor linked.
virtual void SetAnchorIsLinkedToNode( bool /*bAnchorLinkedToNode*/){};
/// Is processing of fly postponed ?
virtual bool IsFlyProcessingPostponed(){ return false; };
/// Reset the flag for FlyProcessing
virtual void ResetFlyProcessingFlag(){};
/// Description of the page borders.
virtual void SectionPageBorders( const SwFrmFmt* pFmt, const SwFrmFmt* pFirstPageFmt ) = 0;

View File

@ -287,6 +287,20 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
m_bIsFirstParagraph = false;
}
static void lcl_deleteAndResetTheLists( ::sax_fastparser::FastAttributeList* &pSdtPrTokenChildren, ::sax_fastparser::FastAttributeList* &pSdtPrDataBindingAttrs)
{
if( pSdtPrTokenChildren )
{
delete pSdtPrTokenChildren ;
pSdtPrTokenChildren = NULL;
}
if( pSdtPrDataBindingAttrs )
{
delete pSdtPrDataBindingAttrs;
pSdtPrDataBindingAttrs = NULL;
}
}
void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
{
// write the paragraph properties + the run, already in the correct order
@ -382,8 +396,16 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT
}
m_pSerializer->endElementNS( XML_w, XML_p );
if( !m_bAnchorLinkedToNode )
WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs );
else
{
//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_nParagraphSdtPrToken = 0;
lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs );
}
WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs );
//sdtcontent is written so Set m_bParagraphHasDrawing to false
m_rExport.SdrExporter().setParagraphHasDrawing( false );
m_bRunTextIsOn = false;
@ -701,6 +723,21 @@ void DocxAttributeOutput::EndParagraphProperties( const SfxItemSet* pParagraphMa
m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
}
void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
{
m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
}
void DocxAttributeOutput::ResetFlyProcessingFlag()
{
m_bPostponedProcessingFly = false ;
}
bool DocxAttributeOutput::IsFlyProcessingPostponed()
{
return m_bPostponedProcessingFly;
}
void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSingleEmptyRun*/ )
{
// Don't start redline data here, possibly there is a hyperlink later, and
@ -858,7 +895,15 @@ void DocxAttributeOutput::EndRun()
EndRedline( m_pRedlineData );
// enclose in a sdt block, if necessary
WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs );
if ( !m_bAnchorLinkedToNode )
WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs );
else
{
//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_nRunSdtPrToken = 0;
lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs );
}
m_pSerializer->mergeTopMarks();
WritePostponedMath();
@ -4270,9 +4315,13 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
if ( pGrfNode )
{
if( m_postponedGraphic == NULL )
{
m_bPostponedProcessingFly = false ;
FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), 0, 0, pSdrObj);
}
else // we are writing out attributes, but w:drawing should not be inside w:rPr,
{ // so write it out later
m_bPostponedProcessingFly = true ;
m_postponedGraphic->push_back( PostponedGraphic( pGrfNode, rFrame.GetLayoutSize(), 0, 0, pSdrObj));
}
}
@ -4286,9 +4335,13 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
if ( IsDiagram( pSdrObj ) )
{
if ( m_postponedDiagram == NULL )
{
m_bPostponedProcessingFly = false ;
m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrmFmt(), m_anchorId++);
}
else // we are writing out attributes, but w:drawing should not be inside w:rPr,
{ // so write it out later
m_bPostponedProcessingFly = true ;
m_postponedDiagram->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrmFmt()) ));
}
}
@ -4302,6 +4355,8 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrmFmt(), m_anchorId++);
else
m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrmFmt(), rNdTopLeft, m_anchorId++);
m_bPostponedProcessingFly = false ;
}
// IsAlternateContentChoiceOpen() : check is to ensure that only one object is getting added. Without this check, plus one obejct gets added
// m_bParagraphFrameOpen : Check if the frame is open.
@ -4311,8 +4366,11 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
m_postponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrmFmt()), &rNdTopLeft));
}
else
{
// we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later
m_bPostponedProcessingFly = true ;
m_postponedDMLDrawing->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrmFmt()), &rNdTopLeft));
}
}
}
}
@ -4335,7 +4393,10 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
}
if( !bDuplicate )
{
m_bPostponedProcessingFly = true ;
m_aFramesOfParagraph.push_back(sw::Frame(rFrame));
}
}
break;
case sw::Frame::eOle:
@ -4347,6 +4408,7 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
SwNodeIndex aIdx(*rFrmFmt.GetCntnt().GetCntntIdx(), 1);
SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrmFmt*>( &rFrmFmt ));
m_bPostponedProcessingFly = false ;
}
}
break;
@ -4354,6 +4416,7 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
{
const SdrObject* pObject = rFrame.GetFrmFmt().FindRealSdrObject();
m_aPostponedFormControls.push_back(pObject);
m_bPostponedProcessingFly = true ;
}
break;
default:
@ -7432,6 +7495,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri
m_bOpenedSectPr( false ),
m_bRunTextIsOn( false ),
m_bWritingHeaderFooter( false ),
m_bAnchorLinkedToNode(false),
m_sFieldBkm( ),
m_nNextBookmarkId( 0 ),
m_nNextAnnotationMarkId( 0 ),
@ -7440,6 +7504,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri
m_bParagraphFrameOpen( false ),
m_bIsFirstParagraph( true ),
m_bAlternateContentChoiceOpen( false ),
m_bPostponedProcessingFly( false ),
m_nColBreakStatus( COLBRK_NONE ),
m_nTextFrameLevel( 0 ),
m_closeHyperlinkInThisRun( false ),

View File

@ -210,6 +210,10 @@ public:
/// End of the tag that encloses the run.
void EndRedline( const SwRedlineData * pRedlineData );
virtual void SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode = false ) SAL_OVERRIDE;
virtual bool IsFlyProcessingPostponed() SAL_OVERRIDE;
virtual void ResetFlyProcessingFlag() SAL_OVERRIDE;
virtual void FormatDrop( const SwTxtNode& rNode, const SwFmtDrop& rSwFmtDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) SAL_OVERRIDE;
/// Output style.
@ -726,6 +730,7 @@ private:
/// Flag indicating that the header \ footer are being written
bool m_bWritingHeaderFooter;
bool m_bAnchorLinkedToNode;
/// Field data to remember in the text run
std::vector< FieldInfos > m_Fields;
@ -767,6 +772,7 @@ private:
bool m_bParagraphFrameOpen;
bool m_bIsFirstParagraph;
bool m_bAlternateContentChoiceOpen;
bool m_bPostponedProcessingFly;
// Remember that a column break has to be opened at the
// beginning of the next paragraph

View File

@ -540,7 +540,26 @@ bool SwWW8AttrIter::IsWatermarkFrame()
return false;
}
void SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
bool SwWW8AttrIter::IsAnchorLinkedToThisNode( sal_uLong nNodePos )
{
sw::FrameIter aTmpFlyIter = maFlyIter ;
while ( aTmpFlyIter != maFlyFrms.end() )
{
const SwPosition &rAnchor = maFlyIter->GetPosition();
sal_uLong nAnchorPos = rAnchor.nNode.GetIndex();
/* if current node position and the anchor position are the same
then the frame anchor is linked to this node
*/
if ( nAnchorPos == nNodePos )
return true ;
++aTmpFlyIter;
}
return false ;
}
sal_Int16 SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
{
/*
#i2916#
@ -553,7 +572,7 @@ void SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
const sal_Int32 nPos = rAnchor.nContent.GetIndex();
if ( nPos != nSwPos )
break;
return FLY_NOT_PROCESSED ; //We havent processed the fly
const SdrObject* pSdrObj = maFlyIter->GetFrmFmt().FindRealSdrObject();
@ -585,6 +604,7 @@ void SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
}
++maFlyIter;
}
return ( m_rExport.AttrOutput().IsFlyProcessingPostponed() ? FLY_POSTPONED : FLY_PROCESSED ) ;
}
bool SwWW8AttrIter::IsTxtAttr( sal_Int32 nSwPos )
@ -2017,6 +2037,7 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
sal_Int32 const nEnd = aStr.getLength();
bool bRedlineAtEnd = false;
sal_Int32 nOpenAttrWithRange = 0;
OUString aStringForImage("\001");
ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner;
if ( pTextNodeInfo.get() != NULL )
@ -2024,18 +2045,33 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
do {
const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( nAktPos );
sal_Int16 nStateOfFlyFrame = 0;
bool bPostponeWritingText = false ;
OUString aSavedSnippet ;
sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nAktPos );
// Is this the only run in this paragraph and it's empty?
bool bSingleEmptyRun = nAktPos == 0 && nNextAttr == 0;
AttrOutput().StartRun( pRedlineData, bSingleEmptyRun );
if( nTxtTyp == TXT_FTN || nTxtTyp == TXT_EDN )
AttrOutput().FootnoteEndnoteRefTag();
if( nNextAttr > nEnd )
nNextAttr = nEnd;
aAttrIter.OutFlys( 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.IsTxtNode() && aStr != aStringForImage && aStr != "" &&
!rNode.GetFlyFmt() && aAttrIter.IsAnchorLinkedToThisNode(rNode.GetIndex()))
bPostponeWritingText = true ;
nStateOfFlyFrame = aAttrIter.OutFlys( nAktPos );
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 );
@ -2152,7 +2188,17 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
if ( aSnippet[0] != 0x09 )
aSnippet = OUString( 0x09 ) + aSnippet;
}
AttrOutput().RunText( aSnippet, eChrSet );
if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) )
{
bPostponeWritingText = true ;
aSavedSnippet = aSnippet ;
}
else
{
bPostponeWritingText = false ;
AttrOutput().RunText( aSnippet, eChrSet );
}
}
if ( aAttrIter.IsDropCap( nNextAttr ) )
@ -2179,7 +2225,7 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
else
{
// insert final graphic anchors if any before CR
aAttrIter.OutFlys( nEnd );
nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
// insert final bookmarks if any before CR and after flys
AppendBookmarks( rNode, nEnd, 1 );
AppendAnnotationMarks( rNode, nEnd, 1 );
@ -2228,7 +2274,7 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
if ( bTxtAtr || bAttrWithRange || bRedlineAtEnd )
{
// insert final graphic anchors if any before CR
aAttrIter.OutFlys( nEnd );
nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
// insert final bookmarks if any before CR and after flys
AppendBookmarks( rNode, nEnd, 1 );
AppendAnnotationMarks( rNode, nEnd, 1 );
@ -2256,7 +2302,25 @@ void MSWordExportBase::OutputTextNode( const SwTxtNode& rNode )
AttrOutput().WritePostitFieldReference();
AttrOutput().EndRun();
if( bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame )
{
AttrOutput().EndRun();
//write the postponed text run
bPostponeWritingText = false ;
AttrOutput().StartRun( pRedlineData, bSingleEmptyRun );
AttrOutput().SetAnchorIsLinkedToNode( false );
AttrOutput().ResetFlyProcessingFlag();
if (0 != nEnd)
{
AttrOutput().StartRunProperties();
aAttrIter.OutAttr( nAktPos );
AttrOutput().EndRunProperties( pRedlineData );
}
AttrOutput().RunText( aSavedSnippet, eChrSet );
AttrOutput().EndRun();
}
else
AttrOutput().EndRun();
nAktPos = nNextAttr;
UpdatePosition( &aAttrIter, nAktPos, nEnd );

View File

@ -145,6 +145,16 @@ enum TxtTypes //enums for TextTypes
TXT_EDN = 4, TXT_ATN = 5, TXT_TXTBOX = 6, TXT_HFTXTBOX= 7
};
/**
enum to state the present state of the fly
*/
enum FlyProcessingState
{
FLY_PROCESSED,
FLY_POSTPONED,
FLY_NOT_PROCESSED
};
struct WW8_SepInfo
{
const SwPageDesc* pPageDesc;
@ -1533,7 +1543,7 @@ public:
int OutAttrWithRange(sal_Int32 nPos);
const SwRedlineData* GetParagraphLevelRedline( );
const SwRedlineData* GetRunLevelRedline( sal_Int32 nPos );
void OutFlys(sal_Int32 nSwPos);
sal_Int16 OutFlys(sal_Int32 nSwPos);
sal_Int32 WhereNext() const { return nAktSwPos; }
sal_uInt16 GetScript() const { return mnScript; }
@ -1545,6 +1555,7 @@ public:
const SwFmtDrop& GetSwFmtDrop() const { return mrSwFmtDrop; }
bool IsWatermarkFrame();
bool IsAnchorLinkedToThisNode( sal_uLong nNodePos );
};
/// Class to collect and output the styles table.