1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <memory>
21 #include <hintids.hxx>
22
23 #include <sot/storage.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <svl/urihelper.hxx>
26 #include <vcl/graphicfilter.hxx>
27 #include <tools/diagnose_ex.h>
28 #include <editeng/fontitem.hxx>
29 #include <editeng/eeitem.hxx>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <shellio.hxx>
33 #include <doc.hxx>
34 #include <docary.hxx>
35 #include <IMark.hxx>
36 #include <IDocumentSettingAccess.hxx>
37 #include <IDocumentMarkAccess.hxx>
38 #include <numrule.hxx>
39 #include <swerror.h>
40 #include <com/sun/star/ucb/ContentCreationException.hpp>
41
42 using namespace css;
43
44 namespace
45 {
lcl_OutLongExt(SvStream & rStrm,sal_uLong nVal,bool bNeg)46 SvStream& lcl_OutLongExt( SvStream& rStrm, sal_uLong nVal, bool bNeg )
47 {
48 sal_Char aBuf[28];
49
50 int i = SAL_N_ELEMENTS(aBuf);
51 aBuf[--i] = 0;
52 do
53 {
54 aBuf[--i] = '0' + static_cast<sal_Char>(nVal % 10);
55 nVal /= 10;
56 } while (nVal);
57
58 if (bNeg)
59 aBuf[--i] = '-';
60
61 return rStrm.WriteCharPtr( &aBuf[i] );
62 }
63 }
64
65 typedef std::multimap<sal_uLong, const ::sw::mark::IMark*> SwBookmarkNodeTable;
66
67 struct Writer_Impl
68 {
69 SvStream * m_pStream;
70
71 std::unique_ptr< std::map<OUString, OUString> > pFileNameMap;
72 std::vector<const SvxFontItem*> aFontRemoveLst;
73 SwBookmarkNodeTable aBkmkNodePos;
74
75 Writer_Impl();
76
77 void RemoveFontList( SwDoc& rDoc );
78 void InsertBkmk( const ::sw::mark::IMark& rBkmk );
79 };
80
Writer_Impl()81 Writer_Impl::Writer_Impl()
82 : m_pStream(nullptr)
83 {
84 }
85
RemoveFontList(SwDoc & rDoc)86 void Writer_Impl::RemoveFontList( SwDoc& rDoc )
87 {
88 for( const auto& rpFontItem : aFontRemoveLst )
89 {
90 rDoc.GetAttrPool().Remove( *rpFontItem );
91 }
92 }
93
InsertBkmk(const::sw::mark::IMark & rBkmk)94 void Writer_Impl::InsertBkmk(const ::sw::mark::IMark& rBkmk)
95 {
96 sal_uLong nNd = rBkmk.GetMarkPos().nNode.GetIndex();
97
98 aBkmkNodePos.emplace( nNd, &rBkmk );
99
100 if(rBkmk.IsExpanded() && rBkmk.GetOtherMarkPos().nNode != nNd)
101 {
102 nNd = rBkmk.GetOtherMarkPos().nNode.GetIndex();
103 aBkmkNodePos.emplace( nNd, &rBkmk );
104 }
105 }
106
107 /*
108 * This module is the central collection point for all writer-filters
109 * and is a DLL !
110 *
111 * So that the Writer can work with different writers, the output-functions
112 * of the content carrying objects have to be mapped to the various
113 * output-functions.
114 *
115 * For that, to inquire its output function, every object can be gripped
116 * via the which-value in a table.
117 * These functions are available in the corresponding Writer-DLL's.
118 */
119
Writer()120 Writer::Writer()
121 : m_pImpl(std::make_unique<Writer_Impl>())
122 , m_pOrigFileName(nullptr), m_pDoc(nullptr), m_pOrigPam(nullptr)
123 , m_bHideDeleteRedlines(false)
124 {
125 m_bWriteAll = m_bShowProgress = m_bUCS2_WithStartChar = true;
126 m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR =
127 m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock =
128 m_bOrganizerMode = false;
129 m_bExportPargraphNumbering = true;
130 }
131
~Writer()132 Writer::~Writer()
133 {
134 }
135
136 /*
137 * Document Interface Access
138 */
getIDocumentSettingAccess()139 IDocumentSettingAccess& Writer::getIDocumentSettingAccess() { return m_pDoc->getIDocumentSettingAccess(); }
getIDocumentSettingAccess() const140 const IDocumentSettingAccess& Writer::getIDocumentSettingAccess() const { return m_pDoc->getIDocumentSettingAccess(); }
getIDocumentStylePoolAccess()141 IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() { return m_pDoc->getIDocumentStylePoolAccess(); }
getIDocumentStylePoolAccess() const142 const IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() const { return m_pDoc->getIDocumentStylePoolAccess(); }
143
ResetWriter()144 void Writer::ResetWriter()
145 {
146 m_pImpl->RemoveFontList( *m_pDoc );
147 m_pImpl.reset(new Writer_Impl);
148
149 if( m_pCurrentPam )
150 {
151 while (m_pCurrentPam->GetNext() != m_pCurrentPam.get())
152 delete m_pCurrentPam->GetNext();
153 m_pCurrentPam.reset();
154 }
155 m_pCurrentPam = nullptr;
156 m_pOrigFileName = nullptr;
157 m_pDoc = nullptr;
158
159 m_bShowProgress = m_bUCS2_WithStartChar = true;
160 m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR =
161 m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock =
162 m_bOrganizerMode = false;
163 }
164
CopyNextPam(SwPaM ** ppPam)165 bool Writer::CopyNextPam( SwPaM ** ppPam )
166 {
167 if( (*ppPam)->GetNext() == m_pOrigPam )
168 {
169 *ppPam = m_pOrigPam; // set back to the beginning pam
170 return false; // end of the ring
171 }
172
173 // otherwise copy the next value from the next Pam
174 *ppPam = (*ppPam)->GetNext();
175
176 *m_pCurrentPam->GetPoint() = *(*ppPam)->Start();
177 *m_pCurrentPam->GetMark() = *(*ppPam)->End();
178
179 return true;
180 }
181
182 // search the next Bookmark-Position from the Bookmark-Table
183
FindPos_Bkmk(const SwPosition & rPos) const184 sal_Int32 Writer::FindPos_Bkmk(const SwPosition& rPos) const
185 {
186 const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
187 const IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findFirstBookmarkStartsAfter(rPos);
188 if(ppBkmk != pMarkAccess->getBookmarksEnd())
189 return ppBkmk - pMarkAccess->getBookmarksBegin();
190 return -1;
191 }
192
193 std::shared_ptr<SwUnoCursor>
NewUnoCursor(SwDoc & rDoc,sal_uLong const nStartIdx,sal_uLong const nEndIdx)194 Writer::NewUnoCursor(SwDoc & rDoc, sal_uLong const nStartIdx, sal_uLong const nEndIdx)
195 {
196 SwNodes *const pNds = &rDoc.GetNodes();
197
198 SwNodeIndex aStt( *pNds, nStartIdx );
199 SwContentNode* pCNode = aStt.GetNode().GetContentNode();
200 if( !pCNode && nullptr == pNds->GoNext( &aStt ) )
201 {
202 OSL_FAIL( "No more ContentNode at StartPos" );
203 }
204
205 auto const pNew = rDoc.CreateUnoCursor(SwPosition(aStt), false);
206 pNew->SetMark();
207 aStt = nEndIdx;
208 pCNode = aStt.GetNode().GetContentNode();
209 if (!pCNode)
210 pCNode = SwNodes::GoPrevious(&aStt);
211 assert(pCNode && "No more ContentNode at StartPos");
212 pCNode->MakeEndIndex( &pNew->GetPoint()->nContent );
213 pNew->GetPoint()->nNode = aStt;
214 return pNew;
215 }
216
217 // Stream-specific
Strm()218 SvStream& Writer::Strm()
219 {
220 assert(m_pImpl->m_pStream && "Oh-oh. Writer with no Stream!");
221 return *m_pImpl->m_pStream;
222 }
223
SetStream(SvStream * const pStream)224 void Writer::SetStream(SvStream *const pStream)
225 {
226 m_pImpl->m_pStream = pStream;
227 }
228
OutLong(SvStream & rStrm,long nVal)229 SvStream& Writer::OutLong( SvStream& rStrm, long nVal )
230 {
231 const bool bNeg = nVal < 0;
232 if (bNeg)
233 nVal = -nVal;
234
235 return lcl_OutLongExt(rStrm, static_cast<sal_uLong>(nVal), bNeg);
236 }
237
OutULong(SvStream & rStrm,sal_uLong nVal)238 SvStream& Writer::OutULong( SvStream& rStrm, sal_uLong nVal )
239 {
240 return lcl_OutLongExt(rStrm, nVal, false);
241 }
242
Write(SwPaM & rPaM,SvStream & rStrm,const OUString * pFName)243 ErrCode Writer::Write( SwPaM& rPaM, SvStream& rStrm, const OUString* pFName )
244 {
245 if ( IsStgWriter() )
246 {
247 ErrCode nResult = ERRCODE_ABORT;
248 try
249 {
250 tools::SvRef<SotStorage> aRef = new SotStorage( rStrm );
251 nResult = Write( rPaM, *aRef, pFName );
252 if ( nResult == ERRCODE_NONE )
253 aRef->Commit();
254 }
255 catch (const css::ucb::ContentCreationException &)
256 {
257 TOOLS_WARN_EXCEPTION("sw", "Writer::Write caught");
258 }
259 return nResult;
260 }
261
262 m_pDoc = rPaM.GetDoc();
263 m_pOrigFileName = pFName;
264 m_pImpl->m_pStream = &rStrm;
265
266 // Copy PaM, so that it can be modified
267 m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
268 m_pCurrentPam->SetMark();
269 *m_pCurrentPam->GetPoint() = *rPaM.Start();
270 // for comparison secure to the current Pam
271 m_pOrigPam = &rPaM;
272
273 ErrCode nRet = WriteStream();
274
275 ResetWriter();
276
277 return nRet;
278 }
279
SetupFilterOptions(SfxMedium &)280 void Writer::SetupFilterOptions(SfxMedium& /*rMedium*/)
281 {}
282
Write(SwPaM & rPam,SfxMedium & rMedium,const OUString * pFileName)283 ErrCode Writer::Write( SwPaM& rPam, SfxMedium& rMedium, const OUString* pFileName )
284 {
285 SetupFilterOptions(rMedium);
286 // This method must be overridden in SwXMLWriter a storage from medium will be used there.
287 // The microsoft format can write to storage but the storage will be based on the stream.
288 return Write( rPam, *rMedium.GetOutStream(), pFileName );
289 }
290
Write(SwPaM &,SotStorage &,const OUString *)291 ErrCode Writer::Write( SwPaM& /*rPam*/, SotStorage&, const OUString* )
292 {
293 OSL_ENSURE( false, "Write in Storages on a stream?" );
294 return ERR_SWG_WRITE_ERROR;
295 }
296
Write(SwPaM &,const uno::Reference<embed::XStorage> &,const OUString *,SfxMedium *)297 ErrCode Writer::Write( SwPaM&, const uno::Reference < embed::XStorage >&, const OUString*, SfxMedium* )
298 {
299 OSL_ENSURE( false, "Write in Storages on a stream?" );
300 return ERR_SWG_WRITE_ERROR;
301 }
302
CopyLocalFileToINet(OUString & rFileNm)303 bool Writer::CopyLocalFileToINet( OUString& rFileNm )
304 {
305 if( !m_pOrigFileName ) // can be happen, by example if we
306 return false; // write into the clipboard
307
308 bool bRet = false;
309 INetURLObject aFileUrl( rFileNm ), aTargetUrl( *m_pOrigFileName );
310
311 // this is our old without the Mail-Export
312 if( ! ( INetProtocol::File == aFileUrl.GetProtocol() &&
313 INetProtocol::File != aTargetUrl.GetProtocol() &&
314 INetProtocol::Ftp <= aTargetUrl.GetProtocol() &&
315 INetProtocol::VndSunStarWebdav >= aTargetUrl.GetProtocol() ) )
316 return bRet;
317
318 if (m_pImpl->pFileNameMap)
319 {
320 // has the file been moved?
321 std::map<OUString, OUString>::iterator it = m_pImpl->pFileNameMap->find( rFileNm );
322 if ( it != m_pImpl->pFileNameMap->end() )
323 {
324 rFileNm = it->second;
325 return true;
326 }
327 }
328 else
329 {
330 m_pImpl->pFileNameMap.reset( new std::map<OUString, OUString> );
331 }
332
333 OUString aSrc = rFileNm;
334 OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
335
336 SfxMedium aSrcFile( aSrc, StreamMode::READ );
337 SfxMedium aDstFile( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
338
339 aDstFile.GetOutStream()->WriteStream( *aSrcFile.GetInStream() );
340
341 aSrcFile.Close();
342 aDstFile.Commit();
343
344 bRet = ERRCODE_NONE == aDstFile.GetError();
345
346 if( bRet )
347 {
348 m_pImpl->pFileNameMap->insert( std::make_pair( aSrc, aDest ) );
349 rFileNm = aDest;
350 }
351
352 return bRet;
353 }
354
PutNumFormatFontsInAttrPool()355 void Writer::PutNumFormatFontsInAttrPool()
356 {
357 // then there are a few fonts in the NumRules
358 // These put into the Pool. After this does they have a RefCount > 1
359 // it can be removed - it is already in the Pool
360 SfxItemPool& rPool = m_pDoc->GetAttrPool();
361 const SwNumRuleTable& rListTable = m_pDoc->GetNumRuleTable();
362 const SwNumRule* pRule;
363 const SwNumFormat* pFormat;
364 const vcl::Font* pFont;
365 const vcl::Font* pDefFont = &numfunc::GetDefBulletFont();
366 bool bCheck = false;
367
368 for( size_t nGet = rListTable.size(); nGet; )
369 if( SwDoc::IsUsed( *(pRule = rListTable[ --nGet ] )))
370 for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl )
371 if( SVX_NUM_CHAR_SPECIAL == (pFormat = &pRule->Get( nLvl ))->GetNumberingType() ||
372 SVX_NUM_BITMAP == pFormat->GetNumberingType() )
373 {
374 if( nullptr == ( pFont = pFormat->GetBulletFont() ) )
375 pFont = pDefFont;
376
377 if( bCheck )
378 {
379 if( *pFont == *pDefFont )
380 continue;
381 }
382 else if( *pFont == *pDefFont )
383 bCheck = true;
384
385 AddFontItem( rPool, SvxFontItem( pFont->GetFamilyType(),
386 pFont->GetFamilyName(), pFont->GetStyleName(),
387 pFont->GetPitch(), pFont->GetCharSet(), RES_CHRATR_FONT ));
388 }
389 }
390
PutEditEngFontsInAttrPool()391 void Writer::PutEditEngFontsInAttrPool()
392 {
393 SfxItemPool& rPool = m_pDoc->GetAttrPool();
394 if( rPool.GetSecondaryPool() )
395 {
396 AddFontItems_( rPool, EE_CHAR_FONTINFO );
397 AddFontItems_( rPool, EE_CHAR_FONTINFO_CJK );
398 AddFontItems_( rPool, EE_CHAR_FONTINFO_CTL );
399 }
400 }
401
AddFontItems_(SfxItemPool & rPool,sal_uInt16 nW)402 void Writer::AddFontItems_( SfxItemPool& rPool, sal_uInt16 nW )
403 {
404 const SvxFontItem* pFont = static_cast<const SvxFontItem*>(&rPool.GetDefaultItem( nW ));
405 AddFontItem( rPool, *pFont );
406
407 if( nullptr != ( pFont = static_cast<const SvxFontItem*>(rPool.GetPoolDefaultItem( nW ))) )
408 AddFontItem( rPool, *pFont );
409
410 for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nW))
411 AddFontItem( rPool, *static_cast<const SvxFontItem*>(pItem) );
412 }
413
AddFontItem(SfxItemPool & rPool,const SvxFontItem & rFont)414 void Writer::AddFontItem( SfxItemPool& rPool, const SvxFontItem& rFont )
415 {
416 const SvxFontItem* pItem;
417 if( RES_CHRATR_FONT != rFont.Which() )
418 {
419 SvxFontItem aFont( rFont );
420 aFont.SetWhich( RES_CHRATR_FONT );
421 pItem = &rPool.Put( aFont );
422 }
423 else
424 pItem = &rPool.Put( rFont );
425
426 if( 1 < pItem->GetRefCount() )
427 rPool.Remove( *pItem );
428 else
429 {
430 m_pImpl->aFontRemoveLst.push_back( pItem );
431 }
432 }
433
434 // build a bookmark table, which is sort by the node position. The
435 // OtherPos of the bookmarks also inserted.
CreateBookmarkTable()436 void Writer::CreateBookmarkTable()
437 {
438 const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
439 for(IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getBookmarksBegin();
440 ppBkmk != pMarkAccess->getBookmarksEnd();
441 ++ppBkmk)
442 {
443 m_pImpl->InsertBkmk(**ppBkmk);
444 }
445 }
446
447 // search all Bookmarks in the range and return it in the Array
GetBookmarks(const SwContentNode & rNd,sal_Int32 nStt,sal_Int32 nEnd,std::vector<const::sw::mark::IMark * > & rArr)448 bool Writer::GetBookmarks(const SwContentNode& rNd, sal_Int32 nStt,
449 sal_Int32 nEnd, std::vector< const ::sw::mark::IMark* >& rArr)
450 {
451 OSL_ENSURE( rArr.empty(), "there are still entries available" );
452
453 sal_uLong nNd = rNd.GetIndex();
454 std::pair<SwBookmarkNodeTable::const_iterator, SwBookmarkNodeTable::const_iterator> aIterPair
455 = m_pImpl->aBkmkNodePos.equal_range( nNd );
456 if( aIterPair.first != aIterPair.second )
457 {
458 // there exist some bookmarks, search now all which is in the range
459 if( !nStt && nEnd == rNd.Len() )
460 // all
461 for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it )
462 rArr.push_back( it->second );
463 else
464 {
465 for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it )
466 {
467 const ::sw::mark::IMark& rBkmk = *(it->second);
468 sal_Int32 nContent;
469 if( rBkmk.GetMarkPos().nNode == nNd &&
470 (nContent = rBkmk.GetMarkPos().nContent.GetIndex() ) >= nStt &&
471 nContent < nEnd )
472 {
473 rArr.push_back( &rBkmk );
474 }
475 else if( rBkmk.IsExpanded() && nNd ==
476 rBkmk.GetOtherMarkPos().nNode.GetIndex() && (nContent =
477 rBkmk.GetOtherMarkPos().nContent.GetIndex() ) >= nStt &&
478 nContent < nEnd )
479 {
480 rArr.push_back( &rBkmk );
481 }
482 }
483 }
484 }
485 return !rArr.empty();
486 }
487
488 // Storage-specific
WriteStream()489 ErrCode StgWriter::WriteStream()
490 {
491 OSL_ENSURE( false, "Write in Storages on a stream?" );
492 return ERR_SWG_WRITE_ERROR;
493 }
494
Write(SwPaM & rPaM,SotStorage & rStg,const OUString * pFName)495 ErrCode StgWriter::Write( SwPaM& rPaM, SotStorage& rStg, const OUString* pFName )
496 {
497 SetStream(nullptr);
498 pStg = &rStg;
499 m_pDoc = rPaM.GetDoc();
500 m_pOrigFileName = pFName;
501
502 // Copy PaM, so that it can be modified
503 m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
504 m_pCurrentPam->SetMark();
505 *m_pCurrentPam->GetPoint() = *rPaM.Start();
506 // for comparison secure to the current Pam
507 m_pOrigPam = &rPaM;
508
509 ErrCode nRet = WriteStorage();
510
511 pStg = nullptr;
512 ResetWriter();
513
514 return nRet;
515 }
516
Write(SwPaM & rPaM,const uno::Reference<embed::XStorage> & rStg,const OUString * pFName,SfxMedium * pMedium)517 ErrCode StgWriter::Write( SwPaM& rPaM, const uno::Reference < embed::XStorage >& rStg, const OUString* pFName, SfxMedium* pMedium )
518 {
519 SetStream(nullptr);
520 pStg = nullptr;
521 xStg = rStg;
522 m_pDoc = rPaM.GetDoc();
523 m_pOrigFileName = pFName;
524
525 // Copy PaM, so that it can be modified
526 m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
527 m_pCurrentPam->SetMark();
528 *m_pCurrentPam->GetPoint() = *rPaM.Start();
529 // for comparison secure to the current Pam
530 m_pOrigPam = &rPaM;
531
532 ErrCode nRet = pMedium ? WriteMedium( *pMedium ) : WriteStorage();
533
534 pStg = nullptr;
535 ResetWriter();
536
537 return nRet;
538 }
539
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
541