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 <config_features.h>
21
22 #include <sal/config.h>
23 #include <sal/log.hxx>
24
25 #include <com/sun/star/embed/Aspects.hpp>
26 #include <com/sun/star/embed/ElementModes.hpp>
27 #include <com/sun/star/frame/XModel.hpp>
28 #include <com/sun/star/packages/XPackageEncryption.hpp>
29 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
30 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
31
32 #include <i18nlangtag/languagetag.hxx>
33
34 #include <comphelper/configuration.hxx>
35 #include <comphelper/string.hxx>
36 #include <unotools/ucbstreamhelper.hxx>
37 #include <unotools/streamwrap.hxx>
38 #include <rtl/random.h>
39 #include <rtl/ustring.hxx>
40 #include <rtl/ustrbuf.hxx>
41
42 #include <sfx2/docinf.hxx>
43 #include <sfx2/frame.hxx>
44 #include <sfx2/zoomitem.hxx>
45 #include <tools/urlobj.hxx>
46 #include <unotools/tempfile.hxx>
47
48 #include <comphelper/docpasswordrequest.hxx>
49 #include <comphelper/documentinfo.hxx>
50 #include <comphelper/propertysequence.hxx>
51
52 #include <editeng/outlobj.hxx>
53 #include <editeng/brushitem.hxx>
54 #include <editeng/formatbreakitem.hxx>
55 #include <editeng/tstpitem.hxx>
56 #include <editeng/ulspitem.hxx>
57 #include <editeng/langitem.hxx>
58 #include <editeng/opaqitem.hxx>
59 #include <editeng/charhiddenitem.hxx>
60 #include <editeng/fontitem.hxx>
61 #include <editeng/editeng.hxx>
62 #include <svx/svdoole2.hxx>
63 #include <svx/svdoashp.hxx>
64 #include <svx/svxerr.hxx>
65 #include <filter/msfilter/mscodec.hxx>
66 #include <svx/svdmodel.hxx>
67 #include <svx/xflclit.hxx>
68 #include <svx/sdasitm.hxx>
69 #include <svx/sdtagitm.hxx>
70 #include <svx/sdtcfitm.hxx>
71 #include <svx/sdtditm.hxx>
72 #include <svx/sdtmfitm.hxx>
73 #include <fmtfld.hxx>
74 #include <fmturl.hxx>
75 #include <fmtinfmt.hxx>
76 #include <reffld.hxx>
77 #include <fmthdft.hxx>
78 #include <fmtcntnt.hxx>
79 #include <fmtcnct.hxx>
80 #include <fmtanchr.hxx>
81 #include <fmtpdsc.hxx>
82 #include <ftninfo.hxx>
83 #include <fmtftn.hxx>
84 #include <txtftn.hxx>
85 #include <ndtxt.hxx>
86 #include <pagedesc.hxx>
87 #include <paratr.hxx>
88 #include <poolfmt.hxx>
89 #include <fmtclbl.hxx>
90 #include <section.hxx>
91 #include <docsh.hxx>
92 #include <IDocumentFieldsAccess.hxx>
93 #include <IDocumentLayoutAccess.hxx>
94 #include <IDocumentMarkAccess.hxx>
95 #include <IDocumentStylePoolAccess.hxx>
96 #include <IDocumentExternalData.hxx>
97 #include <../../core/inc/DocumentRedlineManager.hxx>
98 #include <docufld.hxx>
99 #include <swfltopt.hxx>
100 #include <utility>
101 #include <viewsh.hxx>
102 #include <shellres.hxx>
103 #include <swerror.h>
104 #include <swtable.hxx>
105 #include <fchrfmt.hxx>
106 #include <charfmt.hxx>
107 #include <fmtautofmt.hxx>
108 #include <IDocumentSettingAccess.hxx>
109 #include "sprmids.hxx"
110
111 #include "writerwordglue.hxx"
112
113 #include <ndgrf.hxx>
114 #include <editeng/editids.hrc>
115 #include <fmtflcnt.hxx>
116 #include <txatbase.hxx>
117
118 #include "ww8par2.hxx"
119
120 #include <com/sun/star/beans/PropertyAttribute.hpp>
121 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
122 #include <com/sun/star/document/XViewDataSupplier.hpp>
123
124 #include <svl/lngmisc.hxx>
125 #include <svl/itemiter.hxx>
126 #include <svl/whiter.hxx>
127
128 #include <comphelper/indexedpropertyvalues.hxx>
129 #include <comphelper/processfactory.hxx>
130 #include <basic/basmgr.hxx>
131
132 #include "ww8toolbar.hxx"
133 #include <o3tl/unit_conversion.hxx>
134 #include <o3tl/safeint.hxx>
135 #include <osl/file.hxx>
136
137 #include <breakit.hxx>
138
139 #include <sfx2/docfile.hxx>
140 #include <swdll.hxx>
141 #include "WW8Sttbf.hxx"
142 #include "WW8FibData.hxx"
143 #include <unordered_set>
144 #include <memory>
145
146 #include <com/sun/star/i18n/XBreakIterator.hpp>
147 #include <com/sun/star/i18n/ScriptType.hpp>
148 #include <unotools/pathoptions.hxx>
149 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
150
151 #include <com/sun/star/script/vba/XVBACompatibility.hpp>
152 #include <comphelper/sequenceashashmap.hxx>
153 #include <oox/ole/vbaproject.hxx>
154 #include <oox/ole/olestorage.hxx>
155 #include <comphelper/storagehelper.hxx>
156 #include <sfx2/DocumentMetadataAccess.hxx>
157 #include <comphelper/diagnose_ex.hxx>
158 #include <officecfg/Office/Common.hxx>
159
160 using namespace ::com::sun::star;
161 using namespace sw::util;
162 using namespace sw::types;
163 using namespace nsHdFtFlags;
164
GetMacroInfo(SdrObject * pObj)165 static SwMacroInfo* GetMacroInfo( SdrObject* pObj )
166 {
167 if ( pObj )
168 {
169 sal_uInt16 nCount = pObj->GetUserDataCount();
170 for( sal_uInt16 i = 0; i < nCount; i++ )
171 {
172 SdrObjUserData* pData = pObj->GetUserData( i );
173 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
174 && pData->GetId() == SW_UD_IMAPDATA)
175 {
176 return dynamic_cast<SwMacroInfo*>(pData);
177 }
178 }
179 SwMacroInfo* pData = new SwMacroInfo;
180 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
181 return pData;
182 }
183
184 return nullptr;
185 };
186
lclGetAbsPath(OUString & rPath,sal_uInt16 nLevel,SwDocShell const * pDocShell)187 static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell)
188 {
189 OUStringBuffer aTmpStr;
190 while( nLevel )
191 {
192 aTmpStr.append("../");
193 --nLevel;
194 }
195 if (!aTmpStr.isEmpty())
196 aTmpStr.append(rPath);
197 else
198 aTmpStr = rPath;
199
200 if (!aTmpStr.isEmpty())
201 {
202 bool bWasAbs = false;
203 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
204 // full path as stored in SvxURLField must be encoded
205 }
206 }
207
208 namespace
209 {
lclIgnoreUString32(SvStream & rStrm)210 void lclIgnoreUString32(SvStream& rStrm)
211 {
212 sal_uInt32 nChars(0);
213 rStrm.ReadUInt32(nChars);
214 nChars *= 2;
215 rStrm.SeekRel(nChars);
216 }
217 }
218
ReadEmbeddedData(SvStream & rStrm,SwDocShell const * pDocShell,struct HyperLinksTable & hlStr)219 void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
220 {
221 // (0x01B8) HLINK
222 // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
223 constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL.
224 constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path.
225 constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description.
226 constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark.
227 constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame.
228 constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path.
229
230 //sal_uInt8 maGuidStdLink[ 16 ] ={
231 // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
232
233 sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
234 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
235
236 sal_uInt8 const aGuidFileMoniker[ 16 ] = {
237 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
238
239 sal_uInt8 aGuid[16];
240 sal_uInt32 nFlags(0);
241
242 rStrm.ReadBytes(aGuid, 16);
243 rStrm.SeekRel( 4 );
244 rStrm.ReadUInt32( nFlags );
245
246 std::unique_ptr< OUString > xLongName; // link / file name
247 std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
248 std::unique_ptr< OUString > xTextMark; // text mark
249
250 // description (ignore)
251 if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
252 lclIgnoreUString32( rStrm );
253
254 // target frame
255 if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
256 {
257 hlStr.tarFrame = read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm);
258 }
259
260 // UNC path
261 if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
262 {
263 // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the
264 // string field, including the null-terminating character.
265 sal_uInt32 nStrLen(0);
266 rStrm.ReadUInt32(nStrLen);
267 if (nStrLen)
268 {
269 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
270 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
271 lclGetAbsPath( *xLongName, 0 , pDocShell);
272 }
273 }
274 // file link or URL
275 else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
276 {
277 rStrm.ReadBytes(aGuid, 16);
278
279 if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
280 {
281 sal_uInt16 nLevel = 0; // counter for level to climb down in path
282 rStrm.ReadUInt16( nLevel );
283 // MS-OSHARED: An unsigned integer that specifies the number of
284 // ANSI characters in ansiPath, including the terminating NULL character
285 sal_uInt32 nUnits = 0;
286 rStrm.ReadUInt32(nUnits);
287 if (!nUnits)
288 xShortName.reset(new OUString);
289 else
290 {
291 OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1));
292 rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
293 xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage()));
294 }
295 rStrm.SeekRel( 24 );
296
297 sal_uInt32 nStrLen(0);
298 rStrm.ReadUInt32( nStrLen );
299 if( nStrLen )
300 {
301 nStrLen = 0;
302 rStrm.ReadUInt32( nStrLen );
303 nStrLen /= 2;
304 rStrm.SeekRel( 2 );
305 // MS-OSHARED: This array MUST not include a terminating NULL character.
306 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen)));
307 lclGetAbsPath( *xLongName, nLevel, pDocShell);
308 }
309 else
310 lclGetAbsPath( *xShortName, nLevel, pDocShell);
311 }
312 else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
313 {
314 // MS-OSHARED: An unsigned integer that specifies the size of this
315 // structure in bytes, excluding the size of the length field. The
316 // value of this field MUST be ... the byte size of the url
317 // field (including the terminating NULL character)
318 sal_uInt32 nStrLen(0);
319 rStrm.ReadUInt32( nStrLen );
320 nStrLen /= 2;
321 if (!nStrLen)
322 xLongName.reset(new OUString);
323 else
324 {
325 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
326 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
327 }
328 if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
329 lclGetAbsPath( *xLongName, 0 ,pDocShell);
330 }
331 else
332 {
333 SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
334 }
335 }
336
337 // text mark
338 if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
339 {
340 xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
341 }
342
343 if (!xLongName && xShortName)
344 xLongName.reset(new OUString(*xShortName));
345 else if (!xLongName && xTextMark)
346 xLongName.reset( new OUString );
347
348 if (xLongName)
349 {
350 if (xTextMark)
351 {
352 if (xLongName->isEmpty())
353 *xTextMark = xTextMark->replace('!', '.');
354 *xLongName += "#" + *xTextMark;
355 }
356 hlStr.hLinkAddr = *xLongName;
357 }
358 }
359
360 namespace {
361
362 class BasicProjImportHelper
363 {
364 SwDocShell& mrDocShell;
365 uno::Reference< uno::XComponentContext > mxCtx;
366 public:
BasicProjImportHelper(SwDocShell & rShell)367 explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
368 mxCtx(comphelper::getProcessComponentContext())
369 {
370 }
371 bool import( const uno::Reference< io::XInputStream >& rxIn );
372 OUString getProjectName() const;
373 };
374
375 }
376
import(const uno::Reference<io::XInputStream> & rxIn)377 bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
378 {
379 bool bRet = false;
380 try
381 {
382 oox::ole::OleStorage root( mxCtx, rxIn, false );
383 oox::StorageRef vbaStg = root.openSubStorage( u"Macros"_ustr , false );
384 if ( vbaStg )
385 {
386 oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
387 bRet = aVbaPrj.importVbaProject( *vbaStg );
388 }
389 }
390 catch( const uno::Exception& )
391 {
392 bRet = false;
393 }
394 return bRet;
395 }
396
getProjectName() const397 OUString BasicProjImportHelper::getProjectName() const
398 {
399 OUString sProjName( u"Standard"_ustr );
400 uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
401 if ( xProps.is() )
402 {
403 try
404 {
405 uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( u"BasicLibraries"_ustr ), uno::UNO_QUERY_THROW );
406 sProjName = xVBA->getProjectName();
407
408 }
409 catch( const uno::Exception& )
410 {
411 }
412 }
413 return sProjName;
414 }
415
416 namespace {
417
418 class Sttb : public TBBase
419 {
420 struct SBBItem
421 {
422 sal_uInt16 cchData;
423 OUString data;
SBBItem__anonf22e66620311::Sttb::SBBItem424 SBBItem() : cchData(0){}
425 };
426 sal_uInt16 m_fExtend;
427 sal_uInt16 m_cData;
428 sal_uInt16 m_cbExtra;
429
430 std::vector< SBBItem > m_dataItems;
431
432 Sttb(Sttb const&) = delete;
433 Sttb& operator=(Sttb const&) = delete;
434
435 public:
436 Sttb();
437
438 bool Read(SvStream &rS) override;
439 OUString getStringAtIndex( sal_uInt32 );
440 };
441
442 }
443
Sttb()444 Sttb::Sttb()
445 : m_fExtend(0)
446 , m_cData(0)
447 , m_cbExtra(0)
448 {
449 }
450
Read(SvStream & rS)451 bool Sttb::Read( SvStream& rS )
452 {
453 SAL_INFO("sw.ww8", "stream pos " << rS.Tell());
454 nOffSet = rS.Tell();
455 rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
456 if ( m_cData )
457 {
458 //if they are all going to be empty strings, how many could there be
459 const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16);
460 if (m_cData > nMaxPossibleRecords)
461 return false;
462 for ( sal_Int32 index = 0; index < m_cData; ++index )
463 {
464 SBBItem aItem;
465 rS.ReadUInt16( aItem.cchData );
466 aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
467 m_dataItems.push_back( aItem );
468 }
469 }
470 return true;
471 }
472
473 OUString
getStringAtIndex(sal_uInt32 index)474 Sttb::getStringAtIndex( sal_uInt32 index )
475 {
476 OUString aRet;
477 if ( index < m_dataItems.size() )
478 aRet = m_dataItems[ index ].data;
479 return aRet;
480
481 }
482
SwMSDffManager(SwWW8ImplReader & rRdr,bool bSkipImages)483 SwMSDffManager::SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages )
484 : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo,
485 rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages),
486 m_rReader(rRdr), m_pFallbackStream(nullptr)
487 {
488 SetSvxMSDffSettings( GetSvxMSDffSettings() );
489 nSvxMSDffOLEConvFlags = SwMSDffManager::GetFilterFlags();
490 }
491
GetFilterFlags()492 sal_uInt32 SwMSDffManager::GetFilterFlags()
493 {
494 sal_uInt32 nFlags(0);
495 if (officecfg::Office::Common::Filter::Microsoft::Import::MathTypeToMath::get())
496 nFlags |= OLE_MATHTYPE_2_STARMATH;
497 if (officecfg::Office::Common::Filter::Microsoft::Import::ExcelToCalc::get())
498 nFlags |= OLE_EXCEL_2_STARCALC;
499 if (officecfg::Office::Common::Filter::Microsoft::Import::PowerPointToImpress::get())
500 nFlags |= OLE_POWERPOINT_2_STARIMPRESS;
501 if (officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get())
502 nFlags |= OLE_WINWORD_2_STARWRITER;
503 return nFlags;
504 }
505
506 /*
507 * I would like to override the default OLE importing to add a test
508 * and conversion of OCX controls from their native OLE type into our
509 * native nonOLE Form Control Objects.
510 */
511 // #i32596# - consider new parameter <_nCalledByGroup>
ImportOLE(sal_uInt32 nOLEId,const Graphic & rGrf,const tools::Rectangle & rBoundRect,const tools::Rectangle & rVisArea,const int _nCalledByGroup) const512 rtl::Reference<SdrObject> SwMSDffManager::ImportOLE( sal_uInt32 nOLEId,
513 const Graphic& rGrf,
514 const tools::Rectangle& rBoundRect,
515 const tools::Rectangle& rVisArea,
516 const int _nCalledByGroup ) const
517 {
518 // #i32596# - no import of OLE object, if it's inside a group.
519 // NOTE: This can be undone, if grouping of Writer fly frames is possible or
520 // if drawing OLE objects are allowed in Writer.
521 if ( _nCalledByGroup > 0 )
522 {
523 return nullptr;
524 }
525
526 rtl::Reference<SdrObject> pRet;
527 OUString sStorageName;
528 rtl::Reference<SotStorage> xSrcStg;
529 uno::Reference < embed::XStorage > xDstStg;
530 if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
531 {
532 rtl::Reference<SotStorage> xSrc = xSrcStg->OpenSotStorage(sStorageName);
533 OSL_ENSURE(m_rReader.m_xFormImpl, "No Form Implementation!");
534 css::uno::Reference< css::drawing::XShape > xShape;
535 if ( (!(m_rReader.m_bIsHeader || m_rReader.m_bIsFooter)) &&
536 m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
537 {
538 pRet = SdrObject::getSdrObjectFromXShape(xShape);
539 }
540 else
541 {
542 ErrCode nError = ERRCODE_NONE;
543 pRet = CreateSdrOLEFromStorage(
544 *pSdrModel,
545 sStorageName,
546 xSrcStg,
547 xDstStg,
548 rGrf,
549 rBoundRect,
550 rVisArea,
551 pStData,
552 nError,
553 nSvxMSDffOLEConvFlags,
554 css::embed::Aspects::MSOLE_CONTENT,
555 m_rReader.GetBaseURL());
556 }
557 }
558 return pRet;
559 }
560
DisableFallbackStream()561 void SwMSDffManager::DisableFallbackStream()
562 {
563 OSL_ENSURE(!m_pFallbackStream,
564 "if you're recursive, you're broken");
565 m_pFallbackStream = pStData2;
566 m_aOldEscherBlipCache = aEscherBlipCache;
567 aEscherBlipCache.clear();
568 pStData2 = nullptr;
569 }
570
EnableFallbackStream()571 void SwMSDffManager::EnableFallbackStream()
572 {
573 pStData2 = m_pFallbackStream;
574 aEscherBlipCache = m_aOldEscherBlipCache;
575 m_aOldEscherBlipCache.clear();
576 m_pFallbackStream = nullptr;
577 }
578
GetToggleAttrFlags() const579 sal_uInt16 SwWW8ImplReader::GetToggleAttrFlags() const
580 {
581 return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
582 }
583
GetToggleBiDiAttrFlags() const584 sal_uInt16 SwWW8ImplReader::GetToggleBiDiAttrFlags() const
585 {
586 return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
587 }
588
SetToggleAttrFlags(sal_uInt16 nFlags)589 void SwWW8ImplReader::SetToggleAttrFlags(sal_uInt16 nFlags)
590 {
591 if (m_xCtrlStck)
592 m_xCtrlStck->SetToggleAttrFlags(nFlags);
593 }
594
SetToggleBiDiAttrFlags(sal_uInt16 nFlags)595 void SwWW8ImplReader::SetToggleBiDiAttrFlags(sal_uInt16 nFlags)
596 {
597 if (m_xCtrlStck)
598 m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
599 }
600
ProcessObj(SvStream & rSt,DffObjData & rObjData,SvxMSDffClientData & rData,tools::Rectangle & rTextRect,SdrObject * pObj1)601 rtl::Reference<SdrObject> SwMSDffManager::ProcessObj(SvStream& rSt,
602 DffObjData& rObjData,
603 SvxMSDffClientData& rData,
604 tools::Rectangle& rTextRect,
605 SdrObject* pObj1
606 )
607 {
608 rtl::Reference<SdrObject> pObj = pObj1;
609 if( !rTextRect.IsEmpty() )
610 {
611 SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
612 std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
613
614 // fill Import Record with data
615 pImpRec->nShapeId = rObjData.nShapeId;
616 pImpRec->eShapeType = rObjData.eShapeType;
617
618 rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt,
619 DFF_msofbtClientAnchor,
620 SEEK_FROM_CURRENT_AND_RESTART );
621 if( rObjData.bClientAnchor )
622 ProcessClientAnchor( rSt,
623 maShapeRecords.Current()->nRecLen,
624 pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
625
626 rObjData.bClientData = maShapeRecords.SeekToContent( rSt,
627 DFF_msofbtClientData,
628 SEEK_FROM_CURRENT_AND_RESTART );
629 if( rObjData.bClientData )
630 ProcessClientData( rSt,
631 maShapeRecords.Current()->nRecLen,
632 pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
633
634 pImpRec->nGroupShapeBooleanProperties = 0;
635
636 if( maShapeRecords.SeekToContent( rSt,
637 DFF_msofbtUDefProp,
638 SEEK_FROM_CURRENT_AND_RESTART )
639 && maShapeRecords.Current()->nRecLen )
640 {
641 sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
642 auto nAvailableBytes = rSt.remainingSize();
643 if (nBytesLeft > nAvailableBytes)
644 {
645 SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
646 nBytesLeft = nAvailableBytes;
647 }
648 while( 5 < nBytesLeft )
649 {
650 sal_uInt16 nPID(0);
651 rSt.ReadUInt16(nPID);
652 sal_uInt32 nUDData(0);
653 rSt.ReadUInt32(nUDData);
654 if (!rSt.good())
655 break;
656 switch (nPID)
657 {
658 case 0x038F: pImpRec->nXAlign = nUDData; break;
659 case 0x0390:
660 pImpRec->nXRelTo = nUDData;
661 break;
662 case 0x0391: pImpRec->nYAlign = nUDData; break;
663 case 0x0392:
664 pImpRec->nYRelTo = nUDData;
665 break;
666 case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
667 case 0x0393:
668 // This seems to correspond to o:hrpct from .docx (even including
669 // the difference that it's in 0.1% even though the .docx spec
670 // says it's in 1%).
671 pImpRec->relativeHorizontalWidth = nUDData;
672 break;
673 case 0x0394:
674 // And this is really just a guess, but a mere presence of this
675 // flag makes a horizontal rule be as wide as the page (unless
676 // overridden by something), so it probably matches o:hr from .docx.
677 pImpRec->isHorizontalRule = true;
678 break;
679 }
680 nBytesLeft -= 6;
681 }
682 }
683
684 // Text Frame also Title or Outline
685 sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
686 if( nTextId )
687 {
688 SfxItemSet aSet( pSdrModel->GetItemPool() );
689
690 // Originally anything that as a mso_sptTextBox was created as a
691 // textbox, this was changed to be created as a simple
692 // rect to keep impress happy. For the rest of us we'd like to turn
693 // it back into a textbox again.
694 bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox);
695 if (!bIsSimpleDrawingTextBox)
696 {
697 // Either
698 // a) it's a simple text object or
699 // b) it's a rectangle with text and square wrapping.
700 bIsSimpleDrawingTextBox =
701 (
702 (pImpRec->eShapeType == mso_sptTextSimple) ||
703 (
704 (pImpRec->eShapeType == mso_sptRectangle)
705 && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
706 )
707 );
708 }
709
710 // Distance of Textbox to its surrounding Autoshape
711 sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440);
712 sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 );
713 sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 );
714 sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 );
715
716 ScaleEmu( nTextLeft );
717 ScaleEmu( nTextRight );
718 ScaleEmu( nTextTop );
719 ScaleEmu( nTextBottom );
720
721 Degree100 nTextRotationAngle;
722 bool bVerticalText = false;
723 if ( IsProperty( DFF_Prop_txflTextFlow ) )
724 {
725 MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
726 DFF_Prop_txflTextFlow, 0) & 0xFFFF);
727 switch( eTextFlow )
728 {
729 case mso_txflBtoT:
730 nTextRotationAngle = 9000_deg100;
731 break;
732 case mso_txflVertN:
733 case mso_txflTtoBN:
734 nTextRotationAngle = 27000_deg100;
735 break;
736 case mso_txflTtoBA:
737 bVerticalText = true;
738 break;
739 case mso_txflHorzA:
740 bVerticalText = true;
741 nTextRotationAngle = 9000_deg100;
742 break;
743 case mso_txflHorzN:
744 default :
745 break;
746 }
747 }
748
749 if (nTextRotationAngle)
750 {
751 if (nTextRotationAngle == 9000_deg100)
752 {
753 tools::Long nWidth = rTextRect.GetWidth();
754 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
755 rTextRect.SetBottom( rTextRect.Top() + nWidth );
756
757 sal_Int32 nOldTextLeft = nTextLeft;
758 sal_Int32 nOldTextRight = nTextRight;
759 sal_Int32 nOldTextTop = nTextTop;
760 sal_Int32 nOldTextBottom = nTextBottom;
761
762 nTextLeft = nOldTextBottom;
763 nTextRight = nOldTextTop;
764 nTextTop = nOldTextLeft;
765 nTextBottom = nOldTextRight;
766 }
767 else if (nTextRotationAngle == 27000_deg100)
768 {
769 tools::Long nWidth = rTextRect.GetWidth();
770 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
771 rTextRect.SetBottom( rTextRect.Top() + nWidth );
772
773 sal_Int32 nOldTextLeft = nTextLeft;
774 sal_Int32 nOldTextRight = nTextRight;
775 sal_Int32 nOldTextTop = nTextTop;
776 sal_Int32 nOldTextBottom = nTextBottom;
777
778 nTextLeft = nOldTextTop;
779 nTextRight = nOldTextBottom;
780 nTextTop = nOldTextRight;
781 nTextBottom = nOldTextLeft;
782 }
783 }
784
785 if (bIsSimpleDrawingTextBox)
786 {
787 pObj = new SdrRectObj(
788 *pSdrModel,
789 SdrObjKind::Text,
790 rTextRect);
791 }
792
793 // The vertical paragraph justification are contained within the
794 // BoundRect so calculate it here
795 tools::Rectangle aNewRect(rTextRect);
796 aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
797 aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
798
799 // Only if it's a simple Textbox, Writer can replace the Object
800 // with a Frame, else
801 if( bIsSimpleDrawingTextBox )
802 {
803 std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
804 std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
805
806 SvxMSDffShapeInfos_ById::const_iterator const it =
807 GetShapeInfos()->find(xTmpRec);
808 if (it != GetShapeInfos()->end())
809 {
810 SvxMSDffShapeInfo& rInfo = **it;
811 pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
812 }
813
814 ApplyAttributes(rSt, aSet, rObjData);
815 }
816
817 if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2)
818 {
819 aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
820 aSet.Put( makeSdrTextMinFrameHeightItem(
821 aNewRect.Bottom() - aNewRect.Top() ) );
822 aSet.Put( makeSdrTextMinFrameWidthItem(
823 aNewRect.Right() - aNewRect.Left() ) );
824 }
825 else
826 {
827 aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
828 aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
829 }
830
831 switch ( static_cast<MSO_WrapMode>(GetPropertyValue( DFF_Prop_WrapText, mso_wrapSquare )) )
832 {
833 case mso_wrapNone :
834 aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
835 pImpRec->bAutoWidth = true;
836 break;
837 case mso_wrapByPoints :
838 aSet.Put( makeSdrTextContourFrameItem( true ) );
839 break;
840 default:
841 ;
842 }
843
844 // Set distances on Textbox's margins
845 aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
846 aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
847 aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
848 aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
849 pImpRec->nDxTextLeft = nTextLeft;
850 pImpRec->nDyTextTop = nTextTop;
851 pImpRec->nDxTextRight = nTextRight;
852 pImpRec->nDyTextBottom = nTextBottom;
853
854 // Taking the correct default (which is mso_anchorTop)
855 sal_uInt32 eTextAnchor =
856 GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
857
858 SdrTextVertAdjust eTVA = bVerticalText
859 ? SDRTEXTVERTADJUST_BLOCK
860 : SDRTEXTVERTADJUST_CENTER;
861 SdrTextHorzAdjust eTHA = bVerticalText
862 ? SDRTEXTHORZADJUST_CENTER
863 : SDRTEXTHORZADJUST_BLOCK;
864
865 switch( eTextAnchor )
866 {
867 case mso_anchorTop:
868 {
869 if ( bVerticalText )
870 eTHA = SDRTEXTHORZADJUST_RIGHT;
871 else
872 eTVA = SDRTEXTVERTADJUST_TOP;
873 }
874 break;
875 case mso_anchorTopCentered:
876 {
877 if ( bVerticalText )
878 eTHA = SDRTEXTHORZADJUST_RIGHT;
879 else
880 eTVA = SDRTEXTVERTADJUST_TOP;
881 }
882 break;
883 case mso_anchorMiddle:
884 break;
885 case mso_anchorMiddleCentered:
886 break;
887 case mso_anchorBottom:
888 {
889 if ( bVerticalText )
890 eTHA = SDRTEXTHORZADJUST_LEFT;
891 else
892 eTVA = SDRTEXTVERTADJUST_BOTTOM;
893 }
894 break;
895 case mso_anchorBottomCentered:
896 {
897 if ( bVerticalText )
898 eTHA = SDRTEXTHORZADJUST_LEFT;
899 else
900 eTVA = SDRTEXTVERTADJUST_BOTTOM;
901 }
902 break;
903 default:
904 ;
905 }
906
907 aSet.Put( SdrTextVertAdjustItem( eTVA ) );
908 aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
909
910 if (pObj != nullptr)
911 {
912 pObj->SetMergedItemSet(aSet);
913
914 if (bVerticalText)
915 {
916 SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
917 if (pTextObj)
918 pTextObj->SetVerticalWriting(true);
919 }
920
921 if ( bIsSimpleDrawingTextBox )
922 {
923 if ( nTextRotationAngle )
924 {
925 tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
926 rTextRect.GetWidth() : rTextRect.GetHeight();
927 nMinWH /= 2;
928 Point aPivot(rTextRect.TopLeft());
929 aPivot.AdjustX(nMinWH );
930 aPivot.AdjustY(nMinWH );
931 pObj->NbcRotate(aPivot, nTextRotationAngle);
932 }
933 }
934
935 if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
936 {
937 SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
938 if (pCustomShape)
939 {
940 double fExtraTextRotation = 0.0;
941 if ( mnFix16Angle && !( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 4 ) )
942 { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
943 fExtraTextRotation = -mnFix16Angle.get();
944 }
945 if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text
946 { // remains unchanged, so we have to take back the flipping here
947 fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped.
948 }
949 fExtraTextRotation += nTextRotationAngle.get();
950 if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
951 {
952 fExtraTextRotation /= 100.0;
953 SdrCustomShapeGeometryItem aGeometryItem( pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
954 css::beans::PropertyValue aPropVal;
955 aPropVal.Name = "TextRotateAngle";
956 aPropVal.Value <<= fExtraTextRotation;
957 aGeometryItem.SetPropertyValue( aPropVal );
958 pCustomShape->SetMergedItem( aGeometryItem );
959 }
960 }
961 }
962 else if ( mnFix16Angle )
963 {
964 // rotate text with shape ?
965 pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
966 }
967 }
968 }
969 else if( !pObj )
970 {
971 // simple rectangular objects are ignored by ImportObj() :-(
972 // this is OK for Draw but not for Calc and Writer
973 // cause here these objects have a default border
974 pObj = new SdrRectObj(
975 *pSdrModel,
976 rTextRect);
977
978 SfxItemSet aSet( pSdrModel->GetItemPool() );
979 ApplyAttributes( rSt, aSet, rObjData );
980
981 SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
982 if( SfxItemState::DEFAULT == eState )
983 aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
984 pObj->SetMergedItemSet(aSet);
985 }
986
987 // Means that fBehindDocument is set
988 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
989 pImpRec->bDrawHell = true;
990 else
991 pImpRec->bDrawHell = false;
992 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
993 pImpRec->bHidden = true;
994 pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
995
996 if ( nTextId )
997 {
998 pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
999 pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
1000 }
1001
1002 pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
1003 o3tl::Length::emu, o3tl::Length::twip);
1004 pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
1005 o3tl::Length::emu, o3tl::Length::twip);
1006 pImpRec->nDxWrapDistRight
1007 = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistRight, 114935), o3tl::Length::emu,
1008 o3tl::Length::twip);
1009 pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
1010 o3tl::Length::emu, o3tl::Length::twip);
1011 // 16.16 fraction times total image width or height, as appropriate.
1012
1013 if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt))
1014 {
1015 pImpRec->pWrapPolygon.reset();
1016
1017 sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0);
1018 rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
1019 bool bOk = false;
1020 if (nNumElemVert && (nElemSizeVert == 8 || nElemSizeVert == 4))
1021 {
1022 //check if there is enough data in the file to make the
1023 //record sane
1024 // coverity[tainted_data : FALSE] - nElemSizeVert is either 8 or 4 so it has been sanitized
1025 bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert;
1026 }
1027 if (bOk)
1028 {
1029 pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
1030 for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
1031 {
1032 sal_Int32 nX(0), nY(0);
1033 if (nElemSizeVert == 8)
1034 rSt.ReadInt32( nX ).ReadInt32( nY );
1035 else
1036 {
1037 sal_Int16 nSmallX(0), nSmallY(0);
1038 rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
1039 nX = nSmallX;
1040 nY = nSmallY;
1041 }
1042 (*(pImpRec->pWrapPolygon))[i].setX( nX );
1043 (*(pImpRec->pWrapPolygon))[i].setY( nY );
1044 }
1045 }
1046 }
1047
1048 pImpRec->nCropFromTop = GetPropertyValue(
1049 DFF_Prop_cropFromTop, 0 );
1050 pImpRec->nCropFromBottom = GetPropertyValue(
1051 DFF_Prop_cropFromBottom, 0 );
1052 pImpRec->nCropFromLeft = GetPropertyValue(
1053 DFF_Prop_cropFromLeft, 0 );
1054 pImpRec->nCropFromRight = GetPropertyValue(
1055 DFF_Prop_cropFromRight, 0 );
1056
1057 sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
1058
1059 if ( !IsHardAttribute( DFF_Prop_fLine ) &&
1060 pImpRec->eShapeType == mso_sptPictureFrame )
1061 {
1062 nLineFlags &= ~0x08;
1063 }
1064
1065 pImpRec->eLineStyle = (nLineFlags & 8)
1066 ? static_cast<MSO_LineStyle>(GetPropertyValue(
1067 DFF_Prop_lineStyle,
1068 mso_lineSimple ))
1069 : MSO_LineStyle(USHRT_MAX);
1070 pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
1071 DFF_Prop_lineDashing, mso_lineSolid ));
1072
1073 pImpRec->nFlags = rObjData.nSpFlags;
1074
1075 if( pImpRec->nShapeId )
1076 {
1077 auto nShapeId = pImpRec->nShapeId;
1078 auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16)
1079 + pImpRec->aTextId.nSequence;
1080 // Complement Import Record List
1081 pImpRec->pObj = pObj;
1082 rImportData.insert(std::move(pImpRec));
1083
1084 // Complement entry in Z Order List with a pointer to this Object
1085 // Only store objects which are not deep inside the tree
1086 if( ( rObjData.nCalledByGroup == 0 )
1087 ||
1088 ( (rObjData.nSpFlags & ShapeFlag::Group)
1089 && (rObjData.nCalledByGroup < 2) )
1090 )
1091 {
1092 StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
1093 }
1094 }
1095 else
1096 pImpRec.reset();
1097 }
1098
1099 sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
1100 if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
1101 {
1102 SvMemoryStream aMemStream;
1103 struct HyperLinksTable hlStr;
1104 aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
1105
1106 // copy from DFF stream to memory stream
1107 std::vector< sal_uInt8 > aBuffer( nBufferSize );
1108 if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
1109 {
1110 aMemStream.WriteBytes(aBuffer.data(), nBufferSize);
1111 sal_uInt64 nStreamSize = aMemStream.TellEnd();
1112 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
1113 bool bRet = 4 <= nStreamSize;
1114 sal_uInt16 nRawRecId,nRawRecSize;
1115 if( bRet )
1116 aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize );
1117 SwDocShell* pDocShell = m_rReader.m_pDocShell;
1118 if (pDocShell)
1119 {
1120 m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
1121 }
1122 }
1123
1124 if (pObj && !hlStr.hLinkAddr.isEmpty())
1125 {
1126 SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
1127 if( pInfo )
1128 {
1129 pInfo->SetShapeId( rObjData.nShapeId );
1130 pInfo->SetHlink( hlStr.hLinkAddr );
1131 if (!hlStr.tarFrame.isEmpty())
1132 pInfo->SetTarFrame( hlStr.tarFrame );
1133 OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt );
1134 if (!aNameStr.isEmpty())
1135 pInfo->SetName( aNameStr );
1136 }
1137 }
1138 }
1139
1140 return pObj;
1141 }
1142
1143 /**
1144 * Special FastSave - Attributes
1145 */
Read_StyleCode(sal_uInt16,const sal_uInt8 * pData,short nLen)1146 void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
1147 {
1148 if (nLen < 0)
1149 {
1150 m_bCpxStyle = false;
1151 return;
1152 }
1153 sal_uInt16 nColl = 0;
1154 if (m_xWwFib->GetFIBVersion() <= ww::eWW2)
1155 nColl = *pData;
1156 else
1157 nColl = SVBT16ToUInt16(pData);
1158 if (nColl < m_vColl.size())
1159 {
1160 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[nColl] );
1161 m_bCpxStyle = true;
1162 }
1163 }
1164
1165 /**
1166 * Read_Majority is for Majority (103) and Majority50 (108)
1167 */
Read_Majority(sal_uInt16,const sal_uInt8 *,short)1168 void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
1169 {
1170 }
1171
1172 /**
1173 * Stack
1174 */
NewAttr(const SwPosition & rPos,const SfxPoolItem & rAttr)1175 void SwWW8FltControlStack::NewAttr(const SwPosition& rPos,
1176 const SfxPoolItem& rAttr)
1177 {
1178 OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put"
1179 "fields into the control stack");
1180 OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put"
1181 "input fields into the control stack");
1182 OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put"
1183 "annotations into the control stack");
1184 OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put"
1185 "redlines into the control stack");
1186 SwFltControlStack::NewAttr(rPos, rAttr);
1187 }
1188
SetAttr(const SwPosition & rPos,sal_uInt16 nAttrId,bool bTstEnd,tools::Long nHand,bool)1189 SwFltStackEntry* SwWW8FltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId,
1190 bool bTstEnd, tools::Long nHand, bool )
1191 {
1192 SwFltStackEntry *pRet = nullptr;
1193 // Doing a textbox, and using the control stack only as a temporary
1194 // collection point for properties which will are not to be set into
1195 // the real document
1196 if (m_rReader.m_xPlcxMan && m_rReader.m_xPlcxMan->GetDoingDrawTextBox())
1197 {
1198 size_t nCnt = size();
1199 for (size_t i=0; i < nCnt; ++i)
1200 {
1201 SwFltStackEntry& rEntry = (*this)[i];
1202 if (nAttrId == rEntry.m_pAttr->Which())
1203 {
1204 DeleteAndDestroy(i--);
1205 --nCnt;
1206 }
1207 }
1208 }
1209 else // Normal case, set the attribute into the document
1210 pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
1211 return pRet;
1212 }
1213
GetListFirstLineIndent(const SwNumFormat & rFormat)1214 tools::Long GetListFirstLineIndent(const SwNumFormat &rFormat)
1215 {
1216 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1217 "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1218
1219 SvxAdjust eAdj = rFormat.GetNumAdjust();
1220 tools::Long nReverseListIndented;
1221 if (eAdj == SvxAdjust::Right)
1222 nReverseListIndented = -rFormat.GetCharTextDistance();
1223 else if (eAdj == SvxAdjust::Center)
1224 nReverseListIndented = rFormat.GetFirstLineOffset()/2;
1225 else
1226 nReverseListIndented = rFormat.GetFirstLineOffset();
1227 return nReverseListIndented;
1228 }
1229
lcl_GetTrueMargin(SvxFirstLineIndentItem const & rFirstLine,SvxTextLeftMarginItem const & rLeftMargin,const SwNumFormat & rFormat,tools::Long & rFirstLinePos)1230 static tools::Long lcl_GetTrueMargin(SvxFirstLineIndentItem const& rFirstLine,
1231 SvxTextLeftMarginItem const& rLeftMargin, const SwNumFormat &rFormat,
1232 tools::Long &rFirstLinePos)
1233 {
1234 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1235 "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1236
1237 const tools::Long nBodyIndent = rLeftMargin.GetTextLeft();
1238 const tools::Long nFirstLineDiff = rFirstLine.GetTextFirstLineOffset();
1239 rFirstLinePos = nBodyIndent + nFirstLineDiff;
1240
1241 const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
1242 const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
1243 tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
1244
1245 return std::max<tools::Long>(nExtraListIndent, 0);
1246 }
1247
1248 // #i103711#
1249 // #i105414#
SyncIndentWithList(SvxFirstLineIndentItem & rFirstLine,SvxTextLeftMarginItem & rLeftMargin,const SwNumFormat & rFormat,const bool bFirstLineOfstSet,const bool bLeftIndentSet)1250 void SyncIndentWithList( SvxFirstLineIndentItem & rFirstLine,
1251 SvxTextLeftMarginItem & rLeftMargin,
1252 const SwNumFormat &rFormat,
1253 const bool bFirstLineOfstSet,
1254 const bool bLeftIndentSet )
1255 {
1256 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
1257 {
1258 tools::Long nWantedFirstLinePos;
1259 tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, rLeftMargin, rFormat, nWantedFirstLinePos);
1260 rLeftMargin.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
1261 rFirstLine.SetTextFirstLineOffset(0);
1262 }
1263 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
1264 {
1265 if ( !bFirstLineOfstSet && bLeftIndentSet &&
1266 rFormat.GetFirstLineIndent() != 0 )
1267 {
1268 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1269 }
1270 else if ( bFirstLineOfstSet && !bLeftIndentSet &&
1271 rFormat.GetIndentAt() != 0 )
1272 {
1273 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1274 }
1275 else if (!bFirstLineOfstSet && !bLeftIndentSet )
1276 {
1277 if ( rFormat.GetFirstLineIndent() != 0 )
1278 {
1279 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1280 }
1281 if ( rFormat.GetIndentAt() != 0 )
1282 {
1283 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1284 }
1285 }
1286 }
1287 }
1288
GetNumFormatFromStack(const SwPosition & rPos,const SwTextNode & rTextNode)1289 const SwNumFormat* SwWW8FltControlStack::GetNumFormatFromStack(const SwPosition &rPos,
1290 const SwTextNode &rTextNode)
1291 {
1292 const SwNumFormat *pRet = nullptr;
1293 const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
1294 if (pItem && rTextNode.GetNumRule())
1295 {
1296 if (rTextNode.IsCountedInList())
1297 {
1298 OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue());
1299 const SwNumRule *pRule = m_rDoc.FindNumRulePtr(sName);
1300 if (pRule)
1301 pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel());
1302 }
1303 }
1304 return pRet;
1305 }
1306
SetAttrInDoc(const SwPosition & rTmpPos,SwFltStackEntry & rEntry)1307 void SwWW8ReferencedFltEndStack::SetAttrInDoc( const SwPosition& rTmpPos,
1308 SwFltStackEntry& rEntry )
1309 {
1310 switch( rEntry.m_pAttr->Which() )
1311 {
1312 case RES_FLTR_BOOKMARK:
1313 {
1314 // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content
1315 // and which is not referenced.
1316 bool bInsertBookmarkIntoDoc = true;
1317
1318 SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
1319 if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
1320 {
1321 const OUString& rName = pFltBookmark->GetName();
1322 std::set< OUString, SwWW8::ltstr >::const_iterator aResult = m_aReferencedTOCBookmarks.find(rName);
1323 if ( aResult == m_aReferencedTOCBookmarks.end() )
1324 {
1325 bInsertBookmarkIntoDoc = false;
1326 }
1327 }
1328 if ( bInsertBookmarkIntoDoc )
1329 {
1330 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1331 }
1332 break;
1333 }
1334 default:
1335 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1336 break;
1337 }
1338
1339 }
1340
SetAttrInDoc(const SwPosition & rTmpPos,SwFltStackEntry & rEntry)1341 void SwWW8FltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
1342 SwFltStackEntry& rEntry)
1343 {
1344 switch (rEntry.m_pAttr->Which())
1345 {
1346 case RES_LR_SPACE:
1347 assert(false);
1348 break;
1349 case RES_MARGIN_FIRSTLINE:
1350 case RES_MARGIN_TEXTLEFT:
1351 {
1352 /*
1353 Loop over the affected nodes and
1354 a) convert the word style absolute indent to indent relative
1355 to any numbering indent active on the nodes
1356 b) adjust the writer style tabstops relative to the old
1357 paragraph indent to be relative to the new paragraph indent
1358 */
1359 SwPaM aRegion(rTmpPos);
1360 if (rEntry.MakeRegion(aRegion, SwFltStackEntry::RegionMode::NoCheck))
1361 {
1362 SvxFirstLineIndentItem firstLineNew(RES_MARGIN_FIRSTLINE);
1363 SvxTextLeftMarginItem leftMarginNew(RES_MARGIN_TEXTLEFT);
1364 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1365 {
1366 SvxFirstLineIndentItem const firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
1367 firstLineNew.SetTextFirstLineOffset(firstLineEntry.GetTextFirstLineOffset(), firstLineEntry.GetPropTextFirstLineOffset());
1368 firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
1369 }
1370 else
1371 {
1372 SvxTextLeftMarginItem const leftMarginEntry(*static_cast<SvxTextLeftMarginItem*>(rEntry.m_pAttr.get()));
1373 leftMarginNew.SetTextLeft(leftMarginEntry.GetTextLeft(), leftMarginEntry.GetPropLeft());
1374 }
1375 SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
1376 SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
1377 for(; nStart <= nEnd; ++nStart)
1378 {
1379 SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
1380 if (!pNode || !pNode->IsTextNode())
1381 continue;
1382
1383 SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
1384 SvxFirstLineIndentItem firstLineOld(pNd->GetAttr(RES_MARGIN_FIRSTLINE));
1385 SvxTextLeftMarginItem leftMarginOld(pNd->GetAttr(RES_MARGIN_TEXTLEFT));
1386 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1387 {
1388 leftMarginNew.SetTextLeft(leftMarginOld.GetTextLeft(), leftMarginOld.GetPropLeft());
1389 }
1390 else
1391 {
1392 firstLineNew.SetTextFirstLineOffset(firstLineOld.GetTextFirstLineOffset(), firstLineOld.GetPropTextFirstLineOffset());
1393 firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
1394 }
1395
1396 SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
1397
1398 const SwNumFormat* pNum
1399 = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
1400 if (!pNum)
1401 {
1402 pNum = GetNumFormatFromTextNode(*pTextNode);
1403 }
1404
1405 if ( pNum )
1406 {
1407 // #i103711#
1408 const bool bFirstLineIndentSet =
1409 ( m_rReader.m_aTextNodesHavingFirstLineOfstSet.end() !=
1410 m_rReader.m_aTextNodesHavingFirstLineOfstSet.find( pNode ) );
1411 // #i105414#
1412 const bool bLeftIndentSet =
1413 ( m_rReader.m_aTextNodesHavingLeftIndentSet.end() !=
1414 m_rReader.m_aTextNodesHavingLeftIndentSet.find( pNode ) );
1415 SyncIndentWithList(firstLineNew, leftMarginNew, *pNum,
1416 bFirstLineIndentSet,
1417 bLeftIndentSet );
1418 }
1419
1420 if (firstLineNew != firstLineOld)
1421 {
1422 if (nStart == aRegion.Start()->GetNodeIndex())
1423 {
1424 pNd->SetAttr(firstLineNew);
1425 }
1426 }
1427 if (leftMarginNew != leftMarginOld)
1428 {
1429 pNd->SetAttr(leftMarginNew);
1430 }
1431 }
1432 }
1433 }
1434 break;
1435
1436 case RES_TXTATR_FIELD:
1437 OSL_ENSURE(false, "What is a field doing in the control stack,"
1438 "probably should have been in the endstack");
1439 break;
1440
1441 case RES_TXTATR_ANNOTATION:
1442 OSL_ENSURE(false, "What is an annotation doing in the control stack,"
1443 "probably should have been in the endstack");
1444 break;
1445
1446 case RES_TXTATR_INPUTFIELD:
1447 OSL_ENSURE(false, "What is an input field doing in the control stack,"
1448 "probably should have been in the endstack");
1449 break;
1450
1451 case RES_TXTATR_INETFMT:
1452 {
1453 SwPaM aRegion(rTmpPos);
1454 if (rEntry.MakeRegion(aRegion, SwFltStackEntry::RegionMode::NoCheck))
1455 {
1456 SwFrameFormat *pFrame;
1457 // If we have just one single inline graphic then
1458 // don't insert a field for the single frame, set
1459 // the frames hyperlink field attribute directly.
1460 pFrame = SwWW8ImplReader::ContainsSingleInlineGraphic(aRegion);
1461 if (nullptr != pFrame)
1462 {
1463 const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
1464 rEntry.m_pAttr.get());
1465 SwFormatURL aURL;
1466 aURL.SetURL(pAttr->GetValue(), false);
1467 aURL.SetTargetFrameName(pAttr->GetTargetFrame());
1468 pFrame->SetFormatAttr(aURL);
1469 }
1470 else
1471 {
1472 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
1473 }
1474 }
1475 }
1476 break;
1477 default:
1478 SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
1479 break;
1480 }
1481 }
1482
GetFormatAttr(const SwPosition & rPos,sal_uInt16 nWhich)1483 const SfxPoolItem* SwWW8FltControlStack::GetFormatAttr(const SwPosition& rPos,
1484 sal_uInt16 nWhich)
1485 {
1486 const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
1487 if (!pItem)
1488 {
1489 SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
1490 if (!pNd)
1491 pItem = &m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(nWhich);
1492 else
1493 {
1494 /*
1495 If we're hunting for the indent on a paragraph and need to use the
1496 parent style indent, then return the indent in msword format, and
1497 not writer format, because that's the style that the filter works
1498 in (naturally)
1499 */
1500 if (nWhich == RES_MARGIN_FIRSTLINE
1501 || nWhich == RES_MARGIN_TEXTLEFT
1502 || nWhich == RES_MARGIN_RIGHT)
1503 {
1504 SfxItemState eState = SfxItemState::DEFAULT;
1505 if (const SfxItemSet *pSet = pNd->GetpSwAttrSet())
1506 eState = pSet->GetItemState(nWhich, false);
1507 if (eState != SfxItemState::SET && m_rReader.m_nCurrentColl < m_rReader.m_vColl.size())
1508 {
1509 switch (nWhich)
1510 {
1511 case RES_MARGIN_FIRSTLINE:
1512 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordFirstLine.get();
1513 break;
1514 case RES_MARGIN_TEXTLEFT:
1515 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordLeftMargin.get();
1516 break;
1517 case RES_MARGIN_RIGHT:
1518 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordRightMargin.get();
1519 break;
1520 }
1521 }
1522 }
1523
1524 /*
1525 If we're hunting for a character property, try and exact position
1526 within the text node for lookup
1527 */
1528 if (pNd->IsTextNode())
1529 {
1530 const sal_Int32 nPos = rPos.GetContentIndex();
1531 m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
1532 if (pNd->GetTextNode()->GetParaAttr(*m_xScratchSet, nPos, nPos))
1533 pItem = m_xScratchSet->GetItem(nWhich);
1534 }
1535
1536 if (!pItem)
1537 pItem = &pNd->GetAttr(nWhich);
1538 }
1539 }
1540 return pItem;
1541 }
1542
GetStackAttr(const SwPosition & rPos,sal_uInt16 nWhich)1543 const SfxPoolItem* SwWW8FltControlStack::GetStackAttr(const SwPosition& rPos,
1544 sal_uInt16 nWhich)
1545 {
1546 SwFltPosition aFltPos(rPos);
1547
1548 size_t nSize = size();
1549 while (nSize)
1550 {
1551 const SwFltStackEntry& rEntry = (*this)[ --nSize ];
1552 if (rEntry.m_pAttr->Which() == nWhich)
1553 {
1554 if ( (rEntry.m_bOpen) ||
1555 (
1556 (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) &&
1557 (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) &&
1558 (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) &&
1559 (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent)
1560 )
1561 )
1562 /*
1563 * e.g. half-open range [0-3) so asking for properties at 3
1564 * means props that end at 3 are not included
1565 */
1566 {
1567 return rEntry.m_pAttr.get();
1568 }
1569 }
1570 }
1571 return nullptr;
1572 }
1573
IsFootnoteEdnBkmField(const SwFormatField & rFormatField,sal_uInt16 & rBkmNo)1574 bool SwWW8FltRefStack::IsFootnoteEdnBkmField(
1575 const SwFormatField& rFormatField,
1576 sal_uInt16& rBkmNo)
1577 {
1578 const SwField* pField = rFormatField.GetField();
1579 sal_uInt16 nSubType;
1580 if(pField && (SwFieldIds::GetRef == pField->Which())
1581 && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType))
1582 && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty())
1583 {
1584 const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
1585 IDocumentMarkAccess::const_iterator_t ppBkmk =
1586 pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
1587 if(ppBkmk != pMarkAccess->getAllMarksEnd())
1588 {
1589 // find Sequence No of corresponding Foot-/Endnote
1590 rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
1591 return true;
1592 }
1593 }
1594 return false;
1595 }
1596
SetAttrInDoc(const SwPosition & rTmpPos,SwFltStackEntry & rEntry)1597 void SwWW8FltRefStack::SetAttrInDoc(const SwPosition& rTmpPos,
1598 SwFltStackEntry& rEntry)
1599 {
1600 switch (rEntry.m_pAttr->Which())
1601 {
1602 /*
1603 Look up these in our lists of bookmarks that were changed to
1604 variables, and replace the ref field with a var field, otherwise
1605 do normal (?) strange stuff
1606 */
1607 case RES_TXTATR_FIELD:
1608 case RES_TXTATR_ANNOTATION:
1609 case RES_TXTATR_INPUTFIELD:
1610 {
1611 SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
1612
1613 SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
1614 SwField* pField = rFormatField.GetField();
1615
1616 if (!RefToVar(pField, rEntry))
1617 {
1618 sal_uInt16 nBkmNo;
1619 if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
1620 {
1621 ::sw::mark::IMark const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
1622
1623 const SwPosition& rBkMrkPos = pMark->GetMarkPos();
1624
1625 SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
1626 if( pText && rBkMrkPos.GetContentIndex() )
1627 {
1628 SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
1629 rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
1630 if( pFootnote )
1631 {
1632 sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
1633
1634 static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
1635
1636 if( pFootnote->GetFootnote().IsEndNote() )
1637 static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
1638 }
1639 }
1640 }
1641 }
1642
1643 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aPaM, *rEntry.m_pAttr);
1644 MoveAttrs(*aPaM.GetPoint());
1645 }
1646 break;
1647 case RES_FLTR_TOX:
1648 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1649 break;
1650 default:
1651 case RES_FLTR_BOOKMARK:
1652 OSL_ENSURE(false, "EndStck used with non field, not what we want");
1653 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1654 break;
1655 }
1656 }
1657
1658 /*
1659 For styles we will do our tabstop arithmetic in word style and adjust them to
1660 writer style after all the styles have been finished and the dust settles as
1661 to what affects what.
1662
1663 For explicit attributes we turn the adjusted writer tabstops back into 0 based
1664 word indexes and we'll turn them back into writer indexes when setting them
1665 into the document. If explicit left indent exist which affects them, then this
1666 is handled when the explicit left indent is set into the document
1667 */
Read_Tab(sal_uInt16,const sal_uInt8 * pData,short nLen)1668 void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
1669 {
1670 if (nLen < 0)
1671 {
1672 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_TABSTOP);
1673 return;
1674 }
1675
1676 sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
1677 const sal_uInt8* pDel = pData + 1; // Del - Array
1678
1679 sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
1680 const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
1681
1682 short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
1683 if (nRequiredLength > nLen)
1684 {
1685 // would require more data than available to describe!
1686 // discard invalid record
1687 nIns = 0;
1688 nDel = 0;
1689 }
1690
1691 WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
1692
1693 std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
1694
1695 const SwFormat * pSty = nullptr;
1696 sal_uInt16 nTabBase;
1697 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
1698 {
1699 nTabBase = m_vColl[m_nCurrentColl].m_nBase;
1700 if (nTabBase < m_vColl.size()) // Based On
1701 pSty = m_vColl[nTabBase].m_pFormat;
1702 }
1703 else
1704 { // Text
1705 nTabBase = m_nCurrentColl;
1706 if (m_nCurrentColl < m_vColl.size())
1707 pSty = m_vColl[m_nCurrentColl].m_pFormat;
1708 //TODO: figure out else here
1709 }
1710
1711 bool bFound = false;
1712 std::unordered_set<size_t> aLoopWatch;
1713 while (pSty && !bFound)
1714 {
1715 const SvxTabStopItem* pTabs;
1716 bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
1717 &pTabs) == SfxItemState::SET;
1718 if( bFound )
1719 {
1720 aAttr.reset(pTabs->Clone());
1721 }
1722 else
1723 {
1724 sal_uInt16 nOldTabBase = nTabBase;
1725 // If based on another
1726 if (nTabBase < m_vColl.size())
1727 nTabBase = m_vColl[nTabBase].m_nBase;
1728
1729 if (
1730 nTabBase < m_vColl.size() &&
1731 nOldTabBase != nTabBase &&
1732 nTabBase != ww::stiNil
1733 )
1734 {
1735 // #i61789: Stop searching when next style is the same as the
1736 // current one (prevent loop)
1737 aLoopWatch.insert(reinterpret_cast<size_t>(pSty));
1738 if (nTabBase < m_vColl.size())
1739 pSty = m_vColl[nTabBase].m_pFormat;
1740 //TODO figure out the else branch
1741
1742 if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
1743 aLoopWatch.end())
1744 pSty = nullptr;
1745 }
1746 else
1747 pSty = nullptr; // Give up on the search
1748 }
1749 }
1750
1751 SvxTabStop aTabStop;
1752 for (short i=0; i < nDel; ++i)
1753 {
1754 sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
1755 if( nPos != SVX_TAB_NOTFOUND )
1756 aAttr->Remove( nPos );
1757 }
1758
1759 for (short i=0; i < nIns; ++i)
1760 {
1761 short nPos = SVBT16ToUInt16(pIns + i*2);
1762 aTabStop.GetTabPos() = nPos;
1763 switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
1764 {
1765 case 0:
1766 aTabStop.GetAdjustment() = SvxTabAdjust::Left;
1767 break;
1768 case 1:
1769 aTabStop.GetAdjustment() = SvxTabAdjust::Center;
1770 break;
1771 case 2:
1772 aTabStop.GetAdjustment() = SvxTabAdjust::Right;
1773 break;
1774 case 3:
1775 aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
1776 break;
1777 case 4:
1778 continue; // Ignore Bar
1779 }
1780
1781 switch( pTyp[i].aBits1 >> 3 & 0x7 )
1782 {
1783 case 0:
1784 aTabStop.GetFill() = ' ';
1785 break;
1786 case 1:
1787 aTabStop.GetFill() = '.';
1788 break;
1789 case 2:
1790 aTabStop.GetFill() = '-';
1791 break;
1792 case 3:
1793 case 4:
1794 aTabStop.GetFill() = '_';
1795 break;
1796 }
1797
1798 sal_uInt16 nPos2 = aAttr->GetPos( nPos );
1799 if (nPos2 != SVX_TAB_NOTFOUND)
1800 aAttr->Remove(nPos2); // Or else Insert() refuses
1801 aAttr->Insert(aTabStop);
1802 }
1803
1804 if (nIns || nDel)
1805 NewAttr(*aAttr);
1806 else
1807 {
1808 // Here we have a tab definition which inserts no extra tabs, or deletes
1809 // no existing tabs. An older version of writer is probably the creator
1810 // of the document :-( . So if we are importing a style we can just
1811 // ignore it. But if we are importing into text we cannot as during
1812 // text SwWW8ImplReader::Read_Tab is called at the begin and end of
1813 // the range the attrib affects, and ignoring it would upset the
1814 // balance
1815 if (!m_pCurrentColl) // not importing into a style
1816 {
1817 SvxTabStopItem aOrig = pSty ?
1818 pSty->GetFormatAttr(RES_PARATR_TABSTOP) :
1819 m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(RES_PARATR_TABSTOP);
1820 NewAttr(aOrig);
1821 }
1822 }
1823 }
1824
1825 /**
1826 * DOP
1827 */
ImportDop()1828 void SwWW8ImplReader::ImportDop()
1829 {
1830 // correct the LastPrinted date in DocumentProperties
1831 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1832 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1833 uno::Reference<document::XDocumentProperties> xDocuProps(
1834 xDPS->getDocumentProperties());
1835 OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null");
1836 if (xDocuProps.is())
1837 {
1838 DateTime aLastPrinted(
1839 msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
1840 ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
1841 xDocuProps->setPrintDate(uDT);
1842 }
1843
1844 // COMPATIBILITY FLAGS START
1845
1846 // #i78951# - remember the unknown compatibility options
1847 // so as to export them out
1848 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions1(m_xWDop->GetCompatibilityOptions());
1849 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions2(m_xWDop->GetCompatibilityOptions2());
1850
1851 // The distance between two paragraphs is the sum of the bottom distance of
1852 // the first paragraph and the top distance of the second one
1853 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, m_xWDop->fDontUseHTMLAutoSpacing);
1854 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, true );
1855 // move tabs on alignment
1856 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, true);
1857 // #i24363# tab stops relative to indent
1858 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, false);
1859 // tdf#117923
1860 m_rDoc.getIDocumentSettingAccess().set(
1861 DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true);
1862 m_rDoc.getIDocumentSettingAccess().set(
1863 DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true);
1864 // tdf#128195
1865 m_rDoc.getIDocumentSettingAccess().set(
1866 DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true);
1867 m_rDoc.getIDocumentSettingAccess().set(
1868 DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA, true);
1869 m_rDoc.getIDocumentSettingAccess().set(
1870 DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND, true);
1871
1872 // Import Default Tabs
1873 tools::Long nDefTabSiz = m_xWDop->dxaTab;
1874 if( nDefTabSiz < 56 )
1875 nDefTabSiz = 709;
1876
1877 // We want exactly one DefaultTab
1878 SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP );
1879 const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default;
1880
1881 m_rDoc.GetAttrPool().SetUserDefaultItem( aNewTab );
1882
1883 // Import zoom factor
1884 if (m_xWDop->wScaleSaved)
1885 {
1886 //Import zoom type
1887 sal_Int16 nZoomType;
1888 switch (m_xWDop->zkSaved) {
1889 case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break;
1890 case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break;
1891 case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break;
1892 default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break;
1893 }
1894 uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({
1895 { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) },
1896 { "VisibleBottom", uno::Any(sal_Int32(0)) },
1897 { "ZoomType", uno::Any(nZoomType) }
1898 }));
1899
1900 rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
1901 xBox->insertByIndex(sal_Int32(0), uno::Any(aViewProps));
1902 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
1903 xViewDataSupplier->setViewData(xBox);
1904 }
1905
1906 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_VIRTUAL_DEVICE, !m_xWDop->fUsePrinterMetrics);
1907 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, true);
1908 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, true );
1909
1910 // No vertical offsets would lead to e.g. overlap of table and fly frames.
1911 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS, true );
1912
1913 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, !m_xWDop->fNoLeading);
1914 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, false);
1915 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, false); // #i47448#
1916 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER, true); // tdf#159382
1917 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, !m_xWDop->fExpShRtn); // #i49277#, #i56856#
1918 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, false); // #i53199#
1919 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, false);
1920
1921 // #i25901# - set new compatibility option
1922 // 'Add paragraph and table spacing at bottom of table cells'
1923 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, true);
1924 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, true);
1925
1926 // #i11860# - set new compatibility option
1927 // 'Use former object positioning' to <false>
1928 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, false);
1929
1930 // #i27767# - set new compatibility option
1931 // 'Consider Wrapping mode when positioning object' to <true>
1932 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, true);
1933
1934 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, false); // #i13832#, #i24135#
1935
1936 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
1937
1938 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, true); // #i3952#
1939
1940 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, true);
1941 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, true);
1942 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, true);
1943 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, true);
1944 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
1945 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true);
1946 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true);
1947 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
1948 // rely on default for HYPHENATE_URLS=false
1949 // rely on default for APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH=true
1950
1951 IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
1952 if (m_xWDop->fDontBreakWrappedTables)
1953 {
1954 rIDSA.set(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES, true);
1955 }
1956
1957 // COMPATIBILITY FLAGS END
1958
1959 // Import magic doptypography information, if it's there
1960 if (m_xWwFib->m_nFib > 105)
1961 ImportDopTypography(m_xWDop->doptypography);
1962
1963 // disable form design mode to be able to use imported controls directly
1964 // #i31239# always disable form design mode, not only in protected docs
1965 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1966 if (xDocProps.is())
1967 {
1968 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1969 if (xInfo.is())
1970 {
1971 if (xInfo->hasPropertyByName(u"ApplyFormDesignMode"_ustr))
1972 xDocProps->setPropertyValue(u"ApplyFormDesignMode"_ustr, css::uno::Any(false));
1973 }
1974
1975 // for the benefit of DOCX - if this is ever saved in that format.
1976 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(u"InteropGrabBag"_ustr));
1977 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1978 { "name", uno::Any(u"compatibilityMode"_ustr) },
1979 { "uri", uno::Any(u"http://schemas.microsoft.com/office/word"_ustr) },
1980 { "val", uno::Any(u"11"_ustr) } //11: Use features specified in MS-DOC.
1981 }));
1982
1983 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
1984 { "compatSetting", uno::Any(aCompatSetting) }
1985 }));
1986
1987 aGrabBag[u"CompatSettings"_ustr] <<= aValue;
1988 xDocProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag.getAsConstPropertyValueList()));
1989 }
1990
1991 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
1992 // Treat comments-only like read-only since Writer has no support for that.
1993 // Still allow editing of form fields, without requiring the password.
1994 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
1995 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
1996 m_pDocShell->SetModifyPasswordHash(m_xWDop->lKeyProtDoc);
1997 else if ( xDocProps.is() )
1998 {
1999 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(u"InteropGrabBag"_ustr));
2000 aGrabBag[u"FormPasswordHash"_ustr] <<= m_xWDop->lKeyProtDoc;
2001 xDocProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag.getAsConstPropertyValueList()));
2002 }
2003
2004 if (officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get())
2005 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, m_xWDop->fProtEnabled );
2006
2007 if (m_xWDop->iGutterPos)
2008 {
2009 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::GUTTER_AT_TOP, true);
2010 }
2011 }
2012
ImportDopTypography(const WW8DopTypography & rTypo)2013 void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo)
2014 {
2015 switch (rTypo.m_iLevelOfKinsoku)
2016 {
2017 case 2: // custom
2018 {
2019 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
2020 OUString(+rTypo.m_rgxchLPunct));
2021 // unary + makes sure not to accidentally call the deleted
2022 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
2023 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
2024 // prefix leading up to the first NUL
2025 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(rTypo.GetConvertedLang(),
2026 aForbidden);
2027 // Obviously cannot set the standard level 1 for japanese, so
2028 // bail out now while we can.
2029 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
2030 return;
2031 }
2032 break;
2033 default:
2034 break;
2035 }
2036
2037 /*
2038 This MS hack means that level 2 of japanese is not in operation, so we put
2039 in what we know are the MS defaults, there is a complementary reverse
2040 hack in the writer. Its our default as well, but we can set it anyway
2041 as a flag for later.
2042 */
2043 if (!rTypo.m_reserved2)
2044 {
2045 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
2046 WW8DopTypography::JapanNotEndLevel1);
2047 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(LANGUAGE_JAPANESE,aForbidden);
2048 }
2049
2050 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bool(rTypo.m_fKerningPunct));
2051 m_rDoc.getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(rTypo.m_iJustification));
2052 }
2053
2054 /**
2055 * Footnotes and Endnotes
2056 */
WW8ReaderSave(SwWW8ImplReader * pRdr,WW8_CP nStartCp)2057 WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) :
2058 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
2059 mxOldStck(std::move(pRdr->m_xCtrlStck)),
2060 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2061 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2062 mxOldPlcxMan(pRdr->m_xPlcxMan),
2063 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2064 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2065 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2066 mpPrevNumRule(pRdr->m_pPrevNumRule),
2067 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2068 mnInTable(pRdr->m_nInTable),
2069 mnCurrentColl(pRdr->m_nCurrentColl),
2070 mcSymbol(pRdr->m_cSymbol),
2071 mbIgnoreText(pRdr->m_bIgnoreText),
2072 mbSymbol(pRdr->m_bSymbol),
2073 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2074 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2075 mbAnl(pRdr->m_bAnl),
2076 mbInHyperlink(pRdr->m_bInHyperlink),
2077 mbPgSecBreak(pRdr->m_bPgSecBreak),
2078 mbWasParaEnd(pRdr->m_bWasParaEnd),
2079 mbHasBorder(pRdr->m_bHasBorder),
2080 mbFirstPara(pRdr->m_bFirstPara)
2081 {
2082 pRdr->m_bSymbol = false;
2083 pRdr->m_bHdFtFootnoteEdn = true;
2084 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2085 = pRdr->m_bHasBorder = false;
2086 pRdr->m_bFirstPara = true;
2087 pRdr->m_nInTable = 0;
2088 pRdr->m_pPreviousNumPaM = nullptr;
2089 pRdr->m_pPrevNumRule = nullptr;
2090 pRdr->m_nCurrentColl = 0;
2091
2092 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2093 *pRdr));
2094
2095 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2096
2097 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2098
2099 // Save the attribute manager: we need this as the newly created PLCFx Manager
2100 // access the same FKPs as the old one and their Start-End position changes.
2101 if (pRdr->m_xPlcxMan)
2102 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2103
2104 if (nStartCp != -1)
2105 {
2106 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2107 mxOldPlcxMan->GetManType(), nStartCp);
2108 }
2109
2110 maOldApos.push_back(false);
2111 maOldApos.swap(pRdr->m_aApos);
2112 maOldFieldStack.swap(pRdr->m_aFieldStack);
2113 }
2114
Restore(SwWW8ImplReader * pRdr)2115 void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr )
2116 {
2117 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2118 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2119 pRdr->m_pPreviousNumPaM = mpPreviousNumPaM;
2120 pRdr->m_pPrevNumRule = mpPrevNumRule;
2121 pRdr->m_xTableDesc = std::move(mxTableDesc);
2122 pRdr->m_cSymbol = mcSymbol;
2123 pRdr->m_bSymbol = mbSymbol;
2124 pRdr->m_bIgnoreText = mbIgnoreText;
2125 pRdr->m_bHdFtFootnoteEdn = mbHdFtFootnoteEdn;
2126 pRdr->m_bTxbxFlySection = mbTxbxFlySection;
2127 pRdr->m_nInTable = mnInTable;
2128 pRdr->m_bAnl = mbAnl;
2129 pRdr->m_bInHyperlink = mbInHyperlink;
2130 pRdr->m_bWasParaEnd = mbWasParaEnd;
2131 pRdr->m_bPgSecBreak = mbPgSecBreak;
2132 pRdr->m_nCurrentColl = mnCurrentColl;
2133 pRdr->m_bHasBorder = mbHasBorder;
2134 pRdr->m_bFirstPara = mbFirstPara;
2135
2136 // Close all attributes as attributes could be created that extend the Fly
2137 pRdr->DeleteCtrlStack();
2138 pRdr->m_xCtrlStck = std::move(mxOldStck);
2139
2140 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2141
2142 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2143 // place, or somewhere close if that place got destroyed
2144 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2145 pRdr->m_oLastAnchorPos.reset();
2146
2147 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2148
2149 if (xLastAnchorCursor)
2150 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2151
2152 pRdr->DeleteAnchorStack();
2153 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2154
2155 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2156
2157 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2158 pRdr->m_xPlcxMan = mxOldPlcxMan;
2159 if (pRdr->m_xPlcxMan)
2160 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2161 pRdr->m_aApos.swap(maOldApos);
2162 pRdr->m_aFieldStack.swap(maOldFieldStack);
2163 }
2164
Read_HdFtFootnoteText(const SwNodeIndex * pSttIdx,WW8_CP nStartCp,WW8_CP nLen,ManTypes nType)2165 void SwWW8ImplReader::Read_HdFtFootnoteText( const SwNodeIndex* pSttIdx,
2166 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2167 {
2168 if (nStartCp < 0 || nLen < 0)
2169 return;
2170
2171 // Saves Flags (amongst other things) and resets them
2172 WW8ReaderSave aSave( this );
2173
2174 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2175
2176 // Read Text for Header, Footer or Footnote
2177 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2178 aSave.Restore( this );
2179 }
2180
2181 /**
2182 * Use authornames, if not available fall back to initials.
2183 */
Read_And(WW8PLCFManResult * pRes)2184 tools::Long SwWW8ImplReader::Read_And(WW8PLCFManResult* pRes)
2185 {
2186 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2187 if (!pSD)
2188 return 0;
2189
2190 const void* pData = pSD->GetData();
2191 if (!pData)
2192 return 0;
2193
2194 OUString sAuthor;
2195 OUString sInitials;
2196 if( m_bVer67 )
2197 {
2198 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2199 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2200 if (pA)
2201 sAuthor = *pA;
2202 else
2203 {
2204 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2205 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2206 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2207 }
2208 }
2209 else
2210 {
2211 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2212 {
2213 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2214 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2215 OUStringBuffer aBuf;
2216 aBuf.setLength(nLen);
2217 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2218 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2219 sInitials = aBuf.makeStringAndClear();
2220 }
2221
2222 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2223 sAuthor = *pA;
2224 else
2225 sAuthor = sInitials;
2226 }
2227
2228 sal_uInt32 nDateTime = 0;
2229
2230 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2231 {
2232 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2233 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2234 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2235 }
2236
2237 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2238
2239 OUString sText;
2240 std::optional<OutlinerParaObject> pOutliner = ImportAsOutliner( sText, pRes->nCp2OrIdx,
2241 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2242
2243 m_xFormatOfJustInsertedApo.reset();
2244 SwPostItField aPostIt(
2245 static_cast<SwPostItFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), sAuthor,
2246 sText, sInitials, OUString(), aDate );
2247 aPostIt.SetTextObject(std::move(pOutliner));
2248
2249 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2250 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2251 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aEnd, SwFormatField(aPostIt));
2252 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2253 // If this is a range, make sure that it ends after the just inserted character, not before it.
2254 m_xReffedStck->MoveAttrs(*aEnd.GetPoint(), SwFltControlStack::MoveAttrsMode::POSTIT_INSERTED);
2255
2256 return 0;
2257 }
2258
Read_HdFtTextAsHackedFrame(WW8_CP nStart,WW8_CP nLen,SwFrameFormat const & rHdFtFormat,sal_uInt16 nPageWidth)2259 void SwWW8ImplReader::Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen,
2260 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2261 {
2262 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2263 OSL_ENSURE(pSttIdx, "impossible");
2264 if (!pSttIdx)
2265 return;
2266
2267 SwPosition aTmpPos(*m_pPaM->GetPoint());
2268
2269 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2270
2271 // tdf#122425: Explicitly remove borders and spacing
2272 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aFlySet(m_rDoc.GetAttrPool());
2273 Reader::ResetFrameFormatAttrs(aFlySet);
2274
2275 SwFlyFrameFormat* pFrame
2276 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2277
2278 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2279 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2280 pFrame->SetFormatAttr( aAnch );
2281 SwFormatFrameSize aSz(SwFrameSize::Minimum, nPageWidth, MINLAY);
2282 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2283 if( eFrameSize != aSz.GetWidthSizeType() )
2284 aSz.SetWidthSizeType( eFrameSize );
2285 pFrame->SetFormatAttr(aSz);
2286 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2287 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2288
2289 // #i43427# - send frame for header/footer into background.
2290 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2291 SdrObject* pFrameObj = CreateContactObject( pFrame );
2292 OSL_ENSURE( pFrameObj,
2293 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2294 if ( pFrameObj )
2295 {
2296 pFrameObj->SetOrdNum( 0 );
2297 }
2298 MoveInsideFly(pFrame);
2299
2300 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2301
2302 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2303
2304 MoveOutsideFly(pFrame, aTmpPos);
2305 }
2306
Read_HdFtText(WW8_CP nStart,WW8_CP nLen,SwFrameFormat const * pHdFtFormat)2307 void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2308 {
2309 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2310 if (!pSttIdx)
2311 return;
2312
2313 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2314
2315 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2316
2317 *m_pPaM->GetPoint() = std::move(aTmpPos);
2318 }
2319
isValid_HdFt_CP(WW8_CP nHeaderCP) const2320 bool SwWW8ImplReader::isValid_HdFt_CP(WW8_CP nHeaderCP) const
2321 {
2322 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2323 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2324 }
2325
HasOwnHeaderFooter(sal_uInt8 nWhichItems,sal_uInt8 grpfIhdt,int nSect)2326 bool SwWW8ImplReader::HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt,
2327 int nSect)
2328 {
2329 if (m_xHdFt)
2330 {
2331 WW8_CP nStart, nLen;
2332 sal_uInt8 nNumber = 5;
2333
2334 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2335 {
2336 if (nI & nWhichItems)
2337 {
2338 bool bOk = true;
2339 if( m_bVer67 )
2340 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2341 else
2342 {
2343 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2344 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2345 }
2346
2347 if (bOk)
2348 return true;
2349 }
2350 }
2351 }
2352 return false;
2353 }
2354
Read_HdFt(int nSect,const SwPageDesc * pPrev,const wwSection & rSection)2355 void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2356 const wwSection &rSection)
2357 {
2358 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2359 SwPageDesc *pPD = rSection.mpPage;
2360
2361 if( !m_xHdFt )
2362 return;
2363
2364 WW8_CP nStart, nLen;
2365 sal_uInt8 nNumber = 5;
2366
2367 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2368 // corresponding to bit fields in grpfIhdt indicating which
2369 // header/footer(s) are present in this section
2370 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2371 {
2372 if (nI & grpfIhdt)
2373 {
2374 bool bOk = true;
2375 if( m_bVer67 )
2376 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2377 else
2378 {
2379 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2380 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2381 }
2382
2383 bool bUseLeft
2384 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2385 bool bUseFirst
2386 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2387
2388 // If we are loading a first-page header/footer which is not
2389 // actually enabled in this section (it still needs to be
2390 // loaded as it may be inherited by a later section)
2391 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2392
2393 bool bFooter
2394 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2395
2396 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2397 : bUseFirst ? pPD->GetFirstMaster()
2398 : pPD->GetMaster();
2399
2400 SwFrameFormat* pHdFtFormat;
2401 // If we have empty first page header and footer.
2402 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2403 if (bFooter)
2404 {
2405 m_bIsFooter = true;
2406 //#i17196# Cannot have left without right
2407 if (!bDisabledFirst
2408 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2409 pPD->GetMaster().SetFormatAttr(SwFormatFooter(true));
2410 if (bUseLeft)
2411 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2412 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2413 pPD->GetFirstMaster().SetFormatAttr(SwFormatFooter(true));
2414 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2415 }
2416 else
2417 {
2418 m_bIsHeader = true;
2419 //#i17196# Cannot have left without right
2420 if (!bDisabledFirst
2421 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2422 pPD->GetMaster().SetFormatAttr(SwFormatHeader(true));
2423 if (bUseLeft)
2424 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2425 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2426 pPD->GetFirstMaster().SetFormatAttr(SwFormatHeader(true));
2427 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2428 }
2429
2430 if (bOk)
2431 {
2432 bool bHackRequired = false;
2433 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2434 bHackRequired = true;
2435 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2436 bHackRequired = true;
2437
2438 if (bHackRequired)
2439 {
2440 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2441 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2442 }
2443 else
2444 Read_HdFtText(nStart, nLen, pHdFtFormat);
2445 }
2446 else if (pPrev)
2447 CopyPageDescHdFt(pPrev, pPD, nI);
2448
2449 m_bIsHeader = m_bIsFooter = false;
2450 }
2451 }
2452 }
2453
SectionIsProtected(const wwSection & rSection) const2454 bool wwSectionManager::SectionIsProtected(const wwSection &rSection) const
2455 {
2456 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2457 }
2458
SetHdFt(wwSection const & rSection,int nSect,const wwSection * pPrevious)2459 void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2460 const wwSection *pPrevious)
2461 {
2462 // Header/Footer not present
2463 if (!rSection.maSep.grpfIhdt)
2464 return;
2465
2466 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2467 if (rSection.mpPage)
2468 {
2469 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2470 rSection);
2471 }
2472
2473 // Header/Footer - Update Index
2474 // So that the index is still valid later on
2475 if (mrReader.m_xHdFt)
2476 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2477
2478 }
2479
FinalizeTextNode(SwPosition & rPos,bool bAddNew)2480 void SwWW8ImplReader::FinalizeTextNode(SwPosition& rPos, bool bAddNew)
2481 {
2482 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
2483
2484 const SwNumRule* pRule = nullptr;
2485
2486 if (pText != nullptr)
2487 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2488
2489 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2490 // set it as a separate paragraph property, just like we do for DOCX.
2491 // This is only being used for numbering currently, so limiting to that context.
2492 if (pRule)
2493 {
2494 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1, RES_TXTATR_CHARFMT,
2495 RES_TXTATR_CHARFMT, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2496 items(m_pPaM->GetDoc().GetAttrPool());
2497
2498 SfxWhichIter aIter(items);
2499 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2500 {
2501 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2502 if (pItem)
2503 items.Put(*pItem);
2504 }
2505 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
2506 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2507 pText->SetAttr(item);
2508 }
2509
2510 if (
2511 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2512 (m_bParaAutoBefore || m_bParaAutoAfter)
2513 )
2514 {
2515 // If after spacing is set to auto, set the after space to 0
2516 if (m_bParaAutoAfter)
2517 SetLowerSpacing(*m_pPaM, 0);
2518
2519 // If the previous textnode had numbering and
2520 // and before spacing is set to auto, set before space to 0
2521 if(m_pPrevNumRule && m_bParaAutoBefore)
2522 SetUpperSpacing(*m_pPaM, 0);
2523
2524 // If the previous numbering rule was different we need
2525 // to insert a space after the previous paragraph
2526 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2527 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2528
2529 // cache current paragraph
2530 if(m_pPreviousNumPaM)
2531 {
2532 delete m_pPreviousNumPaM;
2533 m_pPreviousNumPaM = nullptr;
2534 }
2535
2536 m_pPreviousNumPaM = new SwPaM(*m_pPaM, m_pPaM);
2537 m_pPrevNumRule = pRule;
2538 }
2539 else if(!pRule && m_pPreviousNumPaM)
2540 {
2541 // If the previous paragraph has numbering but the current one does not
2542 // we need to add a space after the previous paragraph
2543 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2544 delete m_pPreviousNumPaM;
2545 m_pPreviousNumPaM = nullptr;
2546 m_pPrevNumRule = nullptr;
2547 }
2548 else
2549 {
2550 // clear paragraph cache
2551 if(m_pPreviousNumPaM)
2552 {
2553 delete m_pPreviousNumPaM;
2554 m_pPreviousNumPaM = nullptr;
2555 }
2556 m_pPrevNumRule = pRule;
2557 }
2558
2559 // If this is the first paragraph in the document and
2560 // Auto-spacing before paragraph is set,
2561 // set the upper spacing value to 0
2562 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2563 SetUpperSpacing(*m_pPaM, 0);
2564
2565 m_bFirstPara = false;
2566
2567 if (bAddNew)
2568 m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos);
2569
2570 // We can flush all anchored graphics at the end of a paragraph.
2571 m_xAnchorStck->Flush();
2572 }
2573
SetSpacing(SwPaM & rMyPam,int nSpace,bool bIsUpper)2574 bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2575 {
2576 bool bRet = false;
2577 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2578
2579 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2580
2581 if(pULSpaceItem != nullptr)
2582 {
2583 SvxULSpaceItem aUL(*pULSpaceItem);
2584
2585 if(bIsUpper)
2586 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2587 else
2588 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2589
2590 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2591 rMyPam.GetPoint()->SetContent(0);
2592 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2593 rMyPam.GetPoint()->SetContent(nEnd);
2594 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2595 bRet = true;
2596 }
2597 return bRet;
2598 }
2599
SetLowerSpacing(SwPaM & rMyPam,int nSpace)2600 bool SwWW8ImplReader::SetLowerSpacing(SwPaM &rMyPam, int nSpace)
2601 {
2602 return SetSpacing(rMyPam, nSpace, false);
2603 }
2604
SetUpperSpacing(SwPaM & rMyPam,int nSpace)2605 bool SwWW8ImplReader::SetUpperSpacing(SwPaM &rMyPam, int nSpace)
2606 {
2607 return SetSpacing(rMyPam, nSpace, true);
2608 }
2609
TabRowSprm(int nLevel) const2610 sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2611 {
2612 if (m_bVer67)
2613 return NS_sprm::v6::sprmPTtp;
2614 return nLevel ? NS_sprm::PFInnerTtp::val : NS_sprm::PFTtp::val;
2615 }
2616
EndSpecial()2617 void SwWW8ImplReader::EndSpecial()
2618 {
2619 // Frame/Table/Anl
2620 if (m_bAnl)
2621 StopAllAnl(); // -> bAnl = false
2622
2623 while(m_aApos.size() > 1)
2624 {
2625 StopTable();
2626 m_aApos.pop_back();
2627 --m_nInTable;
2628 if (m_aApos[m_nInTable])
2629 StopApo();
2630 }
2631
2632 if (m_aApos[0])
2633 StopApo();
2634
2635 OSL_ENSURE(!m_nInTable, "unclosed table!");
2636 }
2637
ProcessSpecial(bool & rbReSync,WW8_CP nStartCp)2638 bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2639 {
2640 // Frame/Table/Anl
2641 if (m_bInHyperlink)
2642 return false;
2643
2644 rbReSync = false;
2645
2646 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2647
2648 // TabRowEnd
2649 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2650
2651 // Unfortunately, for every paragraph we need to check first whether
2652 // they contain a sprm 29 (0x261B), which starts an APO.
2653 // All other sprms then refer to that APO and not to the normal text
2654 // surrounding it.
2655 // The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2656
2657 // WW: Table in APO is possible (Both Start-Ends occur at the same time)
2658 // WW: APO in Table not possible
2659
2660 // This mean that of a Table is the content of an APO, the APO start needs
2661 // to be edited first, so that the Table remains in the APO and not the
2662 // other way around.
2663 // At the End, however, we need to edit the Table End first as the APO
2664 // must end after that Table (or else we never find the APO End).
2665
2666 // The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2667
2668 // If the Table is within an APO the TabRowEnd Area misses the
2669 // APO settings.
2670 // To not end the APO there, we do not call ProcessApo
2671
2672 // KHZ: When there is a table inside the Apo the Apo-flags are also
2673 // missing for the 2nd, 3rd... paragraphs of each cell.
2674
2675 // 1st look for in-table flag, for 2000+ there is a subtable flag to
2676 // be considered, the sprm 6649 gives the level of the table
2677 sal_uInt8 nCellLevel = 0;
2678
2679 if (m_bVer67)
2680 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2681 else
2682 {
2683 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2684 if (!nCellLevel)
2685 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2686 }
2687 do
2688 {
2689 WW8_TablePos *pTabPos=nullptr;
2690 WW8_TablePos aTabPos;
2691 if(nCellLevel && !m_bVer67)
2692 {
2693 WW8PLCFxSave1 aSave;
2694 m_xPlcxMan->GetPap()->Save( aSave );
2695 rbReSync = true;
2696 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2697 WW8_CP nMyStartCp=nStartCp;
2698
2699 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2700 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2701 nCellLevel = *aLevel.pSprm;
2702
2703 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2704
2705 // Bad Table, remain unchanged in level, e.g. #i19667#
2706 if (!bHasRowEnd)
2707 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2708
2709 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2710 pTabPos = &aTabPos;
2711
2712 m_xPlcxMan->GetPap()->Restore( aSave );
2713 }
2714
2715 // Then look if we are in an Apo
2716
2717 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2718
2719 // Look to see if we are in a Table, but Table in foot/end note not allowed
2720 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2721
2722 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2723
2724 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2725 // WW8TabDesc::TableCellEnd() from making nonsense
2726
2727 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2728 bStopTab = bStartTab = true; // Required to stop and start table
2729
2730 // Test for Anl (Numbering) and process all events in the right order
2731 if( m_bAnl && !bTableRowEnd )
2732 {
2733 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2734 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2735 if (pSprm13 && aSprm13.nRemainingData >= 1)
2736 { // Still Anl left?
2737 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2738 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2739 || aApo.HasStartStop() // Forced Anl end
2740 || bStopTab || bStartTab )
2741 {
2742 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2743 }
2744 else
2745 {
2746 NextAnlLine( pSprm13 ); // Next Anl Line
2747 }
2748 }
2749 else
2750 { // Regular Anl end
2751 StopAllAnl(); // Actual end
2752 }
2753 }
2754 if (bStopTab)
2755 {
2756 StopTable();
2757 m_aApos.pop_back();
2758 --m_nInTable;
2759 }
2760 if (aApo.mbStopApo)
2761 {
2762 StopApo();
2763 m_aApos[m_nInTable] = false;
2764 }
2765
2766 if (aApo.mbStartApo)
2767 {
2768 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2769 // We need an ReSync after StartApo
2770 // (actually only if the Apo extends past a FKP border)
2771 rbReSync = true;
2772 }
2773 if (bStartTab)
2774 {
2775 WW8PLCFxSave1 aSave;
2776 m_xPlcxMan->GetPap()->Save( aSave );
2777
2778 // Numbering for cell borders causes a crash -> no Anls in Tables
2779 if (m_bAnl)
2780 StopAllAnl();
2781
2782 if(m_nInTable < nCellLevel)
2783 {
2784 if (StartTable(nStartCp))
2785 ++m_nInTable;
2786 else
2787 break;
2788 m_aApos.push_back(false);
2789 }
2790
2791 if(m_nInTable >= nCellLevel)
2792 {
2793 // We need an ReSync after StartTable
2794 // (actually only if the Apo extends past a FKP border)
2795 rbReSync = true;
2796 m_xPlcxMan->GetPap()->Restore( aSave );
2797 }
2798 }
2799 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2800 return bTableRowEnd;
2801 }
2802
GetCharSetFromLanguage()2803 rtl_TextEncoding SwWW8ImplReader::GetCharSetFromLanguage()
2804 {
2805 /*
2806 #i22206#/#i52786#
2807 The (default) character set used for a run of text is the default
2808 character set for the version of Word that last saved the document.
2809
2810 This is a bit tentative, more might be required if the concept is correct.
2811 When later version of word write older 6/95 documents the charset is
2812 correctly set in the character runs involved, so it's hard to reproduce
2813 documents that require this to be sure of the process involved.
2814 */
2815 const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_LANGUAGE);
2816 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2817 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2818 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2819 }
2820
GetCJKCharSetFromLanguage()2821 rtl_TextEncoding SwWW8ImplReader::GetCJKCharSetFromLanguage()
2822 {
2823 /*
2824 #i22206#/#i52786#
2825 The (default) character set used for a run of text is the default
2826 character set for the version of Word that last saved the document.
2827
2828 This is a bit tentative, more might be required if the concept is correct.
2829 When later version of word write older 6/95 documents the charset is
2830 correctly set in the character runs involved, so it's hard to reproduce
2831 documents that require this to be sure of the process involved.
2832 */
2833 const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
2834 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2835 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2836 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2837 }
2838
GetCurrentCharSet()2839 rtl_TextEncoding SwWW8ImplReader::GetCurrentCharSet()
2840 {
2841 /*
2842 #i2015
2843 If the hard charset is set use it, if not see if there is an open
2844 character run that has set the charset, if not then fallback to the
2845 current underlying paragraph style.
2846 */
2847 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2848 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2849 {
2850 if (!m_bVer67)
2851 eSrcCharSet = GetCharSetFromLanguage();
2852 else if (!m_aFontSrcCharSets.empty())
2853 eSrcCharSet = m_aFontSrcCharSets.top();
2854 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2855 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2856 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2857 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2858 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2859 eSrcCharSet = GetCharSetFromLanguage();
2860 }
2861 return eSrcCharSet;
2862 }
2863
2864 //Takashi Ono for CJK
GetCurrentCJKCharSet()2865 rtl_TextEncoding SwWW8ImplReader::GetCurrentCJKCharSet()
2866 {
2867 /*
2868 #i2015
2869 If the hard charset is set use it, if not see if there is an open
2870 character run that has set the charset, if not then fallback to the
2871 current underlying paragraph style.
2872 */
2873 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2874 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2875 {
2876 if (!m_aFontSrcCJKCharSets.empty())
2877 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2878 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2879 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2880 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2881 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2882 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2883 eSrcCharSet = GetCJKCharSetFromLanguage();
2884 }
2885 return eSrcCharSet;
2886 }
2887
PostProcessAttrs()2888 void SwWW8ImplReader::PostProcessAttrs()
2889 {
2890 if (m_pPostProcessAttrsInfo == nullptr)
2891 return;
2892
2893 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2894
2895 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2896 {
2897 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2898 *pItem);
2899 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2900 pItem->Which());
2901 }
2902
2903 m_pPostProcessAttrsInfo.reset();
2904 }
2905
2906 /*
2907 #i9240#
2908 It appears that some documents that are in a baltic 8 bit encoding which has
2909 some undefined characters can have use made of those characters, in which
2910 case they default to CP1252. If not then it's perhaps that the font encoding
2911 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2912 is always 1252.
2913
2914 So an encoding converter that on an undefined character attempts to
2915 convert from 1252 on the undefined character
2916 */
Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,char const * pIn,std::size_t nInLen,sal_Unicode * pOut,std::size_t nOutLen)2917 static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2918 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2919 {
2920 const sal_uInt32 nFlags =
2921 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2922 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2923 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2924 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2925
2926 const sal_uInt32 nFlags2 =
2927 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2928 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2929 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2930 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2931
2932 std::size_t nDestChars=0;
2933 std::size_t nConverted=0;
2934
2935 do
2936 {
2937 sal_uInt32 nInfo = 0;
2938 sal_Size nThisConverted=0;
2939
2940 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2941 pIn+nConverted, nInLen-nConverted,
2942 pOut+nDestChars, nOutLen-nDestChars,
2943 nFlags, &nInfo, &nThisConverted);
2944
2945 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2946
2947 nConverted += nThisConverted;
2948
2949 if (
2950 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2951 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2952 )
2953 {
2954 sal_Size nOtherConverted;
2955 rtl_TextToUnicodeConverter hCP1252Converter =
2956 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2957 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2958 pIn+nConverted, 1,
2959 pOut+nDestChars, nOutLen-nDestChars,
2960 nFlags2, &nInfo, &nOtherConverted);
2961 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2962 nConverted+=1;
2963 }
2964 } while (nConverted < nInLen);
2965
2966 return nDestChars;
2967 }
2968
LangUsesHindiNumbers(LanguageType nLang)2969 bool SwWW8ImplReader::LangUsesHindiNumbers(LanguageType nLang)
2970 {
2971 bool bResult = false;
2972
2973 switch (static_cast<sal_uInt16>(nLang))
2974 {
2975 case 0x1401: // Arabic(Algeria)
2976 case 0x3c01: // Arabic(Bahrain)
2977 case 0xc01: // Arabic(Egypt)
2978 case 0x801: // Arabic(Iraq)
2979 case 0x2c01: // Arabic (Jordan)
2980 case 0x3401: // Arabic(Kuwait)
2981 case 0x3001: // Arabic(Lebanon)
2982 case 0x1001: // Arabic(Libya)
2983 case 0x1801: // Arabic(Morocco)
2984 case 0x2001: // Arabic(Oman)
2985 case 0x4001: // Arabic(Qatar)
2986 case 0x401: // Arabic(Saudi Arabia)
2987 case 0x2801: // Arabic(Syria)
2988 case 0x1c01: // Arabic(Tunisia)
2989 case 0x3801: // Arabic(U.A.E)
2990 case 0x2401: // Arabic(Yemen)
2991 bResult = true;
2992 break;
2993 default:
2994 break;
2995 }
2996
2997 return bResult;
2998 }
2999
TranslateToHindiNumbers(sal_Unicode nChar)3000 sal_Unicode SwWW8ImplReader::TranslateToHindiNumbers(sal_Unicode nChar)
3001 {
3002 if (nChar >= 0x0030 && nChar <= 0x0039)
3003 return nChar + 0x0630;
3004
3005 return nChar;
3006 }
3007
3008 namespace
3009 {
makeOUString(rtl_uString * pStr,sal_Int32 nAllocLen)3010 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3011 {
3012 //if read len was in or around that of allocated len, just reuse pStr
3013 if (nAllocLen < pStr->length + 256)
3014 return OUString(pStr, SAL_NO_ACQUIRE);
3015 //otherwise copy the shorter used section to release extra mem
3016 OUString sRet(pStr->buffer, pStr->length);
3017 rtl_uString_release(pStr);
3018 return sRet;
3019 }
3020 }
3021
3022 /**
3023 * Return value: true for non special chars
3024 */
ReadPlainChars(WW8_CP & rPos,sal_Int32 nEnd,sal_Int32 nCpOfs)3025 bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3026 {
3027 sal_Int32 nRequestedStrLen = nEnd - rPos;
3028
3029 OSL_ENSURE(nRequestedStrLen, "String is 0");
3030 if (nRequestedStrLen <= 0)
3031 return true;
3032
3033 WW8_CP nCp;
3034 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3035 if (bFail)
3036 {
3037 rPos+=nRequestedStrLen;
3038 return true;
3039 }
3040
3041 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3042 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3043 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3044 if (!bValidPos)
3045 {
3046 // Swallow missing range, e.g. #i95550#
3047 rPos+=nRequestedStrLen;
3048 return true;
3049 }
3050
3051 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3052 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3053 if (!nAvailableStrLen)
3054 {
3055 // Swallow missing range, e.g. #i95550#
3056 rPos+=nRequestedStrLen;
3057 return true;
3058 }
3059
3060 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3061
3062 // Reset Unicode flag and correct FilePos if needed.
3063 // Note: Seek is not expensive, as we're checking inline whether or not
3064 // the correct FilePos has already been reached.
3065 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3066
3067 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3068 RTL_TEXTENCODING_MS_1252;
3069 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3070 {
3071 /*
3072 fdo#82904
3073
3074 Older documents exported as word 95 that use unicode aware fonts will
3075 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3076 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3077 pain.
3078
3079 We will try and use a fallback encoding if the conversion from
3080 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3081 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3082 valid RTL_TEXTENCODING_MS_932 by chance :-(
3083
3084 We're not the only ones that struggle with this: Here's the help from
3085 MSOffice 2003 on the topic:
3086
3087 <<
3088 Earlier versions of Microsoft Word were sometimes used in conjunction with
3089 third-party language-processing add-in programs designed to support Chinese or
3090 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3091 results in incorrect text display in more recent versions of Word.
3092
3093 However, you can set options to convert these documents so that text is
3094 displayed correctly. On the Tools menu, click Options, and then click the
3095 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3096 text (to have Word interpret the text as Asian code page data, regardless of
3097 its font) or Automatically detect Asian text (to have Word attempt to determine
3098 which parts of the text are meant to be Asian).
3099 >>
3100
3101 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3102 the language is not Japanese
3103 */
3104
3105 const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
3106 if (pItem != nullptr && LANGUAGE_JAPANESE != pItem->GetLanguage())
3107 {
3108 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3109 eSrcCharSet = GetCharSetFromLanguage();
3110 }
3111 }
3112 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3113 RTL_TEXTENCODING_MS_1252;
3114
3115 // allocate unicode string data
3116 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3117 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3118 sal_Unicode* pBuffer = xStr->buffer;
3119 sal_Unicode* pWork = pBuffer;
3120
3121 std::unique_ptr<char[]> p8Bits;
3122
3123 rtl_TextToUnicodeConverter hConverter = nullptr;
3124 if (!m_bIsUnicode || m_bVer67)
3125 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3126
3127 if (!m_bIsUnicode)
3128 p8Bits.reset( new char[nStrLen] );
3129
3130 // read the stream data
3131 sal_uInt8 nBCode = 0;
3132 sal_uInt16 nUCode;
3133
3134 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3135 const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CTL_LANGUAGE);
3136 if (pItem != nullptr)
3137 nCTLLang = pItem->GetLanguage();
3138
3139 sal_Int32 nL2;
3140 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3141 {
3142 if (m_bIsUnicode)
3143 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3144 else
3145 {
3146 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3147 nUCode = nBCode;
3148 }
3149
3150 if (!m_pStrm->good())
3151 {
3152 rPos = WW8_CP_MAX-10; // -> eof or other error
3153 return true;
3154 }
3155
3156 if ((32 > nUCode) || (0xa0 == nUCode))
3157 {
3158 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3159 break; // Special character < 32, == 0xa0 found
3160 }
3161
3162 if (m_bIsUnicode)
3163 {
3164 if (!m_bVer67)
3165 *pWork++ = nUCode;
3166 else
3167 {
3168 if (nUCode >= 0x3000) //0x8000 ?
3169 {
3170 char aTest[2];
3171 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3172 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3173 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3174 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3175 *pWork++ = aTemp[0];
3176 }
3177 else
3178 {
3179 char cTest = static_cast< char >(nUCode & 0x00FF);
3180 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3181 }
3182 }
3183 }
3184 else
3185 p8Bits[nL2] = nBCode;
3186 }
3187
3188 if (nL2)
3189 {
3190 const sal_Int32 nEndUsed = !m_bIsUnicode
3191 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3192 : pWork - pBuffer;
3193
3194 if (m_bRegardHindiDigits && m_bBidi && LangUsesHindiNumbers(nCTLLang))
3195 {
3196 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3197 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3198 }
3199
3200 xStr->buffer[nEndUsed] = 0;
3201 xStr->length = nEndUsed;
3202
3203 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3204 rPos += nL2;
3205 if (!m_aApos.back()) // a para end in apo doesn't count
3206 m_bWasParaEnd = false; // No CR
3207 }
3208
3209 if (hConverter)
3210 rtl_destroyTextToUnicodeConverter(hConverter);
3211 return nL2 >= nStrLen;
3212 }
3213
3214 #define MSASCII SAL_MAX_INT16
3215
3216 namespace
3217 {
3218 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
lcl_getScriptType(const uno::Reference<i18n::XBreakIterator> & rBI,const OUString & rString,sal_Int32 nPos)3219 sal_Int16 lcl_getScriptType(
3220 const uno::Reference<i18n::XBreakIterator>& rBI,
3221 const OUString &rString, sal_Int32 nPos)
3222 {
3223 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3224 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3225 nScript = MSASCII;
3226 return nScript;
3227 }
3228
3229 // We want to know about WEAK segments, so endOfScript isn't
3230 // useful, and see lcl_getScriptType anyway
lcl_endOfScript(const uno::Reference<i18n::XBreakIterator> & rBI,const OUString & rString,sal_Int32 nPos,sal_Int16 nScript)3231 sal_Int32 lcl_endOfScript(
3232 const uno::Reference<i18n::XBreakIterator>& rBI,
3233 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3234 {
3235 while (nPos < rString.getLength())
3236 {
3237 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3238 if (nScript != nNewScript)
3239 break;
3240 ++nPos;
3241 }
3242 return nPos;
3243 }
3244
lcl_getWriterScriptType(const uno::Reference<i18n::XBreakIterator> & rBI,const OUString & rString,sal_Int32 nPos)3245 sal_Int32 lcl_getWriterScriptType(
3246 const uno::Reference<i18n::XBreakIterator>& rBI,
3247 const OUString &rString, sal_Int32 nPos)
3248 {
3249 sal_Int16 nScript = i18n::ScriptType::WEAK;
3250
3251 if (rString.isEmpty())
3252 return nScript;
3253
3254 while (nPos >= 0)
3255 {
3256 nScript = rBI->getScriptType(rString, nPos);
3257 if (nScript != i18n::ScriptType::WEAK)
3258 break;
3259 --nPos;
3260 }
3261
3262 return nScript;
3263 }
3264
samePitchIgnoreUnknown(FontPitch eA,FontPitch eB)3265 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3266 {
3267 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3268 }
3269
sameFontIgnoringIrrelevantFields(const SvxFontItem & rA,const SvxFontItem & rB)3270 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3271 {
3272 // Ignoring CharSet, and ignoring unknown pitch
3273 return rA.GetFamilyName() == rB.GetFamilyName() &&
3274 rA.GetStyleName() == rB.GetStyleName() &&
3275 rA.GetFamily() == rB.GetFamily() &&
3276 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3277 }
3278 }
3279
3280 // In writer we categorize text into CJK, CTL and "Western" for everything else.
3281 // Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3282 // NonEastAsian/HighAnsi, with some shared characters and some properties to
3283 // hint as to which way to bias those shared characters.
3284
3285 // That's four categories, we however have three categories. Given that problem
3286 // here we would ideally find out "what would word do" to see what font/language
3287 // word would assign to characters based on the unicode range they fall into and
3288 // hack the word one onto the range we use. However it's unclear what word's
3289 // categorization is. So we don't do that here yet.
3290
3291 // Additional to the categorization, when word encounters weak text for ambiguous
3292 // chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3293 // feature in writer.
3294
3295 // So what we currently do here then is to split our text into non-weak/weak
3296 // sections and uses word's idcthint to determine what font it would use and
3297 // force that on for the segment. Following what we *do* know about word's
3298 // categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3299 // word, something we map to LATIN, so we consider all weak chars in that range
3300 // to auto-bias to LATIN.
3301
3302 // See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
emulateMSWordAddTextToParagraph(const OUString & rAddString)3303 void SwWW8ImplReader::emulateMSWordAddTextToParagraph(const OUString& rAddString)
3304 {
3305 if (rAddString.isEmpty())
3306 return;
3307
3308 if (m_bFuzzing)
3309 {
3310 simpleAddTextToParagraph(rAddString);
3311 return;
3312 }
3313
3314 uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
3315 assert(xBI.is());
3316
3317 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3318 sal_Int32 nLen = rAddString.getLength();
3319
3320 OUString sParagraphText;
3321 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3322 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3323 if (pNd)
3324 sParagraphText = pNd->GetText();
3325 sal_Int32 nParaOffset = sParagraphText.getLength();
3326 sParagraphText = sParagraphText + rAddString;
3327
3328 sal_Int32 nPos = 0;
3329 while (nPos < nLen)
3330 {
3331 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3332 if (nEnd < 0)
3333 break;
3334
3335 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3336 const TypedWhichId<SvxFontItem> aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3337 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3338 bool aForced[] = {false, false, false};
3339
3340 int nLclIdctHint = 0xFF;
3341 if (nScript == i18n::ScriptType::WEAK)
3342 {
3343 const SfxInt16Item *pIdctHint = GetFormatAttr(RES_CHRATR_IDCTHINT);
3344 nLclIdctHint = pIdctHint->GetValue();
3345 }
3346 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3347 nLclIdctHint = 0;
3348
3349 TypedWhichId<SvxFontItem> nForceFromFontId(0);
3350 if (nLclIdctHint != 0xFF)
3351 {
3352 switch (nLclIdctHint)
3353 {
3354 case 0:
3355 nForceFromFontId = RES_CHRATR_FONT;
3356 break;
3357 case 1:
3358 nForceFromFontId = RES_CHRATR_CJK_FONT;
3359 break;
3360 case 2:
3361 nForceFromFontId = RES_CHRATR_CTL_FONT;
3362 break;
3363 default:
3364 break;
3365 }
3366 }
3367
3368 if (sal_uInt16(nForceFromFontId) != 0)
3369 {
3370 // Now we know that word would use the nForceFromFontId font for this range
3371 // Try and determine what script writer would assign this range to
3372
3373 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3374 nPos + nParaOffset);
3375
3376 bool bWriterWillUseSameFontAsWordAutomatically = false;
3377
3378 if (nWriterScript != i18n::ScriptType::WEAK)
3379 {
3380 if (
3381 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3382 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3383 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3384 )
3385 {
3386 bWriterWillUseSameFontAsWordAutomatically = true;
3387 }
3388 else
3389 {
3390 const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
3391 TypedWhichId<SvxFontItem> nDestId = aIds[nWriterScript-1];
3392 const SvxFontItem *pDestFont = GetFormatAttr(nDestId);
3393 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3394 }
3395 }
3396
3397 // Writer won't use the same font as word, so force the issue
3398 if (!bWriterWillUseSameFontAsWordAutomatically)
3399 {
3400 const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
3401
3402 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3403 {
3404 const SvxFontItem *pDestFont = GetFormatAttr(aIds[i]);
3405 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3406 if (aForced[i])
3407 {
3408 pOverriddenItems[i] =
3409 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3410
3411 SvxFontItem aForceFont(*pSourceFont);
3412 aForceFont.SetWhich(aIds[i]);
3413 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3414 }
3415 }
3416 }
3417 }
3418
3419 simpleAddTextToParagraph(sChunk);
3420
3421 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3422 {
3423 if (aForced[i])
3424 {
3425 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3426 if (pOverriddenItems[i])
3427 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3428 }
3429 }
3430
3431 nPos = nEnd;
3432 if (nPos < nLen)
3433 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3434 }
3435 }
3436
3437 namespace sw {
3438
FilterControlChars(std::u16string_view aString)3439 auto FilterControlChars(std::u16string_view aString) -> OUString
3440 {
3441 OUStringBuffer buf(aString.size());
3442 for (size_t i = 0; i < aString.size(); ++i)
3443 {
3444 sal_Unicode const ch(aString[i]);
3445 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3446 {
3447 buf.append(ch);
3448 }
3449 else
3450 {
3451 SAL_INFO("sw.ww8", "filtering control character");
3452 }
3453 }
3454 return buf.makeStringAndClear();
3455 }
3456
3457 } // namespace sw
3458
simpleAddTextToParagraph(std::u16string_view aAddString)3459 void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
3460 {
3461 OUString const addString(sw::FilterControlChars(aAddString));
3462
3463 if (addString.isEmpty())
3464 return;
3465
3466 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3467 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3468
3469 OSL_ENSURE(pNd, "What the hell, where's my text node");
3470
3471 if (!pNd)
3472 return;
3473
3474 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3475 if (nCharsLeft > 0)
3476 {
3477 if (addString.getLength() <= nCharsLeft)
3478 {
3479 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3480 }
3481 else
3482 {
3483 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3484 FinalizeTextNode(*m_pPaM->GetPoint());
3485 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3486 }
3487 }
3488 else
3489 {
3490 FinalizeTextNode(*m_pPaM->GetPoint());
3491 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3492 }
3493
3494 m_bReadTable = false;
3495 }
3496
3497 /**
3498 * Return value: true for para end
3499 */
ReadChars(WW8_CP & rPos,WW8_CP nNextAttr,tools::Long nTextEnd,tools::Long nCpOfs)3500 bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3501 tools::Long nCpOfs)
3502 {
3503 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3504
3505 if (m_bSymbol || m_bIgnoreText)
3506 {
3507 WW8_CP nRequested = nEnd - rPos;
3508 if (m_bSymbol) // Insert special chars
3509 {
3510 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3511 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3512 {
3513 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3514 nRequested = nMaxPossible;
3515 }
3516
3517 if (!linguistic::IsControlChar(m_cSymbol)
3518 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3519 {
3520 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3521 {
3522 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(m_cSymbol));
3523 }
3524 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
3525 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CJK_FONT);
3526 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CTL_FONT);
3527 }
3528 }
3529 m_pStrm->SeekRel(nRequested);
3530 rPos = nEnd; // Ignore until attribute end
3531 return false;
3532 }
3533
3534 while (true)
3535 {
3536 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3537 return false; // Done
3538
3539 bool bStartLine = ReadChar(rPos, nCpOfs);
3540 rPos++;
3541 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3542 {
3543 return bStartLine;
3544 }
3545 }
3546 }
3547
HandlePageBreakChar()3548 bool SwWW8ImplReader::HandlePageBreakChar()
3549 {
3550 bool bParaEndAdded = false;
3551 // #i1909# section/page breaks should not occur in tables, word
3552 // itself ignores them in this case.
3553 if (!m_nInTable)
3554 {
3555 bool IsTemp=true;
3556 SwTextNode* pTemp = m_pPaM->GetPointNode().GetTextNode();
3557 if (pTemp && pTemp->GetText().isEmpty()
3558 && (m_bFirstPara || m_bFirstParaOfPage))
3559 {
3560 IsTemp = false;
3561 FinalizeTextNode(*m_pPaM->GetPoint());
3562 pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
3563 }
3564
3565 m_bPgSecBreak = true;
3566 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3567 /*
3568 If it's a 0x0c without a paragraph end before it, act like a
3569 paragraph end, but nevertheless, numbering (and perhaps other
3570 similar constructs) do not exist on the para.
3571 */
3572 if (!m_bWasParaEnd && IsTemp)
3573 {
3574 bParaEndAdded = true;
3575 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3576 {
3577 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3578 {
3579 pTextNode->SetAttr(
3580 *GetDfltAttr(RES_PARATR_NUMRULE));
3581 }
3582 }
3583 }
3584 }
3585 return bParaEndAdded;
3586 }
3587
ReadChar(tools::Long nPosCp,tools::Long nCpOfs)3588 bool SwWW8ImplReader::ReadChar(tools::Long nPosCp, tools::Long nCpOfs)
3589 {
3590 bool bNewParaEnd = false;
3591 // Reset Unicode flag and correct FilePos if needed.
3592 // Note: Seek is not expensive, as we're checking inline whether or not
3593 // the correct FilePos has already been reached.
3594 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3595 if (!checkSeek(*m_pStrm, nRequestedPos))
3596 return false;
3597
3598 sal_uInt16 nWCharVal(0);
3599 if( m_bIsUnicode )
3600 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3601 else
3602 {
3603 sal_uInt8 nBCode(0);
3604 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3605 nWCharVal = nBCode;
3606 }
3607
3608 sal_Unicode cInsert = '\x0';
3609 bool bParaMark = false;
3610
3611 if ( 0xc != nWCharVal )
3612 m_bFirstParaOfPage = false;
3613
3614 switch (nWCharVal)
3615 {
3616 case 0:
3617 if (!m_bFuzzing)
3618 {
3619 // Page number
3620 SwPageNumberField aField(
3621 static_cast<SwPageNumberFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(
3622 SwFieldIds::PageNumber )), PG_RANDOM, SVX_NUM_ARABIC);
3623 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
3624 }
3625 else
3626 {
3627 // extremely slow, so skip for fuzzing, and insert a space instead
3628 cInsert = ' ';
3629 }
3630 break;
3631 case 0xe:
3632 // if there is only one column word treats a column break like a pagebreak.
3633 if (m_aSectionManager.CurrentSectionColCount() < 2)
3634 bParaMark = HandlePageBreakChar();
3635 else if (!m_nInTable)
3636 {
3637 // Always insert a txtnode for a column break, e.g. ##
3638 SwContentNode *pCntNd=m_pPaM->GetPointContentNode();
3639 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3640 FinalizeTextNode(*m_pPaM->GetPoint());
3641 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
3642 }
3643 break;
3644 case 0x7:
3645 {
3646 bNewParaEnd = true;
3647 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3648 //The last paragraph of each cell is terminated by a special
3649 //paragraph mark called a cell mark. Following the cell mark
3650 //that ends the last cell of a table row, the table row is
3651 //terminated by a special paragraph mark called a row mark
3652 //
3653 //So the 0x7 should be right at the end of the previous
3654 //range to be a real cell-end.
3655 if (pPap->nOrigStartPos == nPosCp+1 ||
3656 pPap->nOrigStartPos == WW8_CP_MAX)
3657 {
3658 TabCellEnd(); // Table cell/row end
3659 }
3660 else
3661 bParaMark = true;
3662 }
3663 break;
3664 case 0xf:
3665 if( !m_bSpec ) // "Satellite"
3666 cInsert = u'\x00a4';
3667 break;
3668 case 0x14:
3669 if( !m_bSpec ) // "Para End" char
3670 cInsert = u'\x00b5';
3671 //TODO: should this be U+00B6 PILCROW SIGN rather than
3672 // U+00B5 MICRO SIGN?
3673 break;
3674 case 0x15:
3675 if( !m_bSpec ) // Juristenparagraph
3676 {
3677 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3678 if (aItr == m_aTOXEndCps.end())
3679 cInsert = u'\x00a7';
3680 else
3681 m_aTOXEndCps.erase(aItr);
3682 }
3683 break;
3684 case 0x9:
3685 cInsert = '\x9'; // Tab
3686 break;
3687 case 0xb:
3688 cInsert = '\xa'; // Hard NewLine
3689 break;
3690 case 0xc:
3691 bParaMark = HandlePageBreakChar();
3692 break;
3693 case 0x1e: // Non-breaking hyphen
3694 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDHYPHEN) );
3695 break;
3696 case 0x1f: // Non-required hyphens
3697 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_SOFTHYPHEN) );
3698 break;
3699 case 0xa0: // Non-breaking spaces
3700 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDBLANK) );
3701 break;
3702 case 0x1:
3703 /*
3704 Current thinking is that if bObj is set then we have a
3705 straightforward "traditional" ole object, otherwise we have a
3706 graphic preview of an associated ole2 object (or a simple
3707 graphic of course)
3708
3709 normally in the canvas field, the code is 0x8 0x1.
3710 in a special case, the code is 0x1 0x1, which yields a simple picture
3711 */
3712 {
3713 bool bReadObj = IsInlineEscherHack();
3714 if( bReadObj )
3715 {
3716 sal_uInt64 nCurPos = m_pStrm->Tell();
3717 sal_uInt16 nWordCode(0);
3718
3719 if( m_bIsUnicode )
3720 m_pStrm->ReadUInt16( nWordCode );
3721 else
3722 {
3723 sal_uInt8 nByteCode(0);
3724 m_pStrm->ReadUChar( nByteCode );
3725 nWordCode = nByteCode;
3726 }
3727 if( nWordCode == 0x1 )
3728 bReadObj = false;
3729 m_pStrm->Seek( nCurPos );
3730 }
3731 if( !bReadObj )
3732 {
3733 SwFrameFormat *pResult = nullptr;
3734 if (m_bObj)
3735 pResult = ImportOle();
3736 else if (m_bSpec)
3737 {
3738 SwFrameFormat* pAsCharFlyFormat =
3739 m_rDoc.MakeFrameFormat(OUString(), m_rDoc.GetDfltFrameFormat());
3740 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3741 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3742 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3743 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3744 }
3745
3746
3747 // If we have a bad 0x1 insert a space instead.
3748 if (!pResult)
3749 {
3750 cInsert = ' ';
3751 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3752 "WW8: Please report this document, it may have a "
3753 "missing graphic");
3754 }
3755 else
3756 {
3757 // reset the flags.
3758 m_bObj = m_bEmbeddObj = false;
3759 m_nObjLocFc = 0;
3760 }
3761 }
3762 }
3763 break;
3764 case 0x8:
3765 if( !m_bObj )
3766 Read_GrafLayer( nPosCp );
3767 break;
3768 case 0xd:
3769 bNewParaEnd = bParaMark = true;
3770 if (m_nInTable > 1)
3771 {
3772 /*
3773 #i9666#/#i23161#
3774 Yes complex, if there is an entry in the undocumented PLCF
3775 which I believe to be a record of cell and row boundaries
3776 see if the magic bit which I believe to mean cell end is
3777 set. I also think btw that the third byte of the 4 byte
3778 value is the level of the cell
3779 */
3780 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3781 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3782 pTest->Where() == nPosCp+1+nCpOfs)
3783 {
3784 WW8_FC nPos;
3785 void *pData;
3786 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3787 : 0;
3788 if (nData & 0x2) // Might be how it works
3789 {
3790 TabCellEnd();
3791 bParaMark = false;
3792 }
3793 }
3794 // tdf#106799: We expect TTP marks to be also cell marks,
3795 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3796 else if (m_bWasTabCellEnd || m_bWasTabRowEnd)
3797 {
3798 TabCellEnd();
3799 bParaMark = false;
3800 }
3801 }
3802
3803 m_bWasTabCellEnd = false;
3804
3805 break; // line end
3806 case 0x5: // Annotation reference
3807 case 0x13:
3808 break;
3809 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3810 if (!m_aFootnoteStack.empty())
3811 cInsert = '?';
3812 break;
3813 default:
3814 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3815 break;
3816 }
3817
3818 if( '\x0' != cInsert )
3819 {
3820 OUString sInsert(cInsert);
3821 emulateMSWordAddTextToParagraph(sInsert);
3822 }
3823 if (!m_aApos.back()) // a para end in apo doesn't count
3824 m_bWasParaEnd = bNewParaEnd;
3825 return bParaMark;
3826 }
3827
ProcessCurrentCollChange(WW8PLCFManResult & rRes,bool * pStartAttr,bool bCallProcessSpecial)3828 void SwWW8ImplReader::ProcessCurrentCollChange(WW8PLCFManResult& rRes,
3829 bool* pStartAttr, bool bCallProcessSpecial)
3830 {
3831 sal_uInt16 nOldColl = m_nCurrentColl;
3832 m_nCurrentColl = m_xPlcxMan->GetColl();
3833
3834 // Invalid Style-Id
3835 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3836 {
3837 m_nCurrentColl = 0;
3838 m_bParaAutoBefore = false;
3839 m_bParaAutoAfter = false;
3840 }
3841 else
3842 {
3843 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3844 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3845 }
3846
3847 if (nOldColl >= m_vColl.size())
3848 nOldColl = 0; // guess! TODO make sure this is what we want
3849
3850 bool bTabRowEnd = false;
3851 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3852 {
3853 bool bReSync;
3854 // Frame/Table/Autonumbering List Level
3855 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3856 if( bReSync )
3857 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3858 }
3859
3860 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3861 {
3862 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[ m_nCurrentColl ]);
3863 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3864 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3865 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3866 }
3867 }
3868
ReadTextAttr(WW8_CP & rTextPos,tools::Long nTextEnd,bool & rbStartLine,int nDepthGuard)3869 tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3870 {
3871 tools::Long nSkipChars = 0;
3872 WW8PLCFManResult aRes;
3873
3874 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3875 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3876 aRes.nCurrentCp = rTextPos; // Current Cp position
3877
3878 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3879 if ( bNewSection ) // New Section
3880 {
3881 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3882 // Create PageDesc and fill it
3883 m_aSectionManager.CreateSep(rTextPos);
3884 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3885 // Create PageDesc and fill it
3886 m_bPgSecBreak = false;
3887 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3888 }
3889
3890 // New paragraph over Plcx.Fkp.papx
3891 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3892 {
3893 ProcessCurrentCollChange( aRes, &bStartAttr,
3894 MAN_MASK_NEW_PAP == (aRes.nFlags & MAN_MASK_NEW_PAP) &&
3895 !m_bIgnoreText );
3896 rbStartLine = false;
3897 }
3898
3899 // position of last CP that's to be ignored
3900 tools::Long nSkipPos = -1;
3901
3902 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3903 {
3904 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3905 {
3906 if( bStartAttr ) // WW attributes
3907 {
3908 if( aRes.nMemLen >= 0 )
3909 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3910 }
3911 else
3912 EndSprm( aRes.nSprmId ); // Switch off Attr
3913 }
3914 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3915 {
3916 if (bStartAttr)
3917 {
3918 nSkipChars = ImportExtSprm(&aRes);
3919 if (
3920 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3921 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3922 )
3923 {
3924 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3925 // Skip Field/Footnote-/End-Note here
3926 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3927 nSkipPos = rTextPos-1;
3928 }
3929 }
3930 else
3931 EndExtSprm( aRes.nSprmId );
3932 }
3933 }
3934
3935 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3936 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3937 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3938
3939 // Find next Attr position (and Skip attributes of field contents if needed)
3940 if (nSkipChars && !m_bIgnoreText)
3941 m_xCtrlStck->MarkAllAttrsOld();
3942 bool bOldIgnoreText = m_bIgnoreText;
3943 m_bIgnoreText = true;
3944 sal_uInt16 nOldColl = m_nCurrentColl;
3945 bool bDoPlcxManPlusPLus = true;
3946 tools::Long nNext;
3947 do
3948 {
3949 if( bDoPlcxManPlusPLus )
3950 m_xPlcxMan->advance();
3951 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3952
3953 if (m_pPostProcessAttrsInfo &&
3954 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3955 {
3956 m_pPostProcessAttrsInfo->mbCopy = true;
3957 }
3958
3959 if( (0 <= nNext) && (nSkipPos >= nNext) )
3960 {
3961 if (nDepthGuard >= 1024)
3962 {
3963 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3964 nNext = nTextEnd;
3965 }
3966 else
3967 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3968 bDoPlcxManPlusPLus = false;
3969 m_bIgnoreText = true;
3970 }
3971
3972 if (m_pPostProcessAttrsInfo &&
3973 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3974 {
3975 m_pPostProcessAttrsInfo->mbCopy = false;
3976 }
3977 }
3978 while( nSkipPos >= nNext );
3979 m_bIgnoreText = bOldIgnoreText;
3980 if( nSkipChars )
3981 {
3982 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
3983 if( nOldColl != m_xPlcxMan->GetColl() )
3984 ProcessCurrentCollChange(aRes, nullptr, false);
3985 }
3986
3987 return nNext;
3988 }
3989
ReadAttrs(WW8_CP & rTextPos,WW8_CP & rNext,tools::Long nTextEnd,bool & rbStartLine)3990 void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
3991 {
3992 // Do we have attributes?
3993 if( rTextPos >= rNext )
3994 {
3995 do
3996 {
3997 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
3998 if (rTextPos == rNext && rTextPos >= nTextEnd)
3999 break;
4000 }
4001 while( rTextPos >= rNext );
4002
4003 }
4004 else if ( rbStartLine )
4005 {
4006 /* No attributes, but still a new line.
4007 * If a line ends with a line break and paragraph attributes or paragraph templates
4008 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
4009 * is false.
4010 * Due to this we need to set the template here as a kind of special treatment.
4011 */
4012 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4013 SetTextFormatCollAndListLevel(*m_pPaM, m_vColl[m_nCurrentColl]);
4014 rbStartLine = false;
4015 }
4016 }
4017
4018 /**
4019 * CloseAttrEnds to only read the attribute ends at the end of a text or a
4020 * text area (Header, Footnote, ...).
4021 * We ignore attribute starts and fields.
4022 */
CloseAttrEnds()4023 void SwWW8ImplReader::CloseAttrEnds()
4024 {
4025 // If there are any unclosed sprms then copy them to
4026 // another stack and close the ones that must be closed
4027 std::stack<sal_uInt16> aStack;
4028 m_xPlcxMan->TransferOpenSprms(aStack);
4029
4030 while (!aStack.empty())
4031 {
4032 sal_uInt16 nSprmId = aStack.top();
4033 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4034 EndSprm(nSprmId);
4035 aStack.pop();
4036 }
4037
4038 EndSpecial();
4039 }
4040
ReadText(WW8_CP nStartCp,WW8_CP nTextLen,ManTypes nType)4041 bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4042 {
4043 bool bJoined=false;
4044
4045 bool bStartLine = true;
4046 short nCrCount = 0;
4047 short nDistance = 0;
4048
4049 m_bWasParaEnd = false;
4050 m_nCurrentColl = 0;
4051 m_xCurrentItemSet.reset();
4052 m_nCharFormat = -1;
4053 m_bSpec = false;
4054 m_bPgSecBreak = false;
4055
4056 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4057 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4058
4059 WW8_CP nNext = m_xPlcxMan->Where();
4060 m_xPreviousNode.reset();
4061 sal_uInt8 nDropLines = 0;
4062 SwCharFormat* pNewSwCharFormat = nullptr;
4063 const SwCharFormat* pFormat = nullptr;
4064
4065 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4066 if (!bValidPos)
4067 return false;
4068
4069 WW8_CP l = nStartCp;
4070 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4071 if (nTextLen > nMaxPossible)
4072 {
4073 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4074 nTextLen = nMaxPossible;
4075 }
4076 WW8_CP nTextEnd = nStartCp+nTextLen;
4077 while (l < nTextEnd)
4078 {
4079 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4080 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4081
4082 if (m_pPostProcessAttrsInfo != nullptr)
4083 PostProcessAttrs();
4084
4085 if (l >= nTextEnd)
4086 break;
4087
4088 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4089
4090 // If the previous paragraph was a dropcap then do not
4091 // create a new txtnode and join the two paragraphs together
4092 if (bStartLine && !m_xPreviousNode) // Line end
4093 {
4094 bool bSplit = true;
4095 if (m_bCareFirstParaEndInToc)
4096 {
4097 m_bCareFirstParaEndInToc = false;
4098 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4099 bSplit = false;
4100 }
4101 if (m_bCareLastParaEndInToc)
4102 {
4103 m_bCareLastParaEndInToc = false;
4104 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4105 bSplit = false;
4106 }
4107 if (bSplit)
4108 {
4109 FinalizeTextNode(*m_pPaM->GetPoint());
4110 }
4111 }
4112
4113 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4114 {
4115 SwTextNode* pEndNd = m_pPaM->GetPointNode().GetTextNode();
4116 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4117 if (pEndNd)
4118 {
4119 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4120
4121 // Need to reset the font size and text position for the dropcap
4122 {
4123 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4124 m_xCtrlStck->Delete(aTmp);
4125 }
4126
4127 // Get the default document dropcap which we can use as our template
4128 const SwFormatDrop* defaultDrop = GetFormatAttr(RES_PARATR_DROP);
4129 SwFormatDrop aDrop(*defaultDrop);
4130
4131 aDrop.GetLines() = nDropLines;
4132 aDrop.GetDistance() = nDistance;
4133 aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
4134 // Word has no concept of a "whole word dropcap"
4135 aDrop.GetWholeWord() = false;
4136
4137 if (pFormat)
4138 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4139 else if(pNewSwCharFormat)
4140 aDrop.SetCharFormat(pNewSwCharFormat);
4141
4142 SwPosition aStart(*pEndNd);
4143 m_xCtrlStck->NewAttr(aStart, aDrop);
4144 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_DROP);
4145 }
4146 m_xPreviousNode.reset();
4147 }
4148 else if (m_bDropCap)
4149 {
4150 // If we have found a dropcap store the textnode
4151 m_xPreviousNode.reset(new TextNodeListener(m_pPaM->GetPointNode().GetTextNode()));
4152
4153 SprmResult aDCS;
4154 if (m_bVer67)
4155 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4156 else
4157 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4158
4159 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4160 nDropLines = (*aDCS.pSprm) >> 3;
4161 else // There is no Drop Cap Specifier hence no dropcap
4162 m_xPreviousNode.reset();
4163
4164 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4165 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4166 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4167 else
4168 nDistance = 0;
4169
4170 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4171
4172 if (m_xCurrentItemSet)
4173 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4174
4175 if (pSwFormatCharFormat)
4176 pFormat = pSwFormatCharFormat->GetCharFormat();
4177
4178 if (m_xCurrentItemSet && !pFormat)
4179 {
4180 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4181 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4182 m_xCurrentItemSet->ClearItem(RES_CHRATR_ESCAPEMENT);
4183 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4184 }
4185
4186 m_xCurrentItemSet.reset();
4187 m_bDropCap=false;
4188 }
4189
4190 if (bStartLine || m_bWasTabRowEnd)
4191 {
4192 // Call all 64 CRs; not for Header and the like
4193 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4194 {
4195 if (nTextLen < WW8_CP_MAX/100)
4196 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4197 else
4198 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4199 m_xProgress->Update(m_nProgress); // Update
4200 }
4201 }
4202
4203 // If we have encountered a 0x0c which indicates either section of
4204 // pagebreak then look it up to see if it is a section break, and
4205 // if it is not then insert a page break. If it is a section break
4206 // it will be handled as such in the ReadAttrs of the next loop
4207 if (m_bPgSecBreak)
4208 {
4209 // We need only to see if a section is ending at this cp,
4210 // the plcf will already be sitting on the correct location
4211 // if it is there.
4212 WW8PLCFxDesc aTemp;
4213 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4214 if (m_xPlcxMan->GetSepPLCF())
4215 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4216 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4217 {
4218 // #i39251# - insert text node for page break, if no one inserted.
4219 // #i43118# - refine condition: the anchor
4220 // control stack has to have entries, otherwise it's not needed
4221 // to insert a text node.
4222 if (!bStartLine && !m_xAnchorStck->empty())
4223 {
4224 FinalizeTextNode(*m_pPaM->GetPoint());
4225 }
4226 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM,
4227 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4228 m_bFirstParaOfPage = true;
4229 m_bPgSecBreak = false;
4230 }
4231 }
4232 }
4233
4234 m_xPreviousNode.reset();
4235
4236 if (m_pPaM->GetPoint()->GetContentIndex())
4237 FinalizeTextNode(*m_pPaM->GetPoint());
4238
4239 if (!m_bInHyperlink)
4240 bJoined = JoinNode(*m_pPaM);
4241
4242 CloseAttrEnds();
4243
4244 m_xPlcxMan.reset();
4245 return bJoined;
4246 }
4247
SwWW8ImplReader(sal_uInt8 nVersionPara,SotStorage * pStorage,SvStream * pSt,SwDoc & rD,OUString aBaseURL,bool bNewDoc,bool bSkipImages,SwPosition const & rPos)4248 SwWW8ImplReader::SwWW8ImplReader(sal_uInt8 nVersionPara, SotStorage* pStorage,
4249 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4250 : m_pDocShell(rD.GetDocShell())
4251 , m_pStg(pStorage)
4252 , m_pStrm(pSt)
4253 , m_pTableStream(nullptr)
4254 , m_pDataStream(nullptr)
4255 , m_rDoc(rD)
4256 , m_pPaM(nullptr)
4257 , m_aSectionManager(*this)
4258 , m_aExtraneousParas(rD)
4259 , m_aInsertedTables(rD)
4260 , m_aSectionNameGenerator(rD, u"WW"_ustr)
4261 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4262 , m_aParaStyleMapper(rD)
4263 , m_aCharStyleMapper(rD)
4264 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4265 , m_pPreviousNumPaM(nullptr)
4266 , m_pPrevNumRule(nullptr)
4267 , m_pCurrentColl(nullptr)
4268 , m_pDfltTextFormatColl(nullptr)
4269 , m_pStandardFormatColl(nullptr)
4270 , m_pDrawModel(nullptr)
4271 , m_pDrawPg(nullptr)
4272 , m_pNumFieldType(nullptr)
4273 , m_sBaseURL(std::move(aBaseURL))
4274 , m_nIniFlags(0)
4275 , m_nIniFlags1(0)
4276 , m_nFieldFlags(0)
4277 , m_bRegardHindiDigits( false )
4278 , m_bDrawCpOValid( false )
4279 , m_nDrawCpO(0)
4280 , m_nPicLocFc(0)
4281 , m_nObjLocFc(0)
4282 , m_nIniFlyDx(0)
4283 , m_nIniFlyDy(0)
4284 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4285 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4286 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4287 , m_nProgress(0)
4288 , m_nCurrentColl(0)
4289 , m_nFieldNum(0)
4290 , m_nLFOPosition(USHRT_MAX)
4291 , m_nCharFormat(0)
4292 , m_nDrawXOfs(0)
4293 , m_nDrawYOfs(0)
4294 , m_nDrawXOfs2(0)
4295 , m_nDrawYOfs2(0)
4296 , m_cSymbol(0)
4297 , m_nWantedVersion(nVersionPara)
4298 , m_nSwNumLevel(0xff)
4299 , m_nWwNumType(0xff)
4300 , m_pChosenWW8OutlineStyle(nullptr)
4301 , m_nListLevel(MAXLEVEL)
4302 , m_bNewDoc(bNewDoc)
4303 , m_bSkipImages(bSkipImages)
4304 , m_bReadNoTable(false)
4305 , m_bPgSecBreak(false)
4306 , m_bSpec(false)
4307 , m_bObj(false)
4308 , m_bTxbxFlySection(false)
4309 , m_bHasBorder(false)
4310 , m_bSymbol(false)
4311 , m_bIgnoreText(false)
4312 , m_nInTable(0)
4313 , m_bWasTabRowEnd(false)
4314 , m_bWasTabCellEnd(false)
4315 , m_bAnl(false)
4316 , m_bHdFtFootnoteEdn(false)
4317 , m_bFootnoteEdn(false)
4318 , m_bIsHeader(false)
4319 , m_bIsFooter(false)
4320 , m_bIsUnicode(false)
4321 , m_bCpxStyle(false)
4322 , m_bStyNormal(false)
4323 , m_bWWBugNormal(false)
4324 , m_bNoAttrImport(false)
4325 , m_bInHyperlink(false)
4326 , m_bWasParaEnd(false)
4327 , m_bVer67(false)
4328 , m_bVer6(false)
4329 , m_bVer7(false)
4330 , m_bVer8(false)
4331 , m_bEmbeddObj(false)
4332 , m_bCurrentAND_fNumberAcross(false)
4333 , m_bNoLnNumYet(true)
4334 , m_bFirstPara(true)
4335 , m_bFirstParaOfPage(false)
4336 , m_bParaAutoBefore(false)
4337 , m_bParaAutoAfter(false)
4338 , m_bDropCap(false)
4339 , m_nDropCap(0)
4340 , m_bBidi(false)
4341 , m_bReadTable(false)
4342 , m_bLoadingTOXCache(false)
4343 , m_nEmbeddedTOXLevel(0)
4344 , m_bLoadingTOXHyperlink(false)
4345 , m_bCareFirstParaEndInToc(false)
4346 , m_bCareLastParaEndInToc(false)
4347 , m_bNotifyMacroEventRead(false)
4348 , m_bFuzzing(comphelper::IsFuzzing())
4349 {
4350 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4351 m_aApos.push_back(false);
4352
4353 mpCursor = m_rDoc.CreateUnoCursor(rPos);
4354 }
4355
~SwWW8ImplReader()4356 SwWW8ImplReader::~SwWW8ImplReader()
4357 {
4358 }
4359
DeleteStack(std::unique_ptr<SwFltControlStack> pStck)4360 void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4361 {
4362 if( pStck )
4363 {
4364 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4365 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4366 }
4367 else
4368 {
4369 OSL_ENSURE( false, "WW stack already deleted" );
4370 }
4371 }
4372
SetSegmentToPageDesc(const wwSection & rSection,bool bIgnoreCols)4373 void wwSectionManager::SetSegmentToPageDesc(const wwSection &rSection,
4374 bool bIgnoreCols)
4375 {
4376 SwPageDesc &rPage = *rSection.mpPage;
4377
4378 SetNumberingType(rSection, rPage);
4379
4380 SwFrameFormat &rFormat = rPage.GetMaster();
4381
4382 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4383 mrReader.GraphicCtor();
4384
4385 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4386 {
4387 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4388 SvxMSDffImportData aData(aRect);
4389 rtl::Reference<SdrObject> pObject;
4390 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4391 {
4392 // Only handle shape if it is a background shape
4393 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4394 {
4395 SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND,XATTR_START, XATTR_END>
4396 aSet(rFormat.GetDoc()->GetAttrPool());
4397 mrReader.MatchSdrItemsIntoFlySet(pObject.get(), aSet, mso_lineSimple,
4398 mso_lineSolid, mso_sptRectangle, aRect);
4399 if ( aSet.HasItem(RES_BACKGROUND) )
4400 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4401 else
4402 rFormat.SetFormatAttr(aSet);
4403 }
4404 }
4405 }
4406 wwULSpaceData aULData;
4407 GetPageULData(rSection, aULData);
4408 SetPageULSpaceItems(rFormat, aULData, rSection);
4409
4410 rPage.SetVerticalAdjustment( rSection.mnVerticalAdjustment );
4411
4412 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4413
4414 if (!(rSection.maSep.pgbApplyTo & 1))
4415 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4416 if (!(rSection.maSep.pgbApplyTo & 2))
4417 SwWW8ImplReader::SetPageBorder(rPage.GetFirstMaster(), rSection);
4418
4419 mrReader.SetDocumentGrid(rFormat, rSection);
4420 }
4421
SetUseOn(wwSection & rSection)4422 void wwSectionManager::SetUseOn(wwSection &rSection)
4423 {
4424 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4425 mrReader.m_xWDop->doptypography.m_f2on1;
4426
4427 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4428 UseOnPage eUse = eUseBase;
4429 if (!mrReader.m_xWDop->fFacingPages)
4430 eUse |= UseOnPage::HeaderShare | UseOnPage::FooterShare;
4431 if (!rSection.HasTitlePage())
4432 eUse |= UseOnPage::FirstShare;
4433
4434 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4435 if (rSection.mpPage)
4436 rSection.mpPage->WriteUseOn(eUse);
4437 }
4438
4439 /**
4440 * Set the page descriptor on this node, handle the different cases for a text
4441 * node or a table
4442 */
GiveNodePageDesc(SwNodeIndex const & rIdx,const SwFormatPageDesc & rPgDesc,SwDoc & rDoc)4443 static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4444 SwDoc &rDoc)
4445 {
4446 /*
4447 If it's a table here, apply the pagebreak to the table
4448 properties, otherwise we add it to the para at this
4449 position
4450 */
4451 if (rIdx.GetNode().IsTableNode())
4452 {
4453 SwTable& rTable =
4454 rIdx.GetNode().GetTableNode()->GetTable();
4455 SwFrameFormat* pApply = rTable.GetFrameFormat();
4456 OSL_ENSURE(pApply, "impossible");
4457 if (pApply)
4458 pApply->SetFormatAttr(rPgDesc);
4459 }
4460 else
4461 {
4462 SwPaM aPage(rIdx);
4463 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4464 }
4465 }
4466
4467 /**
4468 * Map a word section to a writer page descriptor
4469 */
SetSwFormatPageDesc(mySegIter const & rIter,mySegIter const & rStart,bool bIgnoreCols)4470 SwFormatPageDesc wwSectionManager::SetSwFormatPageDesc(mySegIter const &rIter,
4471 mySegIter const &rStart, bool bIgnoreCols)
4472 {
4473 if (mrReader.m_bNewDoc && rIter == rStart)
4474 {
4475 rIter->mpPage =
4476 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD);
4477 }
4478 else
4479 {
4480 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4481 SwViewShell::GetShellRes()->GetPageDescName(mnDesc, ShellResource::NORMAL_PAGE),
4482 nullptr, false);
4483 }
4484 OSL_ENSURE(rIter->mpPage, "no page!");
4485 if (!rIter->mpPage)
4486 return SwFormatPageDesc();
4487
4488 // Set page before hd/ft
4489 const wwSection *pPrevious = nullptr;
4490 if (rIter != rStart)
4491 pPrevious = &(*(rIter-1));
4492 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4493 SetUseOn(*rIter);
4494
4495 // Set hd/ft after set page
4496 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4497
4498 SwFormatPageDesc aRet(rIter->mpPage);
4499
4500 rIter->mpPage->SetFollow(rIter->mpPage);
4501
4502 if (rIter->PageRestartNo())
4503 aRet.SetNumOffset(rIter->PageStartAt());
4504
4505 ++mnDesc;
4506 return aRet;
4507 }
4508
InsertSegments()4509 void wwSectionManager::InsertSegments()
4510 {
4511 mySegIter aEnd = maSegments.end();
4512 mySegIter aStart = maSegments.begin();
4513 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4514 {
4515 // If the section is of type "New column" (0x01), then simply insert a column break.
4516 // But only if there actually are columns on the page, otherwise a column break
4517 // seems to be handled like a page break by MSO.
4518 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4519 {
4520 SwPaM start( aIter->maStart );
4521 mrReader.m_rDoc.getIDocumentContentOperations().InsertPoolItem( start, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
4522 continue;
4523 }
4524
4525 mySegIter aNext = aIter+1;
4526 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4527
4528 // If two following sections are different in following properties, Word will interpret a continuous
4529 // section break between them as if it was a section break next page.
4530 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4531 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4532
4533 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4534 bool bInsertPageDesc = !bInsertSection;
4535 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4536
4537 if (bInsertPageDesc)
4538 {
4539 /*
4540 If a cont section follows this section then we won't be
4541 creating a page desc with 2+ cols as we cannot host a one
4542 col section in a 2+ col pagedesc and make it look like
4543 word. But if the current section actually has columns then
4544 we are forced to insert a section here as well as a page
4545 descriptor.
4546 */
4547
4548 bool bIgnoreCols = bInsertSection;
4549 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4550 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4551 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4552 (aIter->IsLandScape() == aNext->IsLandScape()));
4553
4554 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4555 {
4556 bIgnoreCols = true;
4557 if ((aIter->NoCols() > 1) || bProtected)
4558 bInsertSection = true;
4559 }
4560
4561 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4562 if (!aDesc.GetPageDesc())
4563 continue;
4564
4565 // special case handling for odd/even section break
4566 // a) as before create a new page style for the section break
4567 // b) set Layout of generated page style to right/left ( according
4568 // to section break odd/even )
4569 // c) create a new style to follow the break page style
4570 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4571 {
4572 // SetSwFormatPageDesc calls some methods that could
4573 // modify aIter (e.g. wwSection ).
4574 // Since we call SetSwFormatPageDesc below to generate the
4575 // 'Following' style of the Break style, it is safer
4576 // to take a copy of the contents of aIter.
4577 wwSection aTmpSection = *aIter;
4578 // create a new following page style
4579 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4580 // restore any contents of aIter trashed by SetSwFormatPageDesc
4581 *aIter = std::move(aTmpSection);
4582
4583 // Handle the section break
4584 UseOnPage eUseOnPage = UseOnPage::Left;
4585 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4586 eUseOnPage = UseOnPage::Right;
4587
4588 // Keep the share flags.
4589 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4590 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4591 }
4592
4593 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4594 // unless it is needed to specify a page number.
4595 if (aIter != aStart || aDesc.GetNumOffset())
4596 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4597 }
4598
4599 SwTextNode* pTextNd = nullptr;
4600 if (bInsertSection)
4601 {
4602 // Start getting the bounds of this section
4603 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4604 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4605 if (aNext != aEnd)
4606 {
4607 aAnchor = aNext->maStart;
4608 aSectPaM.GetPoint()->Assign(aAnchor);
4609 aSectPaM.Move(fnMoveBackward);
4610 }
4611
4612 const SwPosition* pPos = aSectPaM.GetPoint();
4613 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4614 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4615 if (pTableNd)
4616 {
4617 pTextNd =
4618 mrReader.m_rDoc.GetNodes().MakeTextNode(aAnchor.GetNode(),
4619 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
4620
4621 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4622 }
4623
4624 aSectPaM.SetMark();
4625
4626 aSectPaM.GetPoint()->Assign(aIter->maStart);
4627
4628 bool bHasOwnHdFt = false;
4629 /*
4630 In this nightmare scenario the continuous section has its own
4631 headers and footers so we will try and find a hard page break
4632 between here and the end of the section and put the headers and
4633 footers there.
4634 */
4635 if (!bInsertPageDesc)
4636 {
4637 bHasOwnHdFt =
4638 mrReader.HasOwnHeaderFooter(
4639 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4640 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4641 );
4642 }
4643 if (bHasOwnHdFt)
4644 {
4645 // #i40766# Need to cache the page descriptor in case there is
4646 // no page break in the section
4647 SwPageDesc *pOrig = aIter->mpPage;
4648 bool bFailed = true;
4649 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4650 if (aDesc.GetPageDesc())
4651 {
4652 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4653 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4654 for(; nStart <= nEnd; ++nStart)
4655 {
4656 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4657 if (!pNode)
4658 continue;
4659 if (sw::util::HasPageBreak(*pNode))
4660 {
4661 SwNodeIndex aIdx(*pNode);
4662 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4663 bFailed = false;
4664 break;
4665 }
4666 }
4667 }
4668 if(bFailed)
4669 {
4670 aIter->mpPage = pOrig;
4671 }
4672 }
4673
4674 // End getting the bounds of this section, quite a job eh?
4675 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4676 // The last section if continuous is always unbalanced
4677 if (pRet)
4678 {
4679 // Set the columns to be UnBalanced if that compatibility option is set
4680 if (mrReader.m_xWDop->fNoColumnBalance)
4681 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4682 else
4683 {
4684 // Otherwise set to unbalanced if the following section is
4685 // not continuous, (which also means that the last section
4686 // is unbalanced)
4687 if (aNext == aEnd || !aNext->IsContinuous())
4688 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4689 }
4690 }
4691 }
4692
4693 if (pTextNd)
4694 {
4695 SwPaM aTest(*pTextNd);
4696 mrReader.m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4697 pTextNd = nullptr;
4698 }
4699 }
4700 }
4701
delete_all_from_doc()4702 void wwExtraneousParas::delete_all_from_doc()
4703 {
4704 auto aEnd = m_aTextNodes.rend();
4705 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4706 {
4707 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4708 SwTextNode* pTextNode = rListener.GetTextNode();
4709 rListener.StopListening(pTextNode);
4710
4711 SwPaM aTest(*pTextNode);
4712 m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4713 }
4714 m_aTextNodes.clear();
4715 }
4716
insert(SwTextNode * pTextNode)4717 void wwExtraneousParas::insert(SwTextNode *pTextNode)
4718 {
4719 m_aTextNodes.emplace(pTextNode, this);
4720 }
4721
remove_if_present(SwModify * pModify)4722 void wwExtraneousParas::remove_if_present(SwModify* pModify)
4723 {
4724 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4725 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4726 if (it == m_aTextNodes.end())
4727 return;
4728 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4729 m_aTextNodes.erase(it);
4730 }
4731
TextNodeListener(SwTextNode * pTextNode)4732 TextNodeListener::TextNodeListener(SwTextNode* pTextNode)
4733 : m_pTextNode(pTextNode)
4734 {
4735 m_pTextNode->Add(*this);
4736 }
4737
~TextNodeListener()4738 TextNodeListener::~TextNodeListener()
4739 {
4740 if (!m_pTextNode)
4741 return;
4742 StopListening(m_pTextNode);
4743 }
4744
SwClientNotify(const SwModify & rModify,const SfxHint & rHint)4745 void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4746 {
4747 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4748 return;
4749 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4750 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4751 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4752 // indicates an underlying bug.
4753 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4754 removed(const_cast<SwModify*>(&rModify));
4755 }
4756
StopListening(SwModify * pTextNode)4757 void TextNodeListener::StopListening(SwModify* pTextNode)
4758 {
4759 pTextNode->Remove(*this);
4760 m_pTextNode = nullptr;
4761 }
4762
removed(SwModify * pTextNode)4763 void TextNodeListener::removed(SwModify* pTextNode)
4764 {
4765 StopListening(pTextNode);
4766 }
4767
removed(SwModify * pTextNode)4768 void wwExtraneousParas::ExtraTextNodeListener::removed(SwModify* pTextNode)
4769 {
4770 m_pOwner->remove_if_present(pTextNode);
4771 }
4772
StoreMacroCmds()4773 void SwWW8ImplReader::StoreMacroCmds()
4774 {
4775 if (!m_xWwFib->m_lcbCmds)
4776 return;
4777
4778 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4779 if (!bValidPos)
4780 return;
4781
4782 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4783
4784 if (!xRoot.is())
4785 return;
4786
4787 try
4788 {
4789 uno::Reference < io::XStream > xStream =
4790 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4791 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4792
4793 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4794 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4795 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4796 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4797 }
4798 catch (...)
4799 {
4800 }
4801 }
4802
ReadDocVars()4803 void SwWW8ImplReader::ReadDocVars()
4804 {
4805 std::vector<OUString> aDocVarStrings;
4806 std::vector<ww::bytes> aDocVarStringIds;
4807 std::vector<OUString> aDocValueStrings;
4808 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4809 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4810 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4811 if (m_bVer67) return;
4812
4813 uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4814 uno::Reference<css::lang::XMultiServiceFactory> xTextFactory(m_pDocShell->GetModel(), uno::UNO_QUERY);
4815 uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
4816 for(size_t i = 0; i < aDocVarStrings.size(); i++)
4817 {
4818 const OUString &rName = aDocVarStrings[i];
4819 uno::Any aValue;
4820 if (aDocValueStrings.size() > i)
4821 {
4822 OUString value = aDocValueStrings[i];
4823 value = value.replaceAll("\r\n", "\n");
4824 value = value.replaceAll("\r", "\n");
4825 // fdo48097-1.doc is an example of a case needing sanitizeStringSurrogates
4826 aValue <<= comphelper::string::sanitizeStringSurrogates(value);
4827 }
4828
4829 uno::Reference< beans::XPropertySet > xMaster;
4830 OUString sFieldMasterService("com.sun.star.text.FieldMaster.User." + rName);
4831
4832 // Find or create Field Master
4833 if (xFieldMasterAccess->hasByName(sFieldMasterService))
4834 {
4835 xMaster.set(xFieldMasterAccess->getByName(sFieldMasterService), uno::UNO_QUERY_THROW);
4836 }
4837 else
4838 {
4839 xMaster.set(xTextFactory->createInstance(u"com.sun.star.text.FieldMaster.User"_ustr), uno::UNO_QUERY_THROW);
4840 xMaster->setPropertyValue(u"Name"_ustr, uno::Any(rName));
4841 }
4842 xMaster->setPropertyValue(u"Content"_ustr, aValue);
4843 }
4844 }
4845
4846 /**
4847 * Document Info
4848 */
ReadDocInfo()4849 void SwWW8ImplReader::ReadDocInfo()
4850 {
4851 if( !m_pStg )
4852 return;
4853
4854 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4855 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4856 uno::Reference<document::XDocumentProperties> xDocProps(
4857 xDPS->getDocumentProperties());
4858 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4859
4860 if (!xDocProps.is())
4861 return;
4862
4863 if ( m_xWwFib->m_fDot )
4864 {
4865 SfxMedium* pMedium = m_pDocShell->GetMedium();
4866 if ( pMedium )
4867 {
4868 const OUString& aName = pMedium->GetName();
4869 INetURLObject aURL( aName );
4870 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4871 if ( !sTemplateURL.isEmpty() )
4872 xDocProps->setTemplateURL( sTemplateURL );
4873 }
4874 }
4875 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4876 {
4877 auto nCur = m_pTableStream->Tell();
4878 Sttb aSttb;
4879 // point at tgc record
4880 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4881 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4882 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4883 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4884 OUString aURL;
4885 // attempt to convert to url (won't work for obvious reasons on linux)
4886 if ( !sPath.isEmpty() )
4887 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4888 if (aURL.isEmpty())
4889 xDocProps->setTemplateURL( aURL );
4890 else
4891 xDocProps->setTemplateURL( sPath );
4892
4893 }
4894 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4895 }
4896
lcl_createTemplateToProjectEntry(const uno::Reference<container::XNameContainer> & xPrjNameCache,const OUString & sTemplatePathOrURL,const OUString & sVBAProjName)4897 static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4898 {
4899 if ( !xPrjNameCache.is() )
4900 return;
4901
4902 INetURLObject aObj;
4903 aObj.SetURL( sTemplatePathOrURL );
4904 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4905 OUString aURL;
4906 if ( bIsURL )
4907 aURL = sTemplatePathOrURL;
4908 else
4909 {
4910 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4911 aObj.SetURL( aURL );
4912 }
4913 try
4914 {
4915 OUString templateNameWithExt = aObj.GetLastName();
4916 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4917 if ( nIndex != -1 )
4918 {
4919 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4920 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4921 }
4922 }
4923 catch( const uno::Exception& )
4924 {
4925 }
4926 }
4927
4928 namespace {
4929
4930 class WW8Customizations
4931 {
4932 SvStream* mpTableStream;
4933 WW8Fib mWw8Fib;
4934 public:
4935 WW8Customizations( SvStream*, WW8Fib const & );
4936 void Import( SwDocShell* pShell );
4937 };
4938
4939 }
4940
WW8Customizations(SvStream * pTableStream,WW8Fib const & rFib)4941 WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4942 {
4943 }
4944
Import(SwDocShell * pShell)4945 void WW8Customizations::Import( SwDocShell* pShell )
4946 {
4947 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4948 return;
4949 try
4950 {
4951 Tcg aTCG;
4952 sal_uInt64 nCur = mpTableStream->Tell();
4953 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4954 {
4955 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4956 return;
4957 }
4958 bool bReadResult = aTCG.Read( *mpTableStream );
4959 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4960 if ( !bReadResult )
4961 {
4962 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4963 return;
4964 }
4965 aTCG.ImportCustomToolBar( *pShell );
4966 }
4967 catch(...)
4968 {
4969 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4970 }
4971 }
4972
ReadGlobalTemplateSettings(std::u16string_view sCreatedFrom,const uno::Reference<container::XNameContainer> & xPrjNameCache)4973 void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4974 {
4975 if (m_bFuzzing)
4976 return;
4977
4978 SvtPathOptions aPathOpt;
4979 const OUString& aAddinPath = aPathOpt.GetAddinPath();
4980 uno::Sequence< OUString > sGlobalTemplates;
4981
4982 // first get the autoload addins in the directory STARTUP
4983 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
4984
4985 if( xSFA->isFolder( aAddinPath ) )
4986 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
4987
4988 for (const auto& rGlobalTemplate : sGlobalTemplates)
4989 {
4990 INetURLObject aObj;
4991 aObj.SetURL( rGlobalTemplate );
4992 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4993 OUString aURL;
4994 if ( bIsURL )
4995 aURL = rGlobalTemplate;
4996 else
4997 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
4998 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
4999 continue; // don't try and read the same document as ourselves
5000
5001 rtl::Reference<SotStorage> rRoot = new SotStorage(aURL, StreamMode::STD_READWRITE);
5002
5003 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5004 // Import vba via oox filter
5005 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5006 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
5007 // Read toolbars & menus
5008 rtl::Reference<SotStorageStream> refMainStream = rRoot->OpenSotStream(u"WordDocument"_ustr);
5009 refMainStream->SetEndian(SvStreamEndian::LITTLE);
5010 WW8Fib aWwFib( *refMainStream, 8 );
5011 rtl::Reference<SotStorageStream> xTableStream =
5012 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table, StreamMode::STD_READ);
5013
5014 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5015 {
5016 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5017 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5018 aGblCustomisations.Import( m_pDocShell );
5019 }
5020 }
5021 }
5022
CoreLoad(WW8Glossary const * pGloss)5023 ErrCode SwWW8ImplReader::CoreLoad(WW8Glossary const *pGloss)
5024 {
5025 m_rDoc.SetDocumentType( SwDoc::DOCTYPE_MSWORD );
5026 if (m_bNewDoc && m_pStg && !pGloss)
5027 {
5028 // Initialize RDF metadata, to be able to add statements during the import.
5029 try
5030 {
5031 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5032 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
5033 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
5034 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5035 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
5036 uno::Reference<task::XInteractionHandler> xHandler;
5037 xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5038 }
5039 catch (const uno::Exception&)
5040 {
5041 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5042 }
5043 ReadDocInfo();
5044 }
5045
5046 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5047
5048 if (m_xWwFib->m_fReadOnlyRecommended)
5049 pFibData->setReadOnlyRecommended(true);
5050 else
5051 pFibData->setReadOnlyRecommended(false);
5052
5053 if (m_xWwFib->m_fWriteReservation)
5054 pFibData->setWriteReservation(true);
5055 else
5056 pFibData->setWriteReservation(false);
5057
5058 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5059
5060 ::sw::tExternalDataPointer pSttbfAsoc
5061 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5062
5063 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5064
5065 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5066 {
5067 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5068 if (pDocShell)
5069 pDocShell->SetReadOnlyUI();
5070 }
5071
5072 m_pPaM = mpCursor.get();
5073
5074 m_xCtrlStck.reset(new SwWW8FltControlStack(m_rDoc, m_nFieldFlags, *this));
5075
5076 m_xRedlineStack.reset(new sw::util::RedlineStack(m_rDoc));
5077
5078 /*
5079 RefFieldStck: Keeps track of bookmarks which may be inserted as
5080 variables instead.
5081 */
5082 m_xReffedStck.reset(new SwWW8ReferencedFltEndStack(m_rDoc, m_nFieldFlags));
5083 m_xReffingStck.reset(new SwWW8FltRefStack(m_rDoc, m_nFieldFlags));
5084
5085 m_xAnchorStck.reset(new SwWW8FltAnchorStack(m_rDoc, m_nFieldFlags));
5086
5087 size_t nPageDescOffset = m_rDoc.GetPageDescCnt();
5088
5089 RedlineFlags eMode = RedlineFlags::ShowInsert | RedlineFlags::ShowDelete;
5090
5091 m_oSprmParser.emplace(*m_xWwFib);
5092
5093 // Set handy helper variables
5094 m_bVer6 = (6 == m_xWwFib->m_nVersion);
5095 m_bVer7 = (7 == m_xWwFib->m_nVersion);
5096 m_bVer67 = m_bVer6 || m_bVer7;
5097 m_bVer8 = (8 == m_xWwFib->m_nVersion);
5098
5099 m_eTextCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chse, m_xWwFib->m_lid);
5100 m_eStructCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chseTables, m_xWwFib->m_lid);
5101
5102 m_bWWBugNormal = m_xWwFib->m_nProduct == 0xc03d;
5103
5104 m_xProgress.reset(new ImportProgress(m_pDocShell, 0, 100));
5105
5106 // read Font Table
5107 m_xFonts.reset(new WW8Fonts(*m_pTableStream, *m_xWwFib));
5108
5109 // Document Properties
5110 m_xWDop.reset(new WW8Dop(*m_pTableStream, m_xWwFib->m_nFib, m_xWwFib->m_fcDop,
5111 m_xWwFib->m_lcbDop));
5112
5113 if (m_bNewDoc)
5114 ImportDop();
5115
5116 /*
5117 Import revisioning data: author names
5118 */
5119 if( m_xWwFib->m_lcbSttbfRMark )
5120 {
5121 ReadRevMarkAuthorStrTabl(*m_pTableStream,
5122 m_xWwFib->m_fcSttbfRMark,
5123 m_xWwFib->m_lcbSttbfRMark, m_rDoc);
5124 }
5125
5126 // Initialize our String/ID map for Linked Sections
5127 std::vector<OUString> aLinkStrings;
5128 std::vector<ww::bytes> aStringIds;
5129
5130 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcSttbFnm,
5131 m_xWwFib->m_lcbSttbFnm, m_bVer67 ? 2 : 0, m_eStructCharSet,
5132 aLinkStrings, &aStringIds);
5133
5134 for (size_t i=0; i < aLinkStrings.size() && i < aStringIds.size(); ++i)
5135 {
5136 const ww::bytes& stringId = aStringIds[i];
5137 if (stringId.size() < sizeof(WW8_STRINGID))
5138 {
5139 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: WW8_STRINGID is too short");
5140 continue;
5141 }
5142 const WW8_STRINGID *stringIdStruct = reinterpret_cast<const WW8_STRINGID*>(stringId.data());
5143 m_aLinkStringMap[SVBT16ToUInt16(stringIdStruct->nStringId)] = aLinkStrings[i];
5144 }
5145
5146 ReadDocVars(); // import document variables as meta information.
5147
5148 m_xProgress->Update(m_nProgress); // Update
5149
5150 m_xLstManager.reset(new WW8ListManager(*m_pTableStream, *this));
5151
5152 /*
5153 first (1) import all styles (see WW8PAR2.CXX)
5154 BEFORE the import of the lists !!
5155 */
5156 m_xProgress->Update(m_nProgress); // Update
5157 m_xStyles.reset(new WW8RStyle(*m_xWwFib, this)); // Styles
5158 m_xStyles->Import();
5159
5160 /*
5161 In the end: (also see WW8PAR3.CXX)
5162
5163 Go through all Styles and attach respective List Format
5164 AFTER we imported the Styles and AFTER we imported the Lists!
5165 */
5166 m_xProgress->Update(m_nProgress); // Update
5167 m_xStyles->PostProcessStyles();
5168
5169 if (!m_vColl.empty())
5170 SetOutlineStyles();
5171
5172 m_xSBase.reset(new WW8ScannerBase(m_pStrm,m_pTableStream,m_pDataStream, m_xWwFib.get()));
5173
5174 if (m_xSBase->AreThereFootnotes())
5175 {
5176 static const SwFootnoteNum eNumA[4] =
5177 {
5178 FTNNUM_DOC, FTNNUM_CHAPTER, FTNNUM_PAGE, FTNNUM_DOC
5179 };
5180
5181 SwFootnoteInfo aInfo = m_rDoc.GetFootnoteInfo(); // Copy-Ctor private
5182
5183 aInfo.m_ePos = FTNPOS_PAGE;
5184 aInfo.m_eNum = eNumA[m_xWDop->rncFootnote];
5185 sal_uInt16 nfcFootnoteRef = m_xWDop->nfcFootnoteRef;
5186 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcFootnoteRef));
5187 if( m_xWDop->nFootnote )
5188 aInfo.m_nFootnoteOffset = m_xWDop->nFootnote - 1;
5189 m_rDoc.SetFootnoteInfo( aInfo );
5190 }
5191 if (m_xSBase->AreThereEndnotes())
5192 {
5193 SwEndNoteInfo aInfo = m_rDoc.GetEndNoteInfo(); // Same as for Footnote
5194 sal_uInt16 nfcEdnRef = m_xWDop->nfcEdnRef;
5195 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcEdnRef));
5196 if( m_xWDop->nEdn )
5197 aInfo.m_nFootnoteOffset = m_xWDop->nEdn - 1;
5198 m_rDoc.SetEndNoteInfo( aInfo );
5199 }
5200
5201 if (m_xWwFib->m_lcbPlcfhdd)
5202 m_xHdFt.reset(new WW8PLCF_HdFt(m_pTableStream, *m_xWwFib, *m_xWDop));
5203
5204 if (!m_bNewDoc)
5205 {
5206 // inserting into an existing document:
5207 // As only complete paragraphs are inserted, the current one
5208 // needs to be split - once or even twice.
5209 const SwPosition* pPos = m_pPaM->GetPoint();
5210
5211 // split current paragraph to get new paragraph for the insertion
5212 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5213
5214 // another split, if insertion position was not at the end of the current paragraph.
5215 SwTextNode const*const pTextNd = pPos->GetNode().GetTextNode();
5216 if ( pTextNd->GetText().getLength() )
5217 {
5218 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5219 // move PaM back to the newly empty paragraph
5220 m_pPaM->Move( fnMoveBackward );
5221 }
5222
5223 // suppress insertion of tables inside footnotes.
5224 const SwNodeOffset nNd = pPos->GetNodeIndex();
5225 m_bReadNoTable = ( nNd < m_rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
5226 m_rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd );
5227 }
5228
5229 m_xProgress->Update(m_nProgress); // Update
5230
5231 // loop for each glossary entry and add dummy section node
5232 if (pGloss)
5233 {
5234 WW8PLCF aPlc(*m_pTableStream, m_xWwFib->m_fcPlcfglsy, m_xWwFib->m_lcbPlcfglsy, 0);
5235
5236 WW8_CP nStart, nEnd;
5237 void* pDummy;
5238
5239 for (int i = 0; i < pGloss->GetNoStrings(); ++i, aPlc.advance())
5240 {
5241 SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfContent());
5242 SwTextFormatColl* pColl =
5243 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,
5244 false);
5245 SwStartNode *pNode =
5246 m_rDoc.GetNodes().MakeTextSection(aIdx.GetNode(),
5247 SwNormalStartNode,pColl);
5248 m_pPaM->GetPoint()->Assign( pNode->GetIndex()+1 );
5249 aPlc.Get( nStart, nEnd, pDummy );
5250 ReadText(nStart,nEnd-nStart-1,MAN_MAINTEXT);
5251 }
5252 }
5253 else // ordinary case
5254 {
5255 if (m_bNewDoc && m_pStg) /*meaningless for a glossary */
5256 {
5257 m_pDocShell->SetIsTemplate( m_xWwFib->m_fDot ); // point at tgc record
5258 uno::Reference<document::XDocumentPropertiesSupplier> const
5259 xDocPropSupp(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
5260 uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW );
5261
5262 OUString sCreatedFrom = xDocProps->getTemplateURL();
5263 uno::Reference< container::XNameContainer > xPrjNameCache;
5264 uno::Reference< lang::XMultiServiceFactory> xSF(m_pDocShell->GetModel(), uno::UNO_QUERY);
5265 if ( xSF.is() )
5266 xPrjNameCache.set( xSF->createInstance( u"ooo.vba.VBAProjectNameProvider"_ustr ), uno::UNO_QUERY );
5267
5268 // Read Global templates
5269 ReadGlobalTemplateSettings( sCreatedFrom, xPrjNameCache );
5270
5271 // Create and insert Word vba Globals
5272 uno::Any aGlobs;
5273 uno::Sequence< uno::Any > aArgs{ uno::Any(m_pDocShell->GetModel()) };
5274 try
5275 {
5276 aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( u"ooo.vba.word.Globals"_ustr, aArgs );
5277 }
5278 catch (const uno::Exception&)
5279 {
5280 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: ooo.vba.word.Globals is not available");
5281 }
5282
5283 #if HAVE_FEATURE_SCRIPTING
5284 if (!m_bFuzzing)
5285 {
5286 BasicManager *pBasicMan = m_pDocShell->GetBasicManager();
5287 if (pBasicMan)
5288 pBasicMan->SetGlobalUNOConstant( u"VBAGlobals"_ustr, aGlobs );
5289 }
5290 #endif
5291 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5292 // Import vba via oox filter
5293 bool bRet = aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5294
5295 lcl_createTemplateToProjectEntry( xPrjNameCache, sCreatedFrom, aBasicImporter.getProjectName() );
5296 WW8Customizations aCustomisations( m_pTableStream, *m_xWwFib );
5297 aCustomisations.Import( m_pDocShell );
5298
5299 if( bRet )
5300 m_rDoc.SetContainsMSVBasic(true);
5301
5302 StoreMacroCmds();
5303 }
5304 ReadText(0, m_xWwFib->m_ccpText, MAN_MAINTEXT);
5305 }
5306
5307 m_xProgress->Update(m_nProgress); // Update
5308
5309 if (m_pDrawPg && m_xMSDffManager && m_xMSDffManager->GetShapeOrders())
5310 {
5311 // Helper array to chain the inserted frames (instead of SdrTextObj)
5312 SvxMSDffShapeTxBxSort aTxBxSort;
5313
5314 // Ensure correct z-order of read Escher objects
5315 sal_uInt16 nShapeCount = m_xMSDffManager->GetShapeOrders()->size();
5316
5317 for (sal_uInt16 nShapeNum=0; nShapeNum < nShapeCount; nShapeNum++)
5318 {
5319 SvxMSDffShapeOrder *pOrder =
5320 (*m_xMSDffManager->GetShapeOrders())[nShapeNum].get();
5321 // Insert Pointer into new Sort array
5322 if (pOrder->nTxBxComp && pOrder->pFly)
5323 aTxBxSort.insert(pOrder);
5324 }
5325 // Chain Frames
5326 if( !aTxBxSort.empty() )
5327 {
5328 SwFormatChain aChain;
5329 for( SvxMSDffShapeTxBxSort::iterator it = aTxBxSort.begin(); it != aTxBxSort.end(); ++it )
5330 {
5331 SvxMSDffShapeOrder *pOrder = *it;
5332
5333 // Initialize FlyFrame Formats
5334 SwFlyFrameFormat* pFlyFormat = pOrder->pFly;
5335 SwFlyFrameFormat* pNextFlyFormat = nullptr;
5336 SwFlyFrameFormat* pPrevFlyFormat = nullptr;
5337
5338 // Determine successor, if we can
5339 SvxMSDffShapeTxBxSort::iterator tmpIter1 = it;
5340 ++tmpIter1;
5341 if( tmpIter1 != aTxBxSort.end() )
5342 {
5343 SvxMSDffShapeOrder *pNextOrder = *tmpIter1;
5344 if ((0xFFFF0000 & pOrder->nTxBxComp)
5345 == (0xFFFF0000 & pNextOrder->nTxBxComp))
5346 pNextFlyFormat = pNextOrder->pFly;
5347 }
5348 // Determine predecessor, if we can
5349 if( it != aTxBxSort.begin() )
5350 {
5351 SvxMSDffShapeTxBxSort::iterator tmpIter2 = it;
5352 --tmpIter2;
5353 SvxMSDffShapeOrder *pPrevOrder = *tmpIter2;
5354 if ((0xFFFF0000 & pOrder->nTxBxComp)
5355 == (0xFFFF0000 & pPrevOrder->nTxBxComp))
5356 pPrevFlyFormat = pPrevOrder->pFly;
5357 }
5358 // If successor or predecessor present, insert the
5359 // chain at the FlyFrame Format
5360 if (pNextFlyFormat || pPrevFlyFormat)
5361 {
5362 aChain.SetNext( pNextFlyFormat );
5363 aChain.SetPrev( pPrevFlyFormat );
5364 pFlyFormat->SetFormatAttr( aChain );
5365 }
5366 }
5367 }
5368 }
5369
5370 bool isHideRedlines(false);
5371
5372 if (m_bNewDoc)
5373 {
5374 if( m_xWDop->fRevMarking )
5375 eMode |= RedlineFlags::On;
5376 isHideRedlines = !m_xWDop->fRMView;
5377 }
5378
5379 m_aInsertedTables.DelAndMakeTableFrames();
5380 m_aSectionManager.InsertSegments();
5381
5382 m_vColl.clear();
5383
5384 m_xStyles.reset();
5385
5386 m_xFormImpl.reset();
5387 GraphicDtor();
5388 m_xMSDffManager.reset();
5389 m_xHdFt.reset();
5390 m_xSBase.reset();
5391 m_xWDop.reset();
5392 m_xFonts.reset();
5393 m_xAtnNames.reset();
5394 m_oSprmParser.reset();
5395 m_xProgress.reset();
5396
5397 m_pDataStream = nullptr;
5398 m_pTableStream = nullptr;
5399
5400 DeleteCtrlStack();
5401 DeleteAnchorStack();
5402 DeleteRefStacks();
5403 m_oLastAnchorPos.reset();//ensure this is deleted before UpdatePageDescs
5404 // ofz#10994 remove any trailing fly paras before processing redlines
5405 m_xWFlyPara.reset();
5406 // ofz#12660 remove any trailing fly paras before deleting extra paras
5407 m_xSFlyPara.reset();
5408 // remove extra paragraphs after attribute ctrl
5409 // stacks etc. are destroyed, and before fields
5410 // are updated
5411 m_aExtraneousParas.delete_all_from_doc();
5412 m_xRedlineStack->closeall(*m_pPaM->GetPoint());
5413
5414 // For i120928,achieve the graphics from the special bookmark with is for graphic bullet
5415 {
5416 std::vector<const SwGrfNode*> vecBulletGrf;
5417 std::vector<SwFrameFormat*> vecFrameFormat;
5418
5419 IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
5420 if ( pMarkAccess )
5421 {
5422 IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark( u"_PictureBullets"_ustr );
5423 if ( ppBkmk != pMarkAccess->getBookmarksEnd() &&
5424 IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::BOOKMARK )
5425 {
5426 SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().GetNode().GetTextNode();
5427
5428 if ( pTextNode )
5429 {
5430 const SwpHints* pHints = pTextNode->GetpSwpHints();
5431 for( size_t nHintPos = 0; pHints && nHintPos < pHints->Count(); ++nHintPos)
5432 {
5433 const SwTextAttr *pHt = pHints->Get(nHintPos);
5434 if (pHt->Which() != RES_TXTATR_FLYCNT)
5435 continue;
5436 const sal_Int32 st = pHt->GetStart();
5437 if (st >= (*ppBkmk)->GetMarkStart().GetContentIndex())
5438 {
5439 SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
5440 vecFrameFormat.push_back(pFrameFormat);
5441 const SwNodeIndex* pNdIdx = pFrameFormat->GetContent().GetContentIdx();
5442 const SwNodes* pNodesArray = (pNdIdx != nullptr)
5443 ? &(pNdIdx->GetNodes())
5444 : nullptr;
5445 const SwGrfNode *pGrf = (pNodesArray != nullptr)
5446 ? (*pNodesArray)[pNdIdx->GetIndex() + 1]->GetGrfNode()
5447 : nullptr;
5448 vecBulletGrf.push_back(pGrf);
5449 }
5450 }
5451 // update graphic bullet information
5452 size_t nCount = m_xLstManager->GetWW8LSTInfoNum();
5453 for (size_t i = 0; i < nCount; ++i)
5454 {
5455 SwNumRule* pRule = m_xLstManager->GetNumRule(i);
5456 for (sal_uInt16 j = 0; j < MAXLEVEL; ++j)
5457 {
5458 SwNumFormat aNumFormat(pRule->Get(j));
5459 const sal_Int16 nType = aNumFormat.GetNumberingType();
5460 const sal_uInt16 nGrfBulletCP = aNumFormat.GetGrfBulletCP();
5461 if ( nType == SVX_NUM_BITMAP
5462 && vecBulletGrf.size() > nGrfBulletCP
5463 && vecBulletGrf[nGrfBulletCP] != nullptr )
5464 {
5465 Graphic aGraphic = vecBulletGrf[nGrfBulletCP]->GetGrf();
5466 SvxBrushItem aBrush(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
5467 const vcl::Font& aFont = numfunc::GetDefBulletFont();
5468 int nHeight = aFont.GetFontHeight() * 12;
5469 Size aPrefSize( aGraphic.GetPrefSize());
5470 if (aPrefSize.Height() * aPrefSize.Width() != 0 )
5471 {
5472 int nWidth = (nHeight * aPrefSize.Width()) / aPrefSize.Height();
5473 Size aSize(nWidth, nHeight);
5474 aNumFormat.SetGraphicBrush(&aBrush, &aSize);
5475 }
5476 else
5477 {
5478 aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
5479 aNumFormat.SetBulletChar(0x2190);
5480 }
5481 pRule->Set( j, aNumFormat );
5482 }
5483 }
5484 }
5485 // Remove additional pictures
5486 for (SwFrameFormat* p : vecFrameFormat)
5487 {
5488 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat(p);
5489 }
5490 }
5491 }
5492 }
5493 m_xLstManager.reset();
5494 }
5495
5496 m_oPosAfterTOC.reset();
5497 m_xRedlineStack.reset();
5498 mpCursor.reset();
5499 m_pPaM = nullptr;
5500
5501 UpdateFields();
5502
5503 // delete the pam before the call for hide all redlines (Bug 73683)
5504 if (m_bNewDoc)
5505 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(eMode);
5506
5507 UpdatePageDescs(m_rDoc, nPageDescOffset);
5508
5509 // can't set it on the layout or view shell because it doesn't exist yet
5510 m_rDoc.GetDocumentRedlineManager().SetHideRedlines(isHideRedlines);
5511
5512 return ERRCODE_NONE;
5513 }
5514
SetSubStreams(rtl::Reference<SotStorageStream> & rTableStream,rtl::Reference<SotStorageStream> & rDataStream)5515 ErrCode SwWW8ImplReader::SetSubStreams(rtl::Reference<SotStorageStream>& rTableStream,
5516 rtl::Reference<SotStorageStream>& rDataStream)
5517 {
5518 ErrCode nErrRet = ERRCODE_NONE;
5519 // 6 stands for "6 OR 7", 7 stands for "ONLY 7"
5520 switch (m_xWwFib->m_nVersion)
5521 {
5522 case 6:
5523 case 7:
5524 m_pTableStream = m_pStrm;
5525 m_pDataStream = m_pStrm;
5526 break;
5527 case 8:
5528 if(!m_pStg)
5529 {
5530 OSL_ENSURE( m_pStg, "Version 8 always needs to have a Storage!!" );
5531 nErrRet = ERR_SWG_READ_ERROR;
5532 break;
5533 }
5534
5535 rTableStream = m_pStg->OpenSotStream(
5536 m_xWwFib->m_fWhichTableStm ? SL::a1Table : SL::a0Table,
5537 StreamMode::STD_READ);
5538
5539 m_pTableStream = rTableStream.get();
5540 m_pTableStream->SetEndian( SvStreamEndian::LITTLE );
5541
5542 rDataStream = m_pStg->OpenSotStream(SL::aData, StreamMode::STD_READ);
5543
5544 if (rDataStream.is() && ERRCODE_NONE == rDataStream->GetError())
5545 {
5546 m_pDataStream = rDataStream.get();
5547 m_pDataStream->SetEndian(SvStreamEndian::LITTLE);
5548 }
5549 else
5550 m_pDataStream = m_pStrm;
5551 break;
5552 default:
5553 // Program error!
5554 OSL_ENSURE( false, "We forgot to encode nVersion!" );
5555 nErrRet = ERR_SWG_READ_ERROR;
5556 break;
5557 }
5558 return nErrRet;
5559 }
5560
5561 namespace
5562 {
MakeTemp(std::optional<utl::TempFileFast> & roTempFile)5563 SvStream* MakeTemp(std::optional<utl::TempFileFast>& roTempFile)
5564 {
5565 roTempFile.emplace();
5566 return roTempFile->GetStream(StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
5567 }
5568
5569 #define WW_BLOCKSIZE 0x200
5570
DecryptRC4(msfilter::MSCodec97 & rCtx,SvStream & rIn,SvStream & rOut)5571 void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut)
5572 {
5573 const std::size_t nLen = rIn.TellEnd();
5574 rIn.Seek(0);
5575
5576 sal_uInt8 in[WW_BLOCKSIZE];
5577 for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
5578 {
5579 std::size_t nBS = std::min<size_t>(nLen - nI, WW_BLOCKSIZE);
5580 nBS = rIn.ReadBytes(in, nBS);
5581 rCtx.InitCipher(nBlock);
5582 rCtx.Decode(in, nBS, in, nBS);
5583 rOut.WriteBytes(in, nBS);
5584 }
5585 }
5586
DecryptXOR(msfilter::MSCodec_XorWord95 & rCtx,SvStream & rIn,SvStream & rOut)5587 void DecryptXOR(msfilter::MSCodec_XorWord95 &rCtx, SvStream &rIn, SvStream &rOut)
5588 {
5589 std::size_t nSt = rIn.Tell();
5590 std::size_t nLen = rIn.TellEnd();
5591
5592 rCtx.InitCipher();
5593 rCtx.Skip(nSt);
5594
5595 sal_uInt8 in[0x4096];
5596 for (std::size_t nI = nSt; nI < nLen; nI += 0x4096)
5597 {
5598 std::size_t nBS = std::min<size_t>(nLen - nI, 0x4096 );
5599 nBS = rIn.ReadBytes(in, nBS);
5600 rCtx.Decode(in, nBS);
5601 rOut.WriteBytes(in, nBS);
5602 }
5603 }
5604
5605 // moan, copy and paste :-(
QueryPasswordForMedium(SfxMedium & rMedium)5606 OUString QueryPasswordForMedium(SfxMedium& rMedium)
5607 {
5608 OUString aPassw;
5609
5610 if (const SfxStringItem* pPasswordItem = rMedium.GetItemSet().GetItemIfSet(SID_PASSWORD))
5611 aPassw = pPasswordItem->GetValue();
5612 else
5613 {
5614 try
5615 {
5616 uno::Reference< task::XInteractionHandler > xHandler( rMedium.GetInteractionHandler() );
5617 if( xHandler.is() )
5618 {
5619 rtl::Reference<::comphelper::DocPasswordRequest> pRequest = new ::comphelper::DocPasswordRequest(
5620 ::comphelper::DocPasswordRequestType::MS, task::PasswordRequestMode_PASSWORD_ENTER,
5621 INetURLObject(rMedium.GetOrigURL())
5622 .GetLastName(INetURLObject::DecodeMechanism::WithCharset));
5623
5624 xHandler->handle( pRequest );
5625
5626 if( pRequest->isPassword() )
5627 aPassw = pRequest->getPassword();
5628 }
5629 }
5630 catch( const uno::Exception& )
5631 {
5632 }
5633 }
5634
5635 return aPassw;
5636 }
5637
InitXorWord95Codec(::msfilter::MSCodec_XorWord95 & rCodec,SfxMedium & rMedium,WW8Fib const * pWwFib)5638 uno::Sequence< beans::NamedValue > InitXorWord95Codec( ::msfilter::MSCodec_XorWord95& rCodec, SfxMedium& rMedium, WW8Fib const * pWwFib )
5639 {
5640 uno::Sequence< beans::NamedValue > aEncryptionData;
5641 const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
5642 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5643 aEncryptionData.realloc( 0 );
5644
5645 if ( !aEncryptionData.hasElements() )
5646 {
5647 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5648
5649 OString sPassword(OUStringToOString(sUniPassword,
5650 WW8Fib::GetFIBCharset(pWwFib->m_chseTables, pWwFib->m_lid)));
5651
5652 sal_Int32 nLen = sPassword.getLength();
5653 if( nLen <= 15 )
5654 {
5655 sal_uInt8 pPassword[16];
5656 memcpy(pPassword, sPassword.getStr(), nLen);
5657 memset(pPassword+nLen, 0, sizeof(pPassword)-nLen);
5658
5659 rCodec.InitKey( pPassword );
5660 aEncryptionData = rCodec.GetEncryptionData();
5661
5662 // the export supports RC4 algorithm only, so we have to
5663 // generate the related EncryptionData as well, so that Save
5664 // can export the document without asking for a password;
5665 // as result there will be EncryptionData for both algorithms
5666 // in the MediaDescriptor
5667 ::msfilter::MSCodec_Std97 aCodec97;
5668
5669 sal_uInt8 pDocId[ 16 ];
5670 if (rtl_random_getBytes(nullptr, pDocId, 16) != rtl_Random_E_None)
5671 {
5672 throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
5673 }
5674
5675 sal_uInt16 pStd97Pass[16] = {};
5676 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5677 pStd97Pass[nChar] = sUniPassword[nChar];
5678
5679 aCodec97.InitKey( pStd97Pass, pDocId );
5680
5681 // merge the EncryptionData, there should be no conflicts
5682 ::comphelper::SequenceAsHashMap aEncryptionHash( aEncryptionData );
5683 aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
5684 aEncryptionHash >> aEncryptionData;
5685 }
5686 }
5687
5688 return aEncryptionData;
5689 }
5690
Init97Codec(msfilter::MSCodec97 & rCodec,sal_uInt8 const pDocId[16],SfxMedium & rMedium)5691 uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 const pDocId[16], SfxMedium& rMedium)
5692 {
5693 uno::Sequence< beans::NamedValue > aEncryptionData;
5694 const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
5695 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5696 aEncryptionData.realloc( 0 );
5697
5698 if ( !aEncryptionData.hasElements() )
5699 {
5700 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5701
5702 sal_Int32 nLen = sUniPassword.getLength();
5703 if ( nLen <= 15 )
5704 {
5705 sal_uInt16 pPassword[16] = {};
5706 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5707 pPassword[nChar] = sUniPassword[nChar];
5708
5709 rCodec.InitKey( pPassword, pDocId );
5710 aEncryptionData = rCodec.GetEncryptionData();
5711 }
5712 }
5713
5714 return aEncryptionData;
5715 }
5716 }
5717
5718 //TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different
5719 //stream thing
lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo & info,SvStream & rStream)5720 static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream)
5721 {
5722 //It is possible there are other variants in existence but these
5723 //are the defaults I get with Word 2013
5724
5725 rStream.ReadUInt32(info.header.flags);
5726 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
5727 return false;
5728
5729 sal_uInt32 nHeaderSize(0);
5730 rStream.ReadUInt32(nHeaderSize);
5731 sal_uInt32 actualHeaderSize = sizeof(info.header);
5732
5733 if (nHeaderSize < actualHeaderSize)
5734 return false;
5735
5736 rStream.ReadUInt32(info.header.flags);
5737 rStream.ReadUInt32(info.header.sizeExtra);
5738 rStream.ReadUInt32(info.header.algId);
5739 rStream.ReadUInt32(info.header.algIdHash);
5740 rStream.ReadUInt32(info.header.keyBits);
5741 rStream.ReadUInt32(info.header.providedType);
5742 rStream.ReadUInt32(info.header.reserved1);
5743 rStream.ReadUInt32(info.header.reserved2);
5744
5745 rStream.SeekRel(nHeaderSize - actualHeaderSize);
5746
5747 rStream.ReadUInt32(info.verifier.saltSize);
5748 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
5749 return false;
5750 rStream.ReadBytes(&info.verifier.salt, sizeof(info.verifier.salt));
5751 rStream.ReadBytes(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
5752
5753 rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize);
5754 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
5755 return false;
5756 rStream.ReadBytes(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
5757
5758 // check flags and algorithm IDs, required are AES128 and SHA-1
5759 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
5760 return false;
5761
5762 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
5763 return false;
5764
5765 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
5766 return false;
5767
5768 // hash algorithm ID 0 defaults to SHA-1 too
5769 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
5770 return false;
5771
5772 return true;
5773 }
5774
LoadThroughDecryption(WW8Glossary * pGloss)5775 ErrCode SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
5776 {
5777 ErrCode nErrRet = ERRCODE_NONE;
5778 if (pGloss)
5779 m_xWwFib = pGloss->GetFib();
5780 else
5781 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5782
5783 if (m_xWwFib->m_nFibError)
5784 nErrRet = ERR_SWG_READ_ERROR;
5785
5786 rtl::Reference<SotStorageStream> xTableStream, xDataStream;
5787
5788 if (!nErrRet)
5789 nErrRet = SetSubStreams(xTableStream, xDataStream);
5790
5791 std::optional<utl::TempFileFast> oTempMain;
5792 std::optional<utl::TempFileFast> oTempTable;
5793 std::optional<utl::TempFileFast> oTempData;
5794 SvStream* pDecryptMain = nullptr;
5795 SvStream* pDecryptTable = nullptr;
5796 SvStream* pDecryptData = nullptr;
5797
5798 bool bDecrypt = false;
5799 enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other;
5800 if (m_xWwFib->m_fEncrypted && !nErrRet)
5801 {
5802 if (!pGloss)
5803 {
5804 bDecrypt = true;
5805 if (8 != m_xWwFib->m_nVersion)
5806 eAlgo = XOR;
5807 else
5808 {
5809 if (m_xWwFib->m_nKey != 0)
5810 eAlgo = XOR;
5811 else
5812 {
5813 m_pTableStream->Seek(0);
5814 sal_uInt32 nEncType(0);
5815 m_pTableStream->ReadUInt32(nEncType);
5816 if (nEncType == msfilter::VERSION_INFO_1997_FORMAT)
5817 eAlgo = RC4;
5818 else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2)
5819 eAlgo = RC4CryptoAPI;
5820 }
5821 }
5822 }
5823 }
5824
5825 if (bDecrypt)
5826 {
5827 nErrRet = ERRCODE_SVX_WRONGPASS;
5828 SfxMedium* pMedium = m_pDocShell->GetMedium();
5829
5830 if ( pMedium )
5831 {
5832 switch (eAlgo)
5833 {
5834 default:
5835 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5836 break;
5837 case XOR:
5838 {
5839 msfilter::MSCodec_XorWord95 aCtx;
5840 uno::Sequence< beans::NamedValue > aEncryptionData = InitXorWord95Codec(aCtx, *pMedium, m_xWwFib.get());
5841
5842 // if initialization has failed the EncryptionData should be empty
5843 if (aEncryptionData.hasElements() && aCtx.VerifyKey(m_xWwFib->m_nKey, m_xWwFib->m_nHash))
5844 {
5845 nErrRet = ERRCODE_NONE;
5846 pDecryptMain = MakeTemp(oTempMain);
5847
5848 m_pStrm->Seek(0);
5849 size_t nUnencryptedHdr =
5850 (8 == m_xWwFib->m_nVersion) ? 0x44 : 0x34;
5851 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5852 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5853 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5854 pIn.reset();
5855
5856 DecryptXOR(aCtx, *m_pStrm, *pDecryptMain);
5857
5858 if (!m_pTableStream || m_pTableStream == m_pStrm)
5859 m_pTableStream = pDecryptMain;
5860 else
5861 {
5862 pDecryptTable = MakeTemp(oTempTable);
5863 DecryptXOR(aCtx, *m_pTableStream, *pDecryptTable);
5864 m_pTableStream = pDecryptTable;
5865 }
5866
5867 if (!m_pDataStream || m_pDataStream == m_pStrm)
5868 m_pDataStream = pDecryptMain;
5869 else
5870 {
5871 pDecryptData = MakeTemp(oTempData);
5872 DecryptXOR(aCtx, *m_pDataStream, *pDecryptData);
5873 m_pDataStream = pDecryptData;
5874 }
5875
5876 pMedium->GetItemSet().ClearItem( SID_PASSWORD );
5877 pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5878 }
5879 }
5880 break;
5881 case RC4:
5882 case RC4CryptoAPI:
5883 {
5884 std::unique_ptr<msfilter::MSCodec97> xCtx;
5885 msfilter::RC4EncryptionInfo info;
5886 bool bCouldReadHeaders;
5887
5888 if (eAlgo == RC4)
5889 {
5890 xCtx.reset(new msfilter::MSCodec_Std97);
5891 assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5);
5892 bCouldReadHeaders =
5893 checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) &&
5894 checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) &&
5895 checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5);
5896 }
5897 else
5898 {
5899 xCtx.reset(new msfilter::MSCodec_CryptoAPI);
5900 bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream);
5901 }
5902
5903 // if initialization has failed the EncryptionData should be empty
5904 uno::Sequence< beans::NamedValue > aEncryptionData;
5905 if (bCouldReadHeaders)
5906 aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium);
5907 else
5908 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5909 if (aEncryptionData.hasElements() && xCtx->VerifyKey(info.verifier.encryptedVerifier,
5910 info.verifier.encryptedVerifierHash))
5911 {
5912 nErrRet = ERRCODE_NONE;
5913
5914 pDecryptMain = MakeTemp(oTempMain);
5915
5916 m_pStrm->Seek(0);
5917 std::size_t nUnencryptedHdr = 0x44;
5918 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5919 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5920
5921 DecryptRC4(*xCtx, *m_pStrm, *pDecryptMain);
5922
5923 pDecryptMain->Seek(0);
5924 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5925 pIn.reset();
5926
5927 pDecryptTable = MakeTemp(oTempTable);
5928 DecryptRC4(*xCtx, *m_pTableStream, *pDecryptTable);
5929 m_pTableStream = pDecryptTable;
5930
5931 if (!m_pDataStream || m_pDataStream == m_pStrm)
5932 m_pDataStream = pDecryptMain;
5933 else
5934 {
5935 pDecryptData = MakeTemp(oTempData);
5936 DecryptRC4(*xCtx, *m_pDataStream, *pDecryptData);
5937 m_pDataStream = pDecryptData;
5938 }
5939
5940 pMedium->GetItemSet().ClearItem( SID_PASSWORD );
5941 pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5942 }
5943 }
5944 break;
5945 }
5946 }
5947
5948 if (nErrRet == ERRCODE_NONE)
5949 {
5950 m_pStrm = pDecryptMain;
5951 assert(m_pStrm);
5952 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5953 if (m_xWwFib->m_nFibError)
5954 nErrRet = ERR_SWG_READ_ERROR;
5955 }
5956 }
5957
5958 if (!nErrRet)
5959 nErrRet = CoreLoad(pGloss);
5960
5961 oTempMain.reset();
5962 oTempTable.reset();
5963 oTempData.reset();
5964
5965 m_xWwFib.reset();
5966 return nErrRet;
5967 }
5968
SetOutlineStyles()5969 void SwWW8ImplReader::SetOutlineStyles()
5970 {
5971 // If we are inserted into a document then don't clobber existing outline
5972 // levels.
5973 sal_uInt16 nOutlineStyleListLevelWithAssignment = 0;
5974 if (!m_bNewDoc)
5975 {
5976 ww8::ParaStyles aOutLined(sw::util::GetParaStyles(m_rDoc));
5977 sw::util::SortByAssignedOutlineStyleListLevel(aOutLined);
5978 ww8::ParaStyles::reverse_iterator aEnd = aOutLined.rend();
5979 for ( ww8::ParaStyles::reverse_iterator aIter = aOutLined.rbegin(); aIter < aEnd; ++aIter)
5980 {
5981 if ((*aIter)->IsAssignedToListLevelOfOutlineStyle())
5982 nOutlineStyleListLevelWithAssignment |= 1 << (*aIter)->GetAssignedOutlineStyleLevel();
5983 else
5984 break;
5985 }
5986 }
5987
5988 // Check applied WW8 list styles at WW8 Built-In Heading Styles
5989 // - Choose the list style which occurs most often as the one which provides
5990 // the list level properties for the Outline Style.
5991 // - Populate temporary list of WW8 Built-In Heading Styles for further
5992 // iteration
5993 std::vector<SwWW8StyInf*> aWW8BuiltInHeadingStyles;
5994 {
5995 sal_uInt16 nStyle = 0;
5996 std::map<const SwNumRule*, int> aWW8ListStyleCounts;
5997 std::map<const SwNumRule*, bool> aPreventUseAsChapterNumbering;
5998 for (SwWW8StyInf& rSI : m_vColl)
5999 {
6000 // Copy inherited numbering info since LO drops inheritance after ChapterNumbering
6001 // and only applies listLevel via style with the selected ChapterNumbering LFO.
6002 bool bReRegister = false;
6003 if (rSI.m_nBase && rSI.m_nBase < m_vColl.size()
6004 && m_vColl[rSI.m_nBase].HasWW8OutlineLevel())
6005 {
6006 if (rSI.m_nLFOIndex == USHRT_MAX)
6007 {
6008 rSI.m_nLFOIndex = m_vColl[rSI.m_nBase].m_nLFOIndex;
6009
6010 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6011 // A style never inherits numbering from Chapter Numbering.
6012 if (rSI.m_nLFOIndex != USHRT_MAX)
6013 {
6014 const SwNumRule* pNumRule = m_vColl[rSI.m_nBase].m_pOutlineNumrule;
6015 if (pNumRule)
6016 aPreventUseAsChapterNumbering[pNumRule] = true;
6017 }
6018 }
6019 if (rSI.m_nListLevel == MAXLEVEL)
6020 rSI.m_nListLevel = m_vColl[rSI.m_nBase].m_nListLevel;
6021 if (rSI.mnWW8OutlineLevel == MAXLEVEL)
6022 rSI.mnWW8OutlineLevel = m_vColl[rSI.m_nBase].mnWW8OutlineLevel;
6023 bReRegister = true;
6024 }
6025
6026 // Undefined listLevel is treated as the first level with valid numbering rule.
6027 if (rSI.m_nLFOIndex < USHRT_MAX && rSI.m_nListLevel == MAXLEVEL)
6028 {
6029 rSI.m_nListLevel = 0;
6030 bReRegister = true;
6031 }
6032
6033 if (bReRegister)
6034 RegisterNumFormatOnStyle(nStyle);
6035
6036 ++nStyle; // increment before the first "continue";
6037
6038 if (!rSI.m_bColl || !rSI.IsWW8BuiltInHeadingStyle() || !rSI.HasWW8OutlineLevel())
6039 {
6040 continue;
6041 }
6042
6043 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6044 if (rSI.IsOutlineNumbered() && rSI.m_nListLevel != rSI.mnWW8OutlineLevel)
6045 {
6046 aPreventUseAsChapterNumbering[rSI.m_pOutlineNumrule] = true;
6047 }
6048
6049 aWW8BuiltInHeadingStyles.push_back(&rSI);
6050
6051 const SwNumRule* pWW8ListStyle = rSI.GetOutlineNumrule();
6052 if (pWW8ListStyle != nullptr)
6053 {
6054 std::map<const SwNumRule*, int>::iterator aCountIter
6055 = aWW8ListStyleCounts.find(pWW8ListStyle);
6056 if (aCountIter == aWW8ListStyleCounts.end())
6057 {
6058 aWW8ListStyleCounts[pWW8ListStyle] = 1;
6059 }
6060 else
6061 {
6062 ++(aCountIter->second);
6063 }
6064 }
6065 }
6066
6067 int nCurrentMaxCount = 0;
6068 for (const auto& rEntry : aWW8ListStyleCounts)
6069 {
6070 if (aPreventUseAsChapterNumbering[rEntry.first])
6071 continue;
6072
6073 if (rEntry.second > nCurrentMaxCount)
6074 {
6075 nCurrentMaxCount = rEntry.second;
6076 m_pChosenWW8OutlineStyle = rEntry.first;
6077 }
6078 }
6079 }
6080
6081 // - set list level properties of Outline Style - ODF's list style applied
6082 // by default to headings
6083 // - assign corresponding Heading Paragraph Styles to the Outline Style
6084 // - If a heading Paragraph Styles is not applying the WW8 list style which
6085 // had been chosen as
6086 // the one which provides the list level properties for the Outline Style,
6087 // its assignment to
6088 // the Outline Style is removed. A potential applied WW8 list style is
6089 // assigned directly and
6090 // its default outline level is applied.
6091 SwNumRule aOutlineRule(*m_rDoc.GetOutlineNumRule());
6092 if (m_pChosenWW8OutlineStyle)
6093 {
6094 for (int i = 0; i < WW8ListManager::nMaxLevel; ++i)
6095 {
6096 // Don't clobber existing outline levels.
6097 const sal_uInt16 nLevel = 1 << i;
6098 if (!(nOutlineStyleListLevelWithAssignment & nLevel))
6099 aOutlineRule.Set(i, m_pChosenWW8OutlineStyle->Get(i));
6100 }
6101 }
6102
6103 for (const SwWW8StyInf* pStyleInf : aWW8BuiltInHeadingStyles)
6104 {
6105 const sal_uInt16 nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6106 = 1 << pStyleInf->mnWW8OutlineLevel;
6107 if (nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6108 & nOutlineStyleListLevelWithAssignment)
6109 {
6110 continue;
6111 }
6112
6113 // in case that there are more styles on this level ignore them
6114 nOutlineStyleListLevelWithAssignment
6115 |= nOutlineStyleListLevelOfWW8BuiltInHeadingStyle;
6116
6117 SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pStyleInf->m_pFormat);
6118 if (pStyleInf->GetOutlineNumrule() != m_pChosenWW8OutlineStyle
6119 || (pStyleInf->m_nListLevel < WW8ListManager::nMaxLevel
6120 && pStyleInf->mnWW8OutlineLevel != pStyleInf->m_nListLevel))
6121 {
6122 // WW8 Built-In Heading Style does not apply the chosen one.
6123 // --> delete assignment to OutlineStyle, but keep its current
6124 // outline level
6125 pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
6126 // Apply existing WW8 list style a normal list style at the
6127 // Paragraph Style
6128 if (pStyleInf->GetOutlineNumrule() != nullptr)
6129 {
6130 pTextFormatColl->SetFormatAttr(
6131 SwNumRuleItem(pStyleInf->GetOutlineNumrule()->GetName()));
6132 }
6133 // apply default outline level of WW8 Built-in Heading Style
6134 const sal_uInt8 nOutlineLevel
6135 = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(
6136 pStyleInf->mnWW8OutlineLevel);
6137 pTextFormatColl->SetFormatAttr(
6138 SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel));
6139 }
6140 else
6141 {
6142 pTextFormatColl->AssignToListLevelOfOutlineStyle(
6143 pStyleInf->mnWW8OutlineLevel);
6144 }
6145 }
6146
6147 if (m_pChosenWW8OutlineStyle)
6148 {
6149 m_rDoc.SetOutlineNumRule(aOutlineRule);
6150 }
6151 }
6152
GetAnnotationAuthor(sal_uInt16 nIdx)6153 const OUString* SwWW8ImplReader::GetAnnotationAuthor(sal_uInt16 nIdx)
6154 {
6155 if (!m_xAtnNames && m_xWwFib->m_lcbGrpStAtnOwners)
6156 {
6157 // Determine authors: can be found in the TableStream
6158 m_xAtnNames.emplace();
6159 SvStream& rStrm = *m_pTableStream;
6160
6161 auto nOldPos = rStrm.Tell();
6162 bool bValidPos = checkSeek(rStrm, m_xWwFib->m_fcGrpStAtnOwners);
6163 if (bValidPos)
6164 {
6165 tools::Long nRead = 0, nCount = m_xWwFib->m_lcbGrpStAtnOwners;
6166 while (nRead < nCount && rStrm.good())
6167 {
6168 if( m_bVer67 )
6169 {
6170 m_xAtnNames->push_back(read_uInt8_PascalString(rStrm,
6171 RTL_TEXTENCODING_MS_1252));
6172 nRead += m_xAtnNames->rbegin()->getLength() + 1; // Length + sal_uInt8 count
6173 }
6174 else
6175 {
6176 m_xAtnNames->push_back(read_uInt16_PascalString(rStrm));
6177 // Unicode: double the length + sal_uInt16 count
6178 nRead += (m_xAtnNames->rbegin()->getLength() + 1)*2;
6179 }
6180 }
6181 }
6182 rStrm.Seek( nOldPos );
6183 }
6184
6185 const OUString *pRet = nullptr;
6186 if (m_xAtnNames && nIdx < m_xAtnNames->size())
6187 pRet = &((*m_xAtnNames)[nIdx]);
6188 return pRet;
6189 }
6190
GetSmartTagInfo(SwFltRDFMark & rMark)6191 void SwWW8ImplReader::GetSmartTagInfo(SwFltRDFMark& rMark)
6192 {
6193 if (!m_pSmartTagData && m_xWwFib->m_lcbFactoidData)
6194 {
6195 m_pSmartTagData.reset(new WW8SmartTagData);
6196 m_pSmartTagData->Read(*m_pTableStream, m_xWwFib->m_fcFactoidData, m_xWwFib->m_lcbFactoidData);
6197 }
6198
6199 if (!m_pSmartTagData)
6200 return;
6201
6202 // Check if the handle is a valid smart tag bookmark index.
6203 size_t nIndex = rMark.GetHandle();
6204 if (nIndex >= m_pSmartTagData->m_aPropBags.size())
6205 return;
6206
6207 // Check if the smart tag bookmark refers to a valid factoid type.
6208 const MSOPropertyBag& rPropertyBag = m_pSmartTagData->m_aPropBags[rMark.GetHandle()];
6209 auto& rFactoidTypes = m_pSmartTagData->m_aPropBagStore.m_aFactoidTypes;
6210 auto itPropertyBag = std::find_if(rFactoidTypes.begin(), rFactoidTypes.end(),
6211 [&rPropertyBag](const MSOFactoidType& rType) { return rType.m_nId == rPropertyBag.m_nId; });
6212 if (itPropertyBag == rFactoidTypes.end())
6213 return;
6214
6215 // Check if the factoid is an RDF one.
6216 const MSOFactoidType& rFactoidType = *itPropertyBag;
6217 if (rFactoidType.m_aUri != "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
6218 return;
6219
6220 // Finally put the relevant attributes to the mark.
6221 std::vector< std::pair<OUString, OUString> > aAttributes;
6222 for (const MSOProperty& rProperty : rPropertyBag.m_aProperties)
6223 {
6224 OUString aKey;
6225 OUString aValue;
6226 if (rProperty.m_nKey < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6227 aKey = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nKey];
6228 if (rProperty.m_nValue < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6229 aValue = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nValue];
6230 if (!aKey.isEmpty() && !aValue.isEmpty())
6231 aAttributes.emplace_back(aKey, aValue);
6232 }
6233 rMark.SetAttributes(std::move(aAttributes));
6234 }
6235
LoadDoc(WW8Glossary * pGloss)6236 ErrCode SwWW8ImplReader::LoadDoc(WW8Glossary *pGloss)
6237 {
6238 ErrCode nErrRet = ERRCODE_NONE;
6239
6240 {
6241 static constexpr OUString aNames[ 13 ] = {
6242 u"WinWord/WW"_ustr, u"WinWord/WW8"_ustr, u"WinWord/WWFT"_ustr,
6243 u"WinWord/WWFLX"_ustr, u"WinWord/WWFLY"_ustr,
6244 u"WinWord/WWF"_ustr,
6245 u"WinWord/WWFA0"_ustr, u"WinWord/WWFA1"_ustr, u"WinWord/WWFA2"_ustr,
6246 u"WinWord/WWFB0"_ustr, u"WinWord/WWFB1"_ustr, u"WinWord/WWFB2"_ustr,
6247 u"WinWord/RegardHindiDigits"_ustr
6248 };
6249 sal_uInt64 aVal[ 13 ];
6250
6251 SwFilterOptions aOpt( 13, aNames, aVal );
6252
6253 m_nIniFlags = aVal[ 0 ];
6254 m_nIniFlags1= aVal[ 1 ];
6255 // Moves Flys by x twips to the right or left
6256 m_nIniFlyDx = aVal[ 3 ];
6257 m_nIniFlyDy = aVal[ 4 ];
6258
6259 m_nFieldFlags = aVal[ 5 ];
6260 m_nFieldTagAlways[0] = aVal[ 6 ];
6261 m_nFieldTagAlways[1] = aVal[ 7 ];
6262 m_nFieldTagAlways[2] = aVal[ 8 ];
6263 m_nFieldTagBad[0] = aVal[ 9 ];
6264 m_nFieldTagBad[1] = aVal[ 10 ];
6265 m_nFieldTagBad[2] = aVal[ 11 ];
6266 m_bRegardHindiDigits = aVal[ 12 ] > 0;
6267 }
6268
6269 sal_uInt16 nMagic(0);
6270 m_pStrm->ReadUInt16( nMagic );
6271
6272 // Remember: 6 means "6 OR 7", 7 means "JUST 7"
6273 switch (m_nWantedVersion)
6274 {
6275 case 6:
6276 case 7:
6277 if (
6278 0xa59b != nMagic && 0xa59c != nMagic &&
6279 0xa5dc != nMagic && 0xa5db != nMagic &&
6280 (nMagic < 0xa697 || nMagic > 0xa699)
6281 )
6282 {
6283 // Test for own 97 fake!
6284 if (m_pStg && 0xa5ec == nMagic)
6285 {
6286 sal_uInt64 nCurPos = m_pStrm->Tell();
6287 if (checkSeek(*m_pStrm, nCurPos + 2))
6288 {
6289 sal_uInt32 nfcMin(0);
6290 m_pStrm->ReadUInt32( nfcMin );
6291 if (0x300 != nfcMin)
6292 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6293 }
6294 m_pStrm->Seek( nCurPos );
6295 }
6296 else
6297 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6298 }
6299 break;
6300 case 8:
6301 if (0xa5ec != nMagic)
6302 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6303 break;
6304 default:
6305 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6306 OSL_ENSURE( false, "We forgot to encode nVersion!" );
6307 break;
6308 }
6309
6310 if (!nErrRet)
6311 nErrRet = LoadThroughDecryption(pGloss);
6312
6313 m_rDoc.PropagateOutlineRule();
6314
6315 return nErrRet;
6316 }
6317
ImportDOC()6318 extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOC()
6319 {
6320 return new WW8Reader;
6321 }
6322
6323 namespace
6324 {
6325 class FontCacheGuard
6326 {
6327 public:
~FontCacheGuard()6328 ~FontCacheGuard()
6329 {
6330 FlushFontCache();
6331 }
6332 };
6333 }
6334
TestImportDOC(SvStream & rStream,const OUString & rFltName)6335 bool TestImportDOC(SvStream &rStream, const OUString &rFltName)
6336 {
6337 FontCacheGuard aFontCacheGuard;
6338 std::unique_ptr<Reader> xReader(ImportDOC());
6339
6340 rtl::Reference<SotStorage> xStorage;
6341 xReader->m_pStream = &rStream;
6342 if (rFltName != "WW6")
6343 {
6344 try
6345 {
6346 xStorage.set(new SotStorage(rStream));
6347 if (xStorage->GetError())
6348 return false;
6349 }
6350 catch (...)
6351 {
6352 return false;
6353 }
6354 xReader->m_pStorage = xStorage.get();
6355 }
6356 xReader->SetFltName(rFltName);
6357
6358 SwGlobals::ensure();
6359
6360 SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
6361 xDocSh->DoInitNew();
6362 SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
6363
6364 SwPaM aPaM(pD->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
6365 pD->SetInReading(true);
6366 bool bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE;
6367 pD->SetInReading(false);
6368
6369 return bRet;
6370 }
6371
TestImportWW8(SvStream & rStream)6372 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW8(SvStream &rStream)
6373 {
6374 return TestImportDOC(rStream, u"CWW8"_ustr);
6375 }
6376
TestImportWW6(SvStream & rStream)6377 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW6(SvStream &rStream)
6378 {
6379 return TestImportDOC(rStream, u"CWW6"_ustr);
6380 }
6381
TestImportWW2(SvStream & rStream)6382 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW2(SvStream &rStream)
6383 {
6384 return TestImportDOC(rStream, u"WW6"_ustr);
6385 }
6386
OpenMainStream(rtl::Reference<SotStorageStream> & rRef,sal_uInt16 & rBuffSize)6387 ErrCode WW8Reader::OpenMainStream(rtl::Reference<SotStorageStream>& rRef, sal_uInt16& rBuffSize)
6388 {
6389 ErrCode nRet = ERR_SWG_READ_ERROR;
6390 OSL_ENSURE(m_pStorage, "Where is my Storage?");
6391 rRef = m_pStorage->OpenSotStream( u"WordDocument"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6392
6393 if( rRef.is() )
6394 {
6395 if( ERRCODE_NONE == rRef->GetError() )
6396 {
6397 sal_uInt16 nOld = rRef->GetBufferSize();
6398 rRef->SetBufferSize( rBuffSize );
6399 rBuffSize = nOld;
6400 nRet = ERRCODE_NONE;
6401 }
6402 else
6403 nRet = rRef->GetError();
6404 }
6405 return nRet;
6406 }
6407
lcl_getListOfStreams(SotStorage * pStorage,comphelper::SequenceAsHashMap & aStreamsData,std::u16string_view sPrefix)6408 static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, std::u16string_view sPrefix)
6409 {
6410 SvStorageInfoList aElements;
6411 pStorage->FillInfoList(&aElements);
6412 for (const auto & aElement : aElements)
6413 {
6414 OUString sStreamFullName = sPrefix.size() ? OUString::Concat(sPrefix) + "/" + aElement.GetName() : aElement.GetName();
6415 if (aElement.IsStorage())
6416 {
6417 rtl::Reference<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
6418 lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
6419 }
6420 else
6421 {
6422 // Read stream
6423 rtl::Reference<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
6424 if (rStream.is())
6425 {
6426 sal_Int32 nStreamSize = rStream->GetSize();
6427 css::uno::Sequence< sal_Int8 > oData;
6428 oData.realloc(nStreamSize);
6429 sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
6430 if (nStreamSize == nReadBytes)
6431 aStreamsData[sStreamFullName] <<= oData;
6432 }
6433 }
6434 }
6435 }
6436
DecryptDRMPackage()6437 ErrCode WW8Reader::DecryptDRMPackage()
6438 {
6439 // We have DRM encrypted storage. We should try to decrypt it first, if we can
6440 uno::Sequence< uno::Any > aArguments;
6441 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
6442 uno::Reference< packages::XPackageEncryption > xPackageEncryption(
6443 xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
6444 u"com.sun.star.comp.oox.crypto.DRMDataSpace"_ustr, aArguments, xComponentContext), uno::UNO_QUERY);
6445
6446 if (!xPackageEncryption.is())
6447 {
6448 // We do not know how to decrypt this
6449 return ERRCODE_IO_ACCESSDENIED;
6450 }
6451
6452 comphelper::SequenceAsHashMap aStreamsData;
6453 lcl_getListOfStreams(m_pStorage.get(), aStreamsData, u"");
6454
6455 try {
6456 uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
6457 if (!xPackageEncryption->readEncryptionInfo(aStreams))
6458 {
6459 // We failed with decryption
6460 return ERRCODE_IO_ACCESSDENIED;
6461 }
6462
6463 rtl::Reference<SotStorageStream> rContentStream = m_pStorage->OpenSotStream(u"\011DRMContent"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6464 if (!rContentStream.is())
6465 {
6466 return ERRCODE_IO_NOTEXISTS;
6467 }
6468
6469 mDecodedStream = std::make_shared<SvMemoryStream>();
6470
6471 uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
6472 uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*mDecodedStream));
6473
6474 if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
6475 {
6476 // We failed with decryption
6477 return ERRCODE_IO_ACCESSDENIED;
6478 }
6479
6480 mDecodedStream->Seek(0);
6481
6482 // Further reading is done from new document
6483 m_pStorage = new SotStorage(*mDecodedStream);
6484
6485 // Set the media descriptor data
6486 uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData(u""_ustr);
6487 m_pMedium->GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
6488 }
6489 catch (const std::exception&)
6490 {
6491 return ERRCODE_IO_ACCESSDENIED;
6492 }
6493
6494 return ERRCODE_NONE;
6495 }
6496
Read(SwDoc & rDoc,const OUString & rBaseURL,SwPaM & rPaM,const OUString &)6497 ErrCodeMsg WW8Reader::Read(SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & /* FileName */)
6498 {
6499 sal_uInt16 nOldBuffSize = 32768;
6500 bool bNew = !m_bInsertMode; // New Doc (no inserting)
6501
6502 rtl::Reference<SotStorageStream> refStrm; // So that no one else can steal the Stream
6503 SvStream* pIn = m_pStream;
6504
6505 ErrCode nRet = ERRCODE_NONE;
6506 sal_uInt8 nVersion = 8;
6507
6508 const OUString sFltName = GetFltName();
6509 if ( sFltName=="WW6" )
6510 {
6511 if (m_pStream)
6512 nVersion = 6;
6513 else
6514 {
6515 OSL_ENSURE(false, "WinWord 95 Reader-Read without Stream");
6516 nRet = ERR_SWG_READ_ERROR;
6517 }
6518 }
6519 else
6520 {
6521 if ( sFltName=="CWW6" )
6522 nVersion = 6;
6523 else if ( sFltName=="CWW7" )
6524 nVersion = 7;
6525
6526 if( m_pStorage.is() )
6527 {
6528 // Check if we have special encrypted content
6529 rtl::Reference<SotStorageStream> rRef = m_pStorage->OpenSotStream(u"\006DataSpaces/DataSpaceInfo/\011DRMDataSpace"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6530 if (rRef.is())
6531 {
6532 nRet = DecryptDRMPackage();
6533 }
6534 nRet = OpenMainStream(refStrm, nOldBuffSize);
6535 pIn = refStrm.get();
6536 }
6537 else
6538 {
6539 OSL_ENSURE(false, "WinWord 95/97 Reader-Read without Storage");
6540 nRet = ERR_SWG_READ_ERROR;
6541 }
6542 }
6543
6544 if( !nRet )
6545 {
6546 std::unique_ptr<SwWW8ImplReader> pRdr(new SwWW8ImplReader(nVersion, m_pStorage.get(), pIn, rDoc,
6547 rBaseURL, bNew, m_bSkipImages, *rPaM.GetPoint()));
6548 if (bNew)
6549 {
6550 rPaM.GetBound().nContent.Assign(nullptr, 0);
6551 rPaM.GetBound(false).nContent.Assign(nullptr, 0);
6552 }
6553 try
6554 {
6555 nRet = pRdr->LoadDoc();
6556 }
6557 catch( const std::exception& )
6558 {
6559 nRet = ERR_WW8_NO_WW8_FILE_ERR;
6560 }
6561
6562 if( refStrm.is() )
6563 {
6564 refStrm->SetBufferSize( nOldBuffSize );
6565 refStrm.clear();
6566 }
6567 else
6568 {
6569 pIn->ResetError();
6570 }
6571
6572 }
6573 return nRet;
6574 }
6575
GetReaderType()6576 SwReaderType WW8Reader::GetReaderType()
6577 {
6578 return SwReaderType::Storage | SwReaderType::Stream;
6579 }
6580
HasGlossaries() const6581 bool WW8Reader::HasGlossaries() const
6582 {
6583 return true;
6584 }
6585
ReadGlossaries(SwTextBlocks & rBlocks,bool bSaveRelFiles) const6586 bool WW8Reader::ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const
6587 {
6588 bool bRet=false;
6589
6590 WW8Reader *pThis = const_cast<WW8Reader *>(this);
6591
6592 sal_uInt16 nOldBuffSize = 32768;
6593 rtl::Reference<SotStorageStream> refStrm;
6594 if (!pThis->OpenMainStream(refStrm, nOldBuffSize))
6595 {
6596 WW8Glossary aGloss( refStrm, 8, m_pStorage.get() );
6597 bRet = aGloss.Load( rBlocks, bSaveRelFiles );
6598 }
6599 return bRet;
6600 }
6601
GetOLEStorageName(sal_uInt32 nOLEId,OUString & rStorageName,rtl::Reference<SotStorage> & rSrcStorage,uno::Reference<embed::XStorage> & rDestStorage) const6602 bool SwMSDffManager::GetOLEStorageName(sal_uInt32 nOLEId, OUString& rStorageName,
6603 rtl::Reference<SotStorage>& rSrcStorage, uno::Reference < embed::XStorage >& rDestStorage) const
6604 {
6605 bool bRet = false;
6606
6607 sal_Int32 nPictureId = 0;
6608 if (m_rReader.m_pStg)
6609 {
6610 // Via the TextBox-PLCF we get the right char Start-End positions
6611 // We should then find the EmbeddedField and the corresponding Sprms
6612 // in that Area.
6613 // We only need the Sprm for the Picture Id.
6614 sal_uInt64 nOldPos = m_rReader.m_pStrm->Tell();
6615 {
6616 // #i32596# - consider return value of method
6617 // <rReader.GetTxbxTextSttEndCp(..)>. If it returns false, method
6618 // wasn't successful. Thus, continue in this case.
6619 // Note: Ask MM for initialization of <nStartCp> and <nEndCp>.
6620 // Note: Ask MM about assertions in method <rReader.GetTxbxTextSttEndCp(..)>.
6621 WW8_CP nStartCp, nEndCp;
6622 if ( m_rReader.m_bDrawCpOValid && m_rReader.GetTxbxTextSttEndCp(nStartCp, nEndCp,
6623 o3tl::narrowing<sal_uInt16>((nOLEId >> 16) & 0xFFFF),
6624 o3tl::narrowing<sal_uInt16>(nOLEId & 0xFFFF)) )
6625 {
6626 WW8PLCFxSaveAll aSave;
6627 m_rReader.m_xPlcxMan->SaveAllPLCFx( aSave );
6628
6629 nStartCp += m_rReader.m_nDrawCpO;
6630 nEndCp += m_rReader.m_nDrawCpO;
6631 WW8PLCFx_Cp_FKP* pChp = m_rReader.m_xPlcxMan->GetChpPLCF();
6632 wwSprmParser aSprmParser(*m_rReader.m_xWwFib);
6633 while (nStartCp <= nEndCp && !nPictureId)
6634 {
6635 if (!pChp->SeekPos( nStartCp))
6636 break;
6637 WW8PLCFxDesc aDesc;
6638 pChp->GetSprms( &aDesc );
6639
6640 if (aDesc.nSprmsLen && aDesc.pMemPos) // Attributes present
6641 {
6642 auto nLen = aDesc.nSprmsLen;
6643 const sal_uInt8* pSprm = aDesc.pMemPos;
6644
6645 while (nLen >= 2 && !nPictureId)
6646 {
6647 sal_uInt16 nId = aSprmParser.GetSprmId(pSprm);
6648 sal_Int32 nSL = aSprmParser.GetSprmSize(nId, pSprm, nLen);
6649
6650 if( nLen < nSL )
6651 break; // Not enough Bytes left
6652
6653 if (0x6A03 == nId)
6654 {
6655 nPictureId = SVBT32ToUInt32(pSprm +
6656 aSprmParser.DistanceToData(nId));
6657 bRet = true;
6658 }
6659 pSprm += nSL;
6660 nLen -= nSL;
6661 }
6662 }
6663 nStartCp = aDesc.nEndPos;
6664 }
6665
6666 m_rReader.m_xPlcxMan->RestoreAllPLCFx( aSave );
6667 }
6668 }
6669 m_rReader.m_pStrm->Seek( nOldPos );
6670 }
6671
6672 if( bRet )
6673 {
6674 rStorageName = "_";
6675 rStorageName += OUString::number(nPictureId);
6676 rSrcStorage = m_rReader.m_pStg->OpenSotStorage(SL::aObjectPool);
6677 if (!m_rReader.m_pDocShell)
6678 bRet=false;
6679 else
6680 rDestStorage = m_rReader.m_pDocShell->GetStorage();
6681 }
6682 return bRet;
6683 }
6684
6685 /**
6686 * When reading a single Box (which possibly is part of a group), we do
6687 * not yet have enough information to decide whether we need it as a TextField
6688 * or not.
6689 * So convert all of them as a precaution.
6690 * FIXME: Actually implement this!
6691 */
ShapeHasText(sal_uLong,sal_uLong) const6692 bool SwMSDffManager::ShapeHasText(sal_uLong, sal_uLong) const
6693 {
6694 return true;
6695 }
6696
InEqualOrHigherApo(int nLvl) const6697 bool SwWW8ImplReader::InEqualOrHigherApo(int nLvl) const
6698 {
6699 if (nLvl)
6700 --nLvl;
6701 // #i60827# - check size of <maApos> to assure that <maApos.begin() + nLvl> can be performed.
6702 if ( sal::static_int_cast< sal_Int32>(nLvl) >= sal::static_int_cast< sal_Int32>(m_aApos.size()) )
6703 {
6704 return false;
6705 }
6706 auto aIter = std::find(m_aApos.begin() + nLvl, m_aApos.end(), true);
6707 return aIter != m_aApos.end();
6708 }
6709
InEqualApo(int nLvl) const6710 bool SwWW8ImplReader::InEqualApo(int nLvl) const
6711 {
6712 // If we are in a table, see if an apo was inserted at the level below the table.
6713 if (nLvl)
6714 --nLvl;
6715 if (nLvl < 0 || o3tl::make_unsigned(nLvl) >= m_aApos.size())
6716 return false;
6717 return m_aApos[nLvl];
6718 }
6719
6720 namespace sw::hack
6721 {
Position(const SwPosition & rPos)6722 Position::Position(const SwPosition &rPos)
6723 : maPtNode(rPos.GetNode()), mnPtContent(rPos.GetContentIndex())
6724 {
6725 }
6726
operator SwPosition() const6727 Position::operator SwPosition() const
6728 {
6729 return SwPosition(maPtNode, maPtNode.GetNode().GetContentNode(), mnPtContent);
6730 }
6731 }
6732
SwMacroInfo()6733 SwMacroInfo::SwMacroInfo()
6734 : SdrObjUserData( SdrInventor::ScOrSwDraw, SW_UD_IMAPDATA )
6735 , mnShapeId(-1)
6736 {
6737 }
6738
~SwMacroInfo()6739 SwMacroInfo::~SwMacroInfo()
6740 {
6741 }
6742
Clone(SdrObject *) const6743 std::unique_ptr<SdrObjUserData> SwMacroInfo::Clone( SdrObject* /*pObj*/ ) const
6744 {
6745 return std::unique_ptr<SdrObjUserData>(new SwMacroInfo( *this ));
6746 }
6747
SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet)6748 std::unique_ptr<SfxItemSet> SwWW8ImplReader::SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet)
6749 {
6750 std::unique_ptr<SfxItemSet> xRet(std::move(m_xCurrentItemSet));
6751 m_xCurrentItemSet = std::move(pItemSet);
6752 return xRet;
6753 }
6754
NotifyMacroEventRead()6755 void SwWW8ImplReader::NotifyMacroEventRead()
6756 {
6757 if (m_bNotifyMacroEventRead)
6758 return;
6759 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
6760 comphelper::DocumentInfo::notifyMacroEventRead(xModel);
6761 m_bNotifyMacroEventRead = true;
6762 }
6763
6764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6765