Kaydet (Commit) e7c8d0bb authored tarafından Justin Luth's avatar Justin Luth Kaydeden (comit) Andras Timar

tdf#87348 enable docx exporting linked textboxes that LO can import

Change-Id: I1f663e1a463919fc0662c94e03b801c7c58f1f5d
Reviewed-on: https://gerrit.libreoffice.org/16745Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
üst 893cc01a
......@@ -453,6 +453,7 @@ void DocxExport::ExportDocument_Impl()
WriteEmbeddings();
m_aLinkedTextboxesHelper.clear(); //final cleanup
delete m_pStyles, m_pStyles = NULL;
delete m_pSections, m_pSections = NULL;
}
......@@ -1314,6 +1315,10 @@ void DocxExport::WriteMainText()
// setup the namespaces
m_pDocumentFS->startElementNS( XML_w, XML_document, MainXmlNamespaces());
// reset the incrementing linked-textboxes chain ID before re-saving.
m_nLinkedTextboxesChainId=0;
m_aLinkedTextboxesHelper.clear();
// Write background page color
if (boost::optional<SvxBrushItem> oBrush = getBackground())
{
......@@ -1331,6 +1336,9 @@ void DocxExport::WriteMainText()
// the text
WriteText();
// clear linked textboxes since old ones can't be linked to frames in a different section (correct?)
m_aLinkedTextboxesHelper.clear();
// the last section info
m_pAttrOutput->EndParaSdtBlock();
const WW8_SepInfo *pSectionInfo = m_pSections? m_pSections->CurrentSectionInfo(): NULL;
......
......@@ -144,8 +144,6 @@ struct DocxSdrExport::Impl
sax_fastparser::FastAttributeList* m_pFlyWrapAttrList;
sax_fastparser::FastAttributeList* m_pBodyPrAttrList;
std::unique_ptr<sax_fastparser::FastAttributeList> m_pDashLineStyleAttr;
sal_Int32 m_nId ;
sal_Int32 m_nSeq ;
bool m_bDMLAndVMLDrawingOpen;
/// List of TextBoxes in this document: they are exported as part of their shape, never alone.
std::set<const SwFrameFormat*> m_aTextBoxes;
......@@ -167,8 +165,6 @@ struct DocxSdrExport::Impl
m_bFlyFrameGraphic(false),
m_pFlyWrapAttrList(0),
m_pBodyPrAttrList(0),
m_nId(0),
m_nSeq(0),
m_bDMLAndVMLDrawingOpen(false),
m_aTextBoxes(SwTextBoxHelper::findTextBoxes(m_rExport.m_pDoc)),
m_nDMLandVMLTextFrameRotation(0)
......@@ -1476,48 +1472,87 @@ void DocxSdrExport::writeDMLTextFrame(sw::Frame* pParentFrame, int nAnchorId, bo
pFS->endElementNS(XML_wps, XML_spPr);
}
//first, loop through ALL of the chained textboxes to identify a unique ID for each chain, and sequence number for each textbox in that chain.
std::map<OUString, MSWordExportBase::LinkedTextboxInfo>::iterator linkedTextboxesIter;
if( !m_pImpl->m_rExport.m_bLinkedTextboxesHelperInitialized )
{
sal_Int32 nSeq=0;
linkedTextboxesIter = m_pImpl->m_rExport.m_aLinkedTextboxesHelper.begin();
while ( linkedTextboxesIter != m_pImpl->m_rExport.m_aLinkedTextboxesHelper.end() )
{
//find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
if ( linkedTextboxesIter->second.sPrevChain.isEmpty() && !linkedTextboxesIter->second.sNextChain.isEmpty() )
{
//assign this chain a unique ID and start a new sequence
nSeq = 0;
linkedTextboxesIter->second.nId = ++m_pImpl->m_rExport.m_nLinkedTextboxesChainId;
linkedTextboxesIter->second.nSeq = nSeq;
OUString sCheckForBrokenChains = linkedTextboxesIter->first;
//follow the chain and assign the same id, and incremental sequence numbers.
std::map<OUString, MSWordExportBase::LinkedTextboxInfo>::iterator followChainIter;
followChainIter = m_pImpl->m_rExport.m_aLinkedTextboxesHelper.find(linkedTextboxesIter->second.sNextChain);
while ( followChainIter != m_pImpl->m_rExport.m_aLinkedTextboxesHelper.end() )
{
//verify that the NEXT textbox also points to me as the PREVIOUS.
// A broken link indicates a leftover remnant that can be ignored.
if( followChainIter->second.sPrevChain != sCheckForBrokenChains )
break;
followChainIter->second.nId = m_pImpl->m_rExport.m_nLinkedTextboxesChainId;
followChainIter->second.nSeq = ++nSeq;
//empty next chain indicates the end of the linked chain.
if ( followChainIter->second.sNextChain.isEmpty() )
break;
sCheckForBrokenChains = followChainIter->first;
followChainIter = m_pImpl->m_rExport.m_aLinkedTextboxesHelper.find(followChainIter->second.sNextChain);
}
}
++linkedTextboxesIter;
}
m_pImpl->m_rExport.m_bLinkedTextboxesHelperInitialized = true;
}
m_pImpl->m_rExport.m_pParentFrame = NULL;
bool skipTxBxContent = false ;
bool isTxbxLinked = false ;
/* Check if the text box is linked and then decides whether
to write the tag txbx or linkedTxbx
*/
if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("ChainPrevName") &&
xPropSetInfo->hasPropertyByName("ChainNextName"))
OUString sLinkChainName;
if ( xPropSetInfo.is() )
{
OUString sChainPrevName;
OUString sChainNextName;
xPropertySet->getPropertyValue("ChainPrevName") >>= sChainPrevName ;
xPropertySet->getPropertyValue("ChainNextName") >>= sChainNextName ;
if ( xPropSetInfo->hasPropertyByName("LinkDisplayName") )
xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
else if ( xPropSetInfo->hasPropertyByName("ChainName") )
xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
}
if (!sChainPrevName.isEmpty())
// second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
linkedTextboxesIter = m_pImpl->m_rExport.m_aLinkedTextboxesHelper.find(sLinkChainName);
if ( linkedTextboxesIter != m_pImpl->m_rExport.m_aLinkedTextboxesHelper.end() )
{
if( (linkedTextboxesIter->second.nId !=0) && (linkedTextboxesIter->second.nSeq != 0) )
{
//not the first in the chain, so write the tag as linkedTxbx
pFS->singleElementNS(XML_wps, XML_linkedTxbx,
XML_id, I32S(linkedTextboxesIter->second.nId),
XML_seq, I32S(linkedTextboxesIter->second.nSeq),
FSEND);
/* no text content should be added to this tag,
since the textbox is linked, the entire content
is written in txbx block
*/
++m_pImpl->m_nSeq ;
pFS->singleElementNS(XML_wps, XML_linkedTxbx,
XML_id, I32S(m_pImpl->m_nId),
XML_seq, I32S(m_pImpl->m_nSeq),
FSEND);
skipTxBxContent = true ;
//Text box chaining for a group of textboxes ends here,
//therefore reset the seq.
if (sChainNextName.isEmpty())
m_pImpl->m_nSeq = 0 ;
}
else if (sChainPrevName.isEmpty() && !sChainNextName.isEmpty())
else if( (linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0) )
{
/* this is the first textbox in the chaining, we add the text content
to this block*/
++m_pImpl->m_nId ;
//since the text box is linked, it needs an id.
pFS->startElementNS(XML_wps, XML_txbx,
XML_id, I32S(m_pImpl->m_nId),
XML_id, I32S(linkedTextboxesIter->second.nId),
FSEND);
isTxbxLinked = true ;
}
......
......@@ -562,6 +562,69 @@ bool SwWW8AttrIter::IsAnchorLinkedToThisNode( sal_uLong nNodePos )
FlyProcessingState SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
{
// collection point to first gather info about all of the potentially linked textboxes: to be analyzed later.
OUString sLinkChainName;
sw::FrameIter linkedTextboxesIter = maFlyIter;
while ( linkedTextboxesIter != maFlyFrms.end() )
{
uno::Reference< drawing::XShape > xShape;
sw::Frame xFrame = *linkedTextboxesIter;
const SdrObject* pSdrObj = xFrame.GetFrameFormat().FindRealSdrObject();
if( pSdrObj )
xShape = uno::Reference< drawing::XShape >(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
uno::Reference< beans::XPropertySetInfo > xPropertySetInfo;
if( xPropertySet.is() )
xPropertySetInfo = xPropertySet->getPropertySetInfo();
if( xPropertySetInfo.is() )
{
MSWordExportBase::LinkedTextboxInfo aLinkedTextboxInfo = MSWordExportBase::LinkedTextboxInfo();
if( xPropertySetInfo->hasPropertyByName("LinkDisplayName") )
xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
else if( xPropertySetInfo->hasPropertyByName("ChainName") )
xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
if( xPropertySetInfo->hasPropertyByName("ChainNextName") )
xPropertySet->getPropertyValue("ChainNextName") >>= aLinkedTextboxInfo.sNextChain;
if( xPropertySetInfo->hasPropertyByName("ChainPrevName") )
xPropertySet->getPropertyValue("ChainPrevName") >>= aLinkedTextboxInfo.sPrevChain;
//collect a list of linked textboxes: those with a NEXT or PREVIOUS link
if( !aLinkedTextboxInfo.sNextChain.isEmpty() || !aLinkedTextboxInfo.sPrevChain.isEmpty() )
{
assert( !sLinkChainName.isEmpty() );
//there are many discarded duplicates in documents - no duplicates allowed in the list, so try to find the real one.
//if this LinkDisplayName/ChainName already exists on a different shape...
// the earlier processed duplicates are thrown out unless this one can be proved as bad. (last processed duplicate usually is stored)
std::map<OUString,MSWordExportBase::LinkedTextboxInfo>::iterator linkFinder;
linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(sLinkChainName);
if( linkFinder != m_rExport.m_aLinkedTextboxesHelper.end() )
{
//If my NEXT/PREV targets have already been discovered, but don't match me, then assume I'm an abandoned remnant
// (this logic fails if both me and one of my links are duplicated, and the remnants were added first.)
linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sNextChain);
if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sPrevChain != sLinkChainName) )
{
++linkedTextboxesIter;
break;
}
linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sPrevChain);
if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sNextChain != sLinkChainName) )
{
++linkedTextboxesIter;
break;
}
}
m_rExport.m_bLinkedTextboxesHelperInitialized = false;
m_rExport.m_aLinkedTextboxesHelper[sLinkChainName] = aLinkedTextboxInfo;
}
}
++linkedTextboxesIter;
}
/*
#i2916#
May have an anchored graphic to be placed, loop through sorted array
......
......@@ -1728,6 +1728,9 @@ void MSWordExportBase::WriteSpecialText( sal_uLong nStart, sal_uLong nEnd, sal_u
// bOutKF was setted / stored in WriteKF1
SetCurPam(nStart, nEnd);
// clear linked textboxes since old ones can't be linked to frames in this section
m_aLinkedTextboxesHelper.clear();
WriteText();
m_bOutPageDescs = bOldPageDescs;
......
......@@ -497,6 +497,18 @@ public:
WW8_WrPlcAnnotations* m_pAtn;
WW8_WrPlcTextBoxes *m_pTextBxs, *m_pHFTextBxs;
struct LinkedTextboxInfo //help analyze textbox flow links
{
sal_Int32 nId;
sal_Int32 nSeq;
OUString sNextChain;
OUString sPrevChain;
LinkedTextboxInfo(): nId(0), nSeq(0) {}
};
std::map<OUString,LinkedTextboxInfo> m_aLinkedTextboxesHelper;
bool m_bLinkedTextboxesHelperInitialized = false;
sal_Int32 m_nLinkedTextboxesChainId=0;
const sw::Frame *m_pParentFrame; // If set we are exporting content inside
// a frame, e.g. a graphic node
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment