1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <unotools/tempfile.hxx>
22 #include <svl/stritem.hxx>
23 #include <svl/eitem.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <sfx2/docfilt.hxx>
26 #include <sfx2/fcontnr.hxx>
27 #include <sfx2/bindings.hxx>
28 #include <sfx2/request.hxx>
29 #include <sfx2/sfxsids.hrc>
30 #include <sfx2/viewfrm.hxx>
31 #include <tools/datetime.hxx>
32 #include <fmtinfmt.hxx>
33 #include <fmtanchr.hxx>
35 #include <IDocumentUndoRedo.hxx>
36 #include <IDocumentRedlineAccess.hxx>
37 #include <DocumentSettingManager.hxx>
38 #include <DocumentContentOperationsManager.hxx>
39 #include <IDocumentLayoutAccess.hxx>
44 #include <section.hxx>
46 #include <iodetect.hxx>
47 #include <frameformats.hxx>
49 #include <com/sun/star/uno/Reference.h>
50 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
51 #include <com/sun/star/document/XDocumentProperties.hpp>
52 #include <com/sun/star/frame/XModel.hpp>
54 using namespace ::com::sun::star
;
60 SPLITDOC_TO_GLOBALDOC
,
66 bool SwDoc::GenerateGlobalDoc( const OUString
& rPath
,
67 const SwTextFormatColl
* pSplitColl
)
69 return SplitDoc( SPLITDOC_TO_GLOBALDOC
, rPath
, false, pSplitColl
);
72 bool SwDoc::GenerateGlobalDoc( const OUString
& rPath
, int nOutlineLevel
)
74 return SplitDoc( SPLITDOC_TO_GLOBALDOC
, rPath
, true, nullptr, nOutlineLevel
);
77 bool SwDoc::GenerateHTMLDoc( const OUString
& rPath
, int nOutlineLevel
)
79 return SplitDoc( SPLITDOC_TO_HTML
, rPath
, true, nullptr, nOutlineLevel
);
82 bool SwDoc::GenerateHTMLDoc( const OUString
& rPath
,
83 const SwTextFormatColl
* pSplitColl
)
85 return SplitDoc( SPLITDOC_TO_HTML
, rPath
, false, pSplitColl
);
88 // two helpers for outline mode
89 static SwNode
* GetStartNode( SwOutlineNodes
const * pOutlNds
, int nOutlineLevel
, SwOutlineNodes::size_type
* nOutl
)
91 for( ; *nOutl
< pOutlNds
->size(); ++(*nOutl
) )
93 SwNode
* pNd
= (*pOutlNds
)[ *nOutl
];
94 if( pNd
->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel
&& !pNd
->FindTableNode() )
103 static SwNode
* GetEndNode( SwOutlineNodes
const * pOutlNds
, int nOutlineLevel
, SwOutlineNodes::size_type
* nOutl
)
107 for( ++(*nOutl
); (*nOutl
) < pOutlNds
->size(); ++(*nOutl
) )
109 pNd
= (*pOutlNds
)[ *nOutl
];
111 const int nLevel
= pNd
->GetTextNode()->GetAttrOutlineLevel();
113 if( ( 0 < nLevel
&& nLevel
<= nOutlineLevel
) &&
114 !pNd
->FindTableNode() )
122 // two helpers for collection mode
123 static SwNode
* GetStartNode( const SwOutlineNodes
* pOutlNds
, const SwTextFormatColl
* pSplitColl
, SwOutlineNodes::size_type
* nOutl
)
125 for( ; *nOutl
< pOutlNds
->size(); ++(*nOutl
) )
127 SwNode
* pNd
= (*pOutlNds
)[ *nOutl
];
128 if( pNd
->GetTextNode()->GetTextColl() == pSplitColl
&&
129 !pNd
->FindTableNode() )
137 static SwNode
* GetEndNode( const SwOutlineNodes
* pOutlNds
, const SwTextFormatColl
* pSplitColl
, SwOutlineNodes::size_type
* nOutl
)
141 for( ++(*nOutl
); *nOutl
< pOutlNds
->size(); ++(*nOutl
) )
143 pNd
= (*pOutlNds
)[ *nOutl
];
144 SwTextFormatColl
* pTColl
= pNd
->GetTextNode()->GetTextColl();
146 if( ( pTColl
== pSplitColl
||
147 ( pSplitColl
->GetAttrOutlineLevel() > 0 &&
148 pTColl
->GetAttrOutlineLevel() > 0 &&
149 pTColl
->GetAttrOutlineLevel() <
150 pSplitColl
->GetAttrOutlineLevel() )) &&
151 !pNd
->FindTableNode() )
159 bool SwDoc::SplitDoc( sal_uInt16 eDocType
, const OUString
& rPath
, bool bOutline
, const SwTextFormatColl
* pSplitColl
, int nOutlineLevel
)
161 // Iterate over all the template's Nodes, creating an own
162 // document for every single one and replace linked sections (GlobalDoc) for links (HTML).
163 // Finally, we save this document as a GlobalDoc/HTMLDoc.
164 if( !mpDocShell
|| !mpDocShell
->GetMedium() ||
165 ( SPLITDOC_TO_GLOBALDOC
== eDocType
&& GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT
) ) )
168 SwOutlineNodes::size_type nOutl
= 0;
169 SwOutlineNodes
* pOutlNds
= const_cast<SwOutlineNodes
*>(&GetNodes().GetOutLineNds());
170 std::unique_ptr
<SwOutlineNodes
> xTmpOutlNds
;
176 // If it isn't an OutlineNumbering, then use an own array and collect the Nodes.
177 if( pSplitColl
->GetAttrOutlineLevel() == 0 )
179 xTmpOutlNds
.reset(new SwOutlineNodes
);
180 pOutlNds
= xTmpOutlNds
.get();
181 SwIterator
<SwTextNode
,SwFormatColl
> aIter( *pSplitColl
);
182 for( SwTextNode
* pTNd
= aIter
.First(); pTNd
; pTNd
= aIter
.Next() )
183 if( pTNd
->GetNodes().IsDocNodes() )
184 pOutlNds
->insert( pTNd
);
186 if( pOutlNds
->empty() )
192 // Look for the 1st level OutlineTemplate
193 const SwTextFormatColls
& rFormatColls
=*GetTextFormatColls();
194 for( SwTextFormatColls::size_type n
= rFormatColls
.size(); n
; )
195 if ( rFormatColls
[ --n
]->GetAttrOutlineLevel() == 1 )
197 pSplitColl
= rFormatColls
[ n
];
206 std::shared_ptr
<const SfxFilter
> pFilter
;
209 case SPLITDOC_TO_HTML
:
210 pFilter
= SwIoSystem::GetFilterOfFormat(u
"HTML");
214 pFilter
= SwIoSystem::GetFilterOfFormat(FILTER_XML
);
215 eDocType
= SPLITDOC_TO_GLOBALDOC
;
222 // Deactivate Undo/Redline in any case
223 GetIDocumentUndoRedo().DoUndo(false);
224 getIDocumentRedlineAccess().SetRedlineFlags_intern( getIDocumentRedlineAccess().GetRedlineFlags() & ~RedlineFlags::On
);
226 OUString sExt
= pFilter
->GetSuffixes().getToken(0, ',');
233 if( '.' != sExt
[ 0 ] )
239 INetURLObject
aEntry(rPath
);
240 OUString
sLeading(aEntry
.GetBase());
241 aEntry
.removeSegment();
242 OUString sPath
= aEntry
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
243 utl::TempFileNamed
aTemp(sLeading
, true, sExt
, &sPath
);
244 aTemp
.EnableKillingFile();
246 DateTime
aTmplDate( DateTime::SYSTEM
);
248 tools::Time
a2Min( 0 ); a2Min
.SetMin( 2 );
252 // Skip all invalid ones
253 while( nOutl
< pOutlNds
->size() &&
254 (*pOutlNds
)[ nOutl
]->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
259 pStartNd
= GetStartNode( pOutlNds
, nOutlineLevel
, &nOutl
);
261 pStartNd
= GetStartNode( pOutlNds
, pSplitColl
, &nOutl
);
267 pEndNd
= GetEndNode( pOutlNds
, nOutlineLevel
, &nOutl
);
269 pEndNd
= GetEndNode( pOutlNds
, pSplitColl
, &nOutl
);
270 SwNodeIndex
aEndIdx( pEndNd
? *pEndNd
271 : GetNodes().GetEndOfContent() );
273 // Write out the Nodes completely
275 if( pStartNd
->GetIndex() + 1 < aEndIdx
.GetIndex() )
277 SfxObjectShellLock
xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL
));
278 if( xDocSh
->DoInitNew() )
280 SwDoc
* pDoc
= static_cast<SwDocShell
*>(&xDocSh
)->GetDoc();
282 uno::Reference
<document::XDocumentPropertiesSupplier
> xDPS(
283 static_cast<SwDocShell
*>(&xDocSh
)->GetModel(),
284 uno::UNO_QUERY_THROW
);
285 uno::Reference
<document::XDocumentProperties
> xDocProps(
286 xDPS
->getDocumentProperties());
287 OSL_ENSURE(xDocProps
.is(), "Doc has no DocumentProperties");
288 // the GlobalDoc is the template
289 xDocProps
->setTemplateName(OUString());
290 ::util::DateTime uDT
= aTmplDate
.GetUNODateTime();
291 xDocProps
->setTemplateDate(uDT
);
292 xDocProps
->setTemplateURL(rPath
);
293 // Set the new doc's title to the text of the "split para".
294 // If the current doc has a title, insert it at the begin.
295 OUString
sTitle( xDocProps
->getTitle() );
296 if (!sTitle
.isEmpty())
298 sTitle
+= pStartNd
->GetTextNode()->GetExpandText(nullptr);
299 xDocProps
->setTitle( sTitle
);
302 pDoc
->ReplaceStyles( *this );
304 // Take over chapter numbering
306 pDoc
->SetOutlineNumRule( *mpOutlineRule
);
308 SwNodeRange
aRg( *pStartNd
, SwNodeOffset(0), aEndIdx
.GetNode() );
309 GetDocumentContentOperationsManager().CopyWithFlyInFly(
310 aRg
, pDoc
->GetNodes().GetEndOfContent(), nullptr, false, false);
312 // Delete the initial TextNode
313 SwNodeIndex
aIdx( pDoc
->GetNodes().GetEndOfExtras(), 2 );
314 if( aIdx
.GetIndex() + 1 !=
315 pDoc
->GetNodes().GetEndOfContent().GetIndex() )
316 pDoc
->GetNodes().Delete( aIdx
);
318 sFileName
= utl::CreateTempURL(sLeading
, true, sExt
, &sPath
);
319 SfxMedium
* pTmpMed
= new SfxMedium( sFileName
,
320 StreamMode::STD_READWRITE
);
321 pTmpMed
->SetFilter( pFilter
);
323 // We need to have a Layout for the HTMLFilter, so that
324 // TextFrames/Controls/OLE objects can be exported correctly as graphics.
325 if( SPLITDOC_TO_HTML
== eDocType
&&
326 !pDoc
->GetSpzFrameFormats()->empty() )
328 SfxViewFrame::LoadHiddenDocument( *xDocSh
, SFX_INTERFACE_NONE
);
330 xDocSh
->DoSaveAs( *pTmpMed
);
331 xDocSh
->DoSaveCompleted( pTmpMed
);
333 // do not insert a FileLinkSection in case of error
334 if( xDocSh
->GetError() )
340 // We can now insert the section
341 if( !sFileName
.isEmpty() )
345 case SPLITDOC_TO_HTML
:
347 // Delete all nodes in the section and, in the "start node",
348 // set the Link to the saved document.
349 SwNodeOffset nNodeDiff
= aEndIdx
.GetIndex() -
350 pStartNd
->GetIndex() - 1;
353 SwPaM
aTmp( *pStartNd
, aEndIdx
.GetNode(), SwNodeOffset(1), SwNodeOffset(-1) );
354 SwNodeIndex
aSIdx( aTmp
.GetMark()->GetNode() );
355 SwNodeIndex
aEIdx( aTmp
.GetPoint()->GetNode() );
357 // Try to move past the end
358 if( !aTmp
.Move( fnMoveForward
, GoInNode
) )
360 // well then, back to the beginning
362 if( !aTmp
.Move( fnMoveBackward
, GoInNode
))
364 OSL_FAIL( "no more Nodes!" );
367 // Move Bookmarks and so forth
368 CorrAbs( aSIdx
, aEIdx
, *aTmp
.GetPoint(), true);
370 // If FlyFrames are still around, delete these too
371 auto& rSpzs
= *GetSpzFrameFormats();
372 for(sw::FrameFormats
<sw::SpzFrameFormat
*>::size_type n
= 0; n
< GetSpzFrameFormats()->size(); ++n
)
374 auto pFly
= rSpzs
[n
];
375 const SwFormatAnchor
* pAnchor
= &pFly
->GetAnchor();
376 SwNode
const*const pAnchorNode
=
377 pAnchor
->GetAnchorNode();
379 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
380 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
381 aSIdx
<= *pAnchorNode
&&
382 *pAnchorNode
< aEIdx
.GetNode() )
384 getIDocumentLayoutAccess().DelLayoutFormat( pFly
);
389 GetNodes().Delete( aSIdx
, nNodeDiff
);
392 // set the link in the StartNode
393 SwFormatINetFormat
aINet( sFileName
, OUString() );
394 SwTextNode
* pTNd
= pStartNd
->GetTextNode();
395 pTNd
->InsertItem(aINet
, 0, pTNd
->GetText().getLength());
397 // If the link cannot be found anymore,
398 // it has to be a bug!
399 if( !pOutlNds
->Seek_Entry( pStartNd
, &nOutl
))
407 const OUString
sNm(INetURLObject(sFileName
).GetLastName());
408 SwSectionData
aSectData( SectionType::FileLink
,
409 GetUniqueSectionName( &sNm
));
410 SwSectionFormat
* pFormat
= MakeSectionFormat();
411 aSectData
.SetLinkFileName(sFileName
);
412 aSectData
.SetProtectFlag(true);
414 --aEndIdx
; // in the InsertSection the end is inclusive
415 while( aEndIdx
.GetNode().IsStartNode() )
418 // If any Section ends or starts in the new sectionrange,
419 // they must end or start before or after the range!
420 SwSectionNode
* pSectNd
= pStartNd
->FindSectionNode();
421 while( pSectNd
&& pSectNd
->EndOfSectionIndex()
422 <= aEndIdx
.GetIndex() )
424 const SwNode
* pSectEnd
= pSectNd
->EndOfSectionNode();
425 if( pSectNd
->GetIndex() + 1 ==
426 pStartNd
->GetIndex() )
428 bool bMvIdx
= aEndIdx
== *pSectEnd
;
429 DelSectionFormat( pSectNd
->GetSection().GetFormat() );
435 SwNodeRange
aRg( *pStartNd
, *pSectEnd
);
436 SwNodeIndex
aIdx( *pSectEnd
, 1 );
437 GetNodes().MoveNodes( aRg
, GetNodes(), aIdx
.GetNode() );
439 pSectNd
= pStartNd
->FindSectionNode();
442 pSectNd
= aEndIdx
.GetNode().FindSectionNode();
443 while( pSectNd
&& pSectNd
->GetIndex() >
444 pStartNd
->GetIndex() )
446 // #i15712# don't attempt to split sections if
447 // they are fully enclosed in [pSectNd,aEndIdx].
448 if( aEndIdx
< pSectNd
->EndOfSectionIndex() )
450 SwNodeRange
aRg( *pSectNd
, SwNodeOffset(1), aEndIdx
.GetNode(), SwNodeOffset(1) );
451 GetNodes().MoveNodes( aRg
, GetNodes(), *pSectNd
);
454 pSectNd
= pStartNd
->FindSectionNode();
458 // Ensure order of start and end of section is sane.
459 SwNodeIndex
aStartIdx(*pStartNd
);
461 if (aEndIdx
>= aStartIdx
)
463 pSectNd
= GetNodes().InsertTextSection(aStartIdx
.GetNode(),
464 *pFormat
, aSectData
, nullptr, &aEndIdx
.GetNode(), false);
468 pSectNd
= GetNodes().InsertTextSection(aEndIdx
.GetNode(),
469 *pFormat
, aSectData
, nullptr, &aStartIdx
.GetNode(), false);
473 pSectNd
->GetSection().CreateLink( LinkCreateType::Connect
);
485 case SPLITDOC_TO_HTML
:
486 if( GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT
) )
488 // save all remaining sections
489 while( !GetSections().empty() )
490 DelSectionFormat( GetSections().front() );
492 SfxFilterContainer
* pFCntnr
= mpDocShell
->GetFactory().GetFilterContainer();
493 pFilter
= pFCntnr
->GetFilter4EA( pFilter
->GetTypeName(), SfxFilterFlags::EXPORT
);
498 // save the Globaldoc
499 GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT
, true);
500 GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS
, false);
503 // The medium isn't locked after reopening the document.
504 SfxRequest
aReq( SID_SAVEASDOC
, SfxCallMode::SYNCHRON
, GetAttrPool() );
505 aReq
.AppendItem( SfxStringItem( SID_FILE_NAME
, rPath
) );
506 aReq
.AppendItem( SfxBoolItem( SID_SAVETO
, true ) );
508 aReq
.AppendItem( SfxStringItem( SID_FILTER_NAME
, pFilter
->GetName() ) );
509 const SfxBoolItem
*pRet
= static_cast<const SfxBoolItem
*>(mpDocShell
->ExecuteSlot( aReq
));
511 return pRet
&& pRet
->GetValue();
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */