summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Luth <justin_luth@sil.org>2015-07-03 20:45:16 +0300
committerMiklos Vajna <vmiklos@collabora.co.uk>2015-07-07 07:17:10 +0000
commit2a51dd6aca55a92d8751b67cdf7a5a6ab8c38237 (patch)
tree457d0581d7e97762d43fbb3c787cb0ef0d9ff954
parentff3a1d983acc04715e16373c1ca8cc64dafdccd0 (diff)
tdf#87348 enable docx exporting linked textboxes that LO can import
Change-Id: I1f663e1a463919fc0662c94e03b801c7c58f1f5d Reviewed-on: https://gerrit.libreoffice.org/16745 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r--sw/source/filter/ww8/docxexport.cxx8
-rw-r--r--sw/source/filter/ww8/docxsdrexport.cxx91
-rw-r--r--sw/source/filter/ww8/wrtw8nds.cxx63
-rw-r--r--sw/source/filter/ww8/wrtww8.cxx3
-rw-r--r--sw/source/filter/ww8/wrtww8.hxx12
5 files changed, 149 insertions, 28 deletions
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 855be5730b10..58d9d0b61227 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -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;
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 13c7bd0ab1e4..c6207a509bb7 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -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 ;
}
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx
index a3779e4f08e3..ac7d9c31c027 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -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
diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx
index 01a95cf8f837..bbf819a898c1 100644
--- a/sw/source/filter/ww8/wrtww8.cxx
+++ b/sw/source/filter/ww8/wrtww8.cxx
@@ -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;
diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx
index 957ab283dbd0..d2cb89d53144 100644
--- a/sw/source/filter/ww8/wrtww8.hxx
+++ b/sw/source/filter/ww8/wrtww8.hxx
@@ -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