cid#1636677 Uninitialized scalar field
[LibreOffice.git] / sw / source / filter / ww8 / ww8par.cxx
blobd2f67900b412ce0346a125572037c3e2b4b08418
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
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>
32 #include <i18nlangtag/languagetag.hxx>
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>
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>
48 #include <comphelper/docpasswordrequest.hxx>
49 #include <comphelper/documentinfo.hxx>
50 #include <comphelper/propertysequence.hxx>
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"
111 #include "writerwordglue.hxx"
113 #include <ndgrf.hxx>
114 #include <editeng/editids.hrc>
115 #include <fmtflcnt.hxx>
116 #include <txatbase.hxx>
118 #include "ww8par2.hxx"
120 #include <com/sun/star/beans/PropertyAttribute.hpp>
121 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
122 #include <com/sun/star/document/XViewDataSupplier.hpp>
124 #include <svl/lngmisc.hxx>
125 #include <svl/itemiter.hxx>
126 #include <svl/whiter.hxx>
128 #include <comphelper/indexedpropertyvalues.hxx>
129 #include <comphelper/processfactory.hxx>
130 #include <basic/basmgr.hxx>
132 #include "ww8toolbar.hxx"
133 #include <o3tl/unit_conversion.hxx>
134 #include <o3tl/safeint.hxx>
135 #include <osl/file.hxx>
137 #include <breakit.hxx>
139 #include <sfx2/docfile.hxx>
140 #include <swdll.hxx>
141 #include "WW8Sttbf.hxx"
142 #include "WW8FibData.hxx"
143 #include <unordered_set>
144 #include <memory>
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>
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 #include <unotxdoc.hxx>
161 using namespace ::com::sun::star;
162 using namespace sw::util;
163 using namespace sw::types;
164 using namespace nsHdFtFlags;
166 static SwMacroInfo* GetMacroInfo( SdrObject* pObj )
168 if ( pObj )
170 sal_uInt16 nCount = pObj->GetUserDataCount();
171 for( sal_uInt16 i = 0; i < nCount; i++ )
173 SdrObjUserData* pData = pObj->GetUserData( i );
174 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
175 && pData->GetId() == SW_UD_IMAPDATA)
177 return dynamic_cast<SwMacroInfo*>(pData);
180 SwMacroInfo* pData = new SwMacroInfo;
181 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
182 return pData;
185 return nullptr;
188 static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell)
190 OUStringBuffer aTmpStr;
191 while( nLevel )
193 aTmpStr.append("../");
194 --nLevel;
196 if (!aTmpStr.isEmpty())
197 aTmpStr.append(rPath);
198 else
199 aTmpStr = rPath;
201 if (!aTmpStr.isEmpty())
203 bool bWasAbs = false;
204 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
205 // full path as stored in SvxURLField must be encoded
209 namespace
211 void lclIgnoreUString32(SvStream& rStrm)
213 sal_uInt32 nChars(0);
214 rStrm.ReadUInt32(nChars);
215 nChars *= 2;
216 rStrm.SeekRel(nChars);
220 // returns true if an embedded null was found
221 static bool clipToFirstNull(OUString& rStr)
223 sal_Int32 nEmbeddedNullIdx = rStr.indexOf(0);
224 if (nEmbeddedNullIdx != -1)
225 rStr = rStr.copy(0, nEmbeddedNullIdx);
226 return nEmbeddedNullIdx != -1;
229 void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
231 // (0x01B8) HLINK
232 // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
233 constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL.
234 constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path.
235 constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description.
236 constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark.
237 constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame.
238 constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path.
240 //sal_uInt8 maGuidStdLink[ 16 ] ={
241 // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
243 sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
244 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
246 sal_uInt8 const aGuidFileMoniker[ 16 ] = {
247 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
249 sal_uInt8 aGuid[16];
250 sal_uInt32 nFlags(0);
252 rStrm.ReadBytes(aGuid, 16);
253 rStrm.SeekRel( 4 );
254 rStrm.ReadUInt32( nFlags );
256 std::unique_ptr< OUString > xLongName; // link / file name
257 std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
258 std::unique_ptr< OUString > xTextMark; // text mark
260 // description (ignore)
261 if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
262 lclIgnoreUString32( rStrm );
264 // target frame
265 if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
267 hlStr.tarFrame = read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm);
270 // UNC path
271 if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
273 // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the
274 // string field, including the null-terminating character.
275 sal_uInt32 nStrLen(0);
276 rStrm.ReadUInt32(nStrLen);
277 if (nStrLen)
279 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
280 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
281 lclGetAbsPath( *xLongName, 0 , pDocShell);
284 // file link or URL
285 else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
287 rStrm.ReadBytes(aGuid, 16);
289 if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
291 sal_uInt16 nLevel = 0; // counter for level to climb down in path
292 rStrm.ReadUInt16( nLevel );
293 // MS-OSHARED: An unsigned integer that specifies the number of
294 // ANSI characters in ansiPath, including the terminating NULL character
295 sal_uInt32 nUnits = 0;
296 rStrm.ReadUInt32(nUnits);
297 if (!nUnits)
298 xShortName.reset(new OUString);
299 else
301 OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1));
302 rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
303 xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage()));
305 rStrm.SeekRel( 24 );
307 sal_uInt32 nStrLen(0);
308 rStrm.ReadUInt32( nStrLen );
309 if( nStrLen )
311 nStrLen = 0;
312 rStrm.ReadUInt32( nStrLen );
313 nStrLen /= 2;
314 rStrm.SeekRel( 2 );
315 // MS-OSHARED: This array MUST not include a terminating NULL character.
316 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen)));
317 lclGetAbsPath( *xLongName, nLevel, pDocShell);
319 else
320 lclGetAbsPath( *xShortName, nLevel, pDocShell);
322 else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
324 // MS-OSHARED: An unsigned integer that specifies the size of this
325 // structure in bytes, excluding the size of the length field. The
326 // value of this field MUST be ... the byte size of the url
327 // field (including the terminating NULL character)
328 sal_uInt32 nStrLen(0);
329 rStrm.ReadUInt32( nStrLen );
330 nStrLen /= 2;
331 if (!nStrLen)
332 xLongName.reset(new OUString);
333 else
335 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
336 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
338 if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
339 lclGetAbsPath( *xLongName, 0 ,pDocShell);
341 else
343 SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
347 // text mark
348 if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
350 xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
351 if (clipToFirstNull(*xTextMark))
352 SAL_WARN("sw.ww8", "HLINK_MARK with embedded null, truncating to: " << *xTextMark);
355 if (!xLongName && xShortName)
356 xLongName.reset(new OUString(*xShortName));
357 else if (!xLongName && xTextMark)
358 xLongName.reset( new OUString );
360 if (xLongName)
362 if (clipToFirstNull(*xLongName))
363 SAL_WARN("sw.ww8", "HLINK with embedded null, truncating to: " << *xLongName);
364 if (xTextMark)
366 if (xLongName->isEmpty())
367 *xTextMark = xTextMark->replace('!', '.');
368 *xLongName += "#" + *xTextMark;
370 hlStr.hLinkAddr = *xLongName;
374 namespace {
376 class BasicProjImportHelper
378 SwDocShell& mrDocShell;
379 uno::Reference< uno::XComponentContext > mxCtx;
380 public:
381 explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
382 mxCtx(comphelper::getProcessComponentContext())
385 bool import( const uno::Reference< io::XInputStream >& rxIn );
386 OUString getProjectName() const;
391 bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
393 bool bRet = false;
396 oox::ole::OleStorage root( mxCtx, rxIn, false );
397 oox::StorageRef vbaStg = root.openSubStorage( u"Macros"_ustr , false );
398 if ( vbaStg )
400 oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
401 bRet = aVbaPrj.importVbaProject( *vbaStg );
404 catch( const uno::Exception& )
406 bRet = false;
408 return bRet;
411 OUString BasicProjImportHelper::getProjectName() const
413 OUString sProjName( u"Standard"_ustr );
414 uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
415 if ( !xProps )
416 return sProjName;
419 uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( u"BasicLibraries"_ustr ), uno::UNO_QUERY );
420 if ( !xVBA )
421 return sProjName;
422 sProjName = xVBA->getProjectName();
425 catch( const uno::Exception& )
428 return sProjName;
431 namespace {
433 class Sttb : public TBBase
435 struct SBBItem
437 sal_uInt16 cchData;
438 OUString data;
439 SBBItem() : cchData(0){}
441 sal_uInt16 m_fExtend;
442 sal_uInt16 m_cData;
443 sal_uInt16 m_cbExtra;
445 std::vector< SBBItem > m_dataItems;
447 Sttb(Sttb const&) = delete;
448 Sttb& operator=(Sttb const&) = delete;
450 public:
451 Sttb();
453 bool Read(SvStream &rS) override;
454 OUString getStringAtIndex( sal_uInt32 );
459 Sttb::Sttb()
460 : m_fExtend(0)
461 , m_cData(0)
462 , m_cbExtra(0)
466 bool Sttb::Read( SvStream& rS )
468 SAL_INFO("sw.ww8", "stream pos " << rS.Tell());
469 nOffSet = rS.Tell();
470 rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
471 if ( m_cData )
473 //if they are all going to be empty strings, how many could there be
474 const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16);
475 if (m_cData > nMaxPossibleRecords)
476 return false;
477 for ( sal_Int32 index = 0; index < m_cData; ++index )
479 SBBItem aItem;
480 rS.ReadUInt16( aItem.cchData );
481 aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
482 m_dataItems.push_back( aItem );
485 return true;
488 OUString
489 Sttb::getStringAtIndex( sal_uInt32 index )
491 OUString aRet;
492 if ( index < m_dataItems.size() )
493 aRet = m_dataItems[ index ].data;
494 return aRet;
498 SwMSDffManager::SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages )
499 : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo,
500 rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages),
501 m_rReader(rRdr), m_pFallbackStream(nullptr)
503 SetSvxMSDffSettings( GetSvxMSDffSettings() );
504 nSvxMSDffOLEConvFlags = SwMSDffManager::GetFilterFlags();
507 sal_uInt32 SwMSDffManager::GetFilterFlags()
509 sal_uInt32 nFlags(0);
510 if (officecfg::Office::Common::Filter::Microsoft::Import::MathTypeToMath::get())
511 nFlags |= OLE_MATHTYPE_2_STARMATH;
512 if (officecfg::Office::Common::Filter::Microsoft::Import::ExcelToCalc::get())
513 nFlags |= OLE_EXCEL_2_STARCALC;
514 if (officecfg::Office::Common::Filter::Microsoft::Import::PowerPointToImpress::get())
515 nFlags |= OLE_POWERPOINT_2_STARIMPRESS;
516 if (officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get())
517 nFlags |= OLE_WINWORD_2_STARWRITER;
518 return nFlags;
522 * I would like to override the default OLE importing to add a test
523 * and conversion of OCX controls from their native OLE type into our
524 * native nonOLE Form Control Objects.
526 // #i32596# - consider new parameter <_nCalledByGroup>
527 rtl::Reference<SdrObject> SwMSDffManager::ImportOLE( sal_uInt32 nOLEId,
528 const Graphic& rGrf,
529 const tools::Rectangle& rBoundRect,
530 const tools::Rectangle& rVisArea,
531 const int _nCalledByGroup ) const
533 // #i32596# - no import of OLE object, if it's inside a group.
534 // NOTE: This can be undone, if grouping of Writer fly frames is possible or
535 // if drawing OLE objects are allowed in Writer.
536 if ( _nCalledByGroup > 0 )
538 return nullptr;
541 rtl::Reference<SdrObject> pRet;
542 OUString sStorageName;
543 rtl::Reference<SotStorage> xSrcStg;
544 uno::Reference < embed::XStorage > xDstStg;
545 if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
547 rtl::Reference<SotStorage> xSrc = xSrcStg->OpenSotStorage(sStorageName);
548 OSL_ENSURE(m_rReader.m_xFormImpl, "No Form Implementation!");
549 css::uno::Reference< css::drawing::XShape > xShape;
550 if ( (!(m_rReader.m_bIsHeader || m_rReader.m_bIsFooter)) &&
551 m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
553 pRet = SdrObject::getSdrObjectFromXShape(xShape);
555 else
557 ErrCode nError = ERRCODE_NONE;
558 pRet = CreateSdrOLEFromStorage(
559 *pSdrModel,
560 sStorageName,
561 xSrcStg,
562 xDstStg,
563 rGrf,
564 rBoundRect,
565 rVisArea,
566 pStData,
567 nError,
568 nSvxMSDffOLEConvFlags,
569 css::embed::Aspects::MSOLE_CONTENT,
570 m_rReader.GetBaseURL());
573 return pRet;
576 void SwMSDffManager::DisableFallbackStream()
578 OSL_ENSURE(!m_pFallbackStream,
579 "if you're recursive, you're broken");
580 m_pFallbackStream = pStData2;
581 m_aOldEscherBlipCache = aEscherBlipCache;
582 aEscherBlipCache.clear();
583 pStData2 = nullptr;
586 void SwMSDffManager::EnableFallbackStream()
588 pStData2 = m_pFallbackStream;
589 aEscherBlipCache = m_aOldEscherBlipCache;
590 m_aOldEscherBlipCache.clear();
591 m_pFallbackStream = nullptr;
594 sal_uInt16 SwWW8ImplReader::GetToggleAttrFlags() const
596 return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
599 sal_uInt16 SwWW8ImplReader::GetToggleBiDiAttrFlags() const
601 return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
604 void SwWW8ImplReader::SetToggleAttrFlags(sal_uInt16 nFlags)
606 if (m_xCtrlStck)
607 m_xCtrlStck->SetToggleAttrFlags(nFlags);
610 void SwWW8ImplReader::SetToggleBiDiAttrFlags(sal_uInt16 nFlags)
612 if (m_xCtrlStck)
613 m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
616 rtl::Reference<SdrObject> SwMSDffManager::ProcessObj(SvStream& rSt,
617 DffObjData& rObjData,
618 SvxMSDffClientData& rData,
619 tools::Rectangle& rTextRect,
620 SdrObject* pObj1
623 rtl::Reference<SdrObject> pObj = pObj1;
624 if( !rTextRect.IsEmpty() )
626 SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
627 std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
629 // fill Import Record with data
630 pImpRec->nShapeId = rObjData.nShapeId;
631 pImpRec->eShapeType = rObjData.eShapeType;
633 rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt,
634 DFF_msofbtClientAnchor,
635 SEEK_FROM_CURRENT_AND_RESTART );
636 if( rObjData.bClientAnchor )
637 ProcessClientAnchor( rSt,
638 maShapeRecords.Current()->nRecLen,
639 pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
641 rObjData.bClientData = maShapeRecords.SeekToContent( rSt,
642 DFF_msofbtClientData,
643 SEEK_FROM_CURRENT_AND_RESTART );
644 if( rObjData.bClientData )
645 ProcessClientData( rSt,
646 maShapeRecords.Current()->nRecLen,
647 pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
649 pImpRec->nGroupShapeBooleanProperties = 0;
651 if( maShapeRecords.SeekToContent( rSt,
652 DFF_msofbtUDefProp,
653 SEEK_FROM_CURRENT_AND_RESTART )
654 && maShapeRecords.Current()->nRecLen )
656 sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
657 auto nAvailableBytes = rSt.remainingSize();
658 if (nBytesLeft > nAvailableBytes)
660 SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
661 nBytesLeft = nAvailableBytes;
663 while( 5 < nBytesLeft )
665 sal_uInt16 nPID(0);
666 rSt.ReadUInt16(nPID);
667 sal_uInt32 nUDData(0);
668 rSt.ReadUInt32(nUDData);
669 if (!rSt.good())
670 break;
671 switch (nPID)
673 case 0x038F: pImpRec->nXAlign = nUDData; break;
674 case 0x0390:
675 pImpRec->nXRelTo = nUDData;
676 break;
677 case 0x0391: pImpRec->nYAlign = nUDData; break;
678 case 0x0392:
679 pImpRec->nYRelTo = nUDData;
680 break;
681 case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
682 case 0x0393:
683 // This seems to correspond to o:hrpct from .docx (even including
684 // the difference that it's in 0.1% even though the .docx spec
685 // says it's in 1%).
686 pImpRec->relativeHorizontalWidth = nUDData;
687 break;
688 case 0x0394:
689 // And this is really just a guess, but a mere presence of this
690 // flag makes a horizontal rule be as wide as the page (unless
691 // overridden by something), so it probably matches o:hr from .docx.
692 pImpRec->isHorizontalRule = true;
693 break;
695 nBytesLeft -= 6;
699 // Text Frame also Title or Outline
700 sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
701 if( nTextId )
703 SfxItemSet aSet( pSdrModel->GetItemPool() );
705 // Originally anything that as a mso_sptTextBox was created as a
706 // textbox, this was changed to be created as a simple
707 // rect to keep impress happy. For the rest of us we'd like to turn
708 // it back into a textbox again.
709 bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox);
710 if (!bIsSimpleDrawingTextBox)
712 // Either
713 // a) it's a simple text object or
714 // b) it's a rectangle with text and square wrapping.
715 bIsSimpleDrawingTextBox =
717 (pImpRec->eShapeType == mso_sptTextSimple) ||
719 (pImpRec->eShapeType == mso_sptRectangle)
720 && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
725 // Distance of Textbox to its surrounding Autoshape
726 sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440);
727 sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 );
728 sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 );
729 sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 );
731 ScaleEmu( nTextLeft );
732 ScaleEmu( nTextRight );
733 ScaleEmu( nTextTop );
734 ScaleEmu( nTextBottom );
736 Degree100 nTextRotationAngle;
737 bool bVerticalText = false;
738 if ( IsProperty( DFF_Prop_txflTextFlow ) )
740 MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
741 DFF_Prop_txflTextFlow, 0) & 0xFFFF);
742 switch( eTextFlow )
744 case mso_txflBtoT:
745 nTextRotationAngle = 9000_deg100;
746 break;
747 case mso_txflVertN:
748 case mso_txflTtoBN:
749 nTextRotationAngle = 27000_deg100;
750 break;
751 case mso_txflTtoBA:
752 bVerticalText = true;
753 break;
754 case mso_txflHorzA:
755 bVerticalText = true;
756 nTextRotationAngle = 9000_deg100;
757 break;
758 case mso_txflHorzN:
759 default :
760 break;
764 if (nTextRotationAngle)
766 if (nTextRotationAngle == 9000_deg100)
768 tools::Long nWidth = rTextRect.GetWidth();
769 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
770 rTextRect.SetBottom( rTextRect.Top() + nWidth );
772 sal_Int32 nOldTextLeft = nTextLeft;
773 sal_Int32 nOldTextRight = nTextRight;
774 sal_Int32 nOldTextTop = nTextTop;
775 sal_Int32 nOldTextBottom = nTextBottom;
777 nTextLeft = nOldTextBottom;
778 nTextRight = nOldTextTop;
779 nTextTop = nOldTextLeft;
780 nTextBottom = nOldTextRight;
782 else if (nTextRotationAngle == 27000_deg100)
784 tools::Long nWidth = rTextRect.GetWidth();
785 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
786 rTextRect.SetBottom( rTextRect.Top() + nWidth );
788 sal_Int32 nOldTextLeft = nTextLeft;
789 sal_Int32 nOldTextRight = nTextRight;
790 sal_Int32 nOldTextTop = nTextTop;
791 sal_Int32 nOldTextBottom = nTextBottom;
793 nTextLeft = nOldTextTop;
794 nTextRight = nOldTextBottom;
795 nTextTop = nOldTextRight;
796 nTextBottom = nOldTextLeft;
800 if (bIsSimpleDrawingTextBox)
802 pObj = new SdrRectObj(
803 *pSdrModel,
804 SdrObjKind::Text,
805 rTextRect);
808 // The vertical paragraph justification are contained within the
809 // BoundRect so calculate it here
810 tools::Rectangle aNewRect(rTextRect);
811 aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
812 aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
814 // Only if it's a simple Textbox, Writer can replace the Object
815 // with a Frame, else
816 if( bIsSimpleDrawingTextBox )
818 std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
819 std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
821 SvxMSDffShapeInfos_ById::const_iterator const it =
822 GetShapeInfos()->find(xTmpRec);
823 if (it != GetShapeInfos()->end())
825 SvxMSDffShapeInfo& rInfo = **it;
826 pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
829 ApplyAttributes(rSt, aSet, rObjData);
832 if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2)
834 aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
835 aSet.Put( makeSdrTextMinFrameHeightItem(
836 aNewRect.Bottom() - aNewRect.Top() ) );
837 aSet.Put( makeSdrTextMinFrameWidthItem(
838 aNewRect.Right() - aNewRect.Left() ) );
840 else
842 aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
843 aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
846 switch ( static_cast<MSO_WrapMode>(GetPropertyValue( DFF_Prop_WrapText, mso_wrapSquare )) )
848 case mso_wrapNone :
849 aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
850 pImpRec->bAutoWidth = true;
851 break;
852 case mso_wrapByPoints :
853 aSet.Put( makeSdrTextContourFrameItem( true ) );
854 break;
855 default:
859 // Set distances on Textbox's margins
860 aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
861 aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
862 aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
863 aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
864 pImpRec->nDxTextLeft = nTextLeft;
865 pImpRec->nDyTextTop = nTextTop;
866 pImpRec->nDxTextRight = nTextRight;
867 pImpRec->nDyTextBottom = nTextBottom;
869 // Taking the correct default (which is mso_anchorTop)
870 sal_uInt32 eTextAnchor =
871 GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
873 SdrTextVertAdjust eTVA = bVerticalText
874 ? SDRTEXTVERTADJUST_BLOCK
875 : SDRTEXTVERTADJUST_CENTER;
876 SdrTextHorzAdjust eTHA = bVerticalText
877 ? SDRTEXTHORZADJUST_CENTER
878 : SDRTEXTHORZADJUST_BLOCK;
880 switch( eTextAnchor )
882 case mso_anchorTop:
883 case mso_anchorTopCentered:
885 if ( bVerticalText )
886 eTHA = SDRTEXTHORZADJUST_RIGHT;
887 else
888 eTVA = SDRTEXTVERTADJUST_TOP;
890 break;
891 case mso_anchorMiddle:
892 break;
893 case mso_anchorMiddleCentered:
894 break;
895 case mso_anchorBottom:
896 case mso_anchorBottomCentered:
898 if ( bVerticalText )
899 eTHA = SDRTEXTHORZADJUST_LEFT;
900 else
901 eTVA = SDRTEXTVERTADJUST_BOTTOM;
903 break;
904 default:
908 aSet.Put( SdrTextVertAdjustItem( eTVA ) );
909 aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
911 if (pObj != nullptr)
913 pObj->SetMergedItemSet(aSet);
915 if (bVerticalText)
917 SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
918 if (pTextObj)
919 pTextObj->SetVerticalWriting(true);
922 if ( bIsSimpleDrawingTextBox )
924 if ( nTextRotationAngle )
926 tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
927 rTextRect.GetWidth() : rTextRect.GetHeight();
928 nMinWH /= 2;
929 Point aPivot(rTextRect.TopLeft());
930 aPivot.AdjustX(nMinWH );
931 aPivot.AdjustY(nMinWH );
932 pObj->NbcRotate(aPivot, nTextRotationAngle);
936 if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
938 SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
939 if (pCustomShape)
941 double fExtraTextRotation = 0.0;
942 if ( mnFix16Angle && !( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 4 ) )
943 { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
944 fExtraTextRotation = -mnFix16Angle.get();
946 if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text
947 { // remains unchanged, so we have to take back the flipping here
948 fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped.
950 fExtraTextRotation += nTextRotationAngle.get();
951 if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
953 fExtraTextRotation /= 100.0;
954 SdrCustomShapeGeometryItem aGeometryItem( pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
955 css::beans::PropertyValue aPropVal;
956 aPropVal.Name = "TextRotateAngle";
957 aPropVal.Value <<= fExtraTextRotation;
958 aGeometryItem.SetPropertyValue( aPropVal );
959 pCustomShape->SetMergedItem( aGeometryItem );
963 else if ( mnFix16Angle )
965 // rotate text with shape ?
966 pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
970 else if( !pObj )
972 // simple rectangular objects are ignored by ImportObj() :-(
973 // this is OK for Draw but not for Calc and Writer
974 // cause here these objects have a default border
975 pObj = new SdrRectObj(
976 *pSdrModel,
977 rTextRect);
979 SfxItemSet aSet( pSdrModel->GetItemPool() );
980 ApplyAttributes( rSt, aSet, rObjData );
982 SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
983 if( SfxItemState::DEFAULT == eState )
984 aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
985 pObj->SetMergedItemSet(aSet);
988 // Means that fBehindDocument is set
989 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
990 pImpRec->bDrawHell = true;
991 else
992 pImpRec->bDrawHell = false;
993 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
994 pImpRec->bHidden = true;
995 pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
997 if ( nTextId )
999 pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
1000 pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
1003 pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
1004 o3tl::Length::emu, o3tl::Length::twip);
1005 pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
1006 o3tl::Length::emu, o3tl::Length::twip);
1007 pImpRec->nDxWrapDistRight
1008 = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistRight, 114935), o3tl::Length::emu,
1009 o3tl::Length::twip);
1010 pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
1011 o3tl::Length::emu, o3tl::Length::twip);
1012 // 16.16 fraction times total image width or height, as appropriate.
1014 if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt))
1016 pImpRec->pWrapPolygon.reset();
1018 sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0);
1019 rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
1020 bool bOk = false;
1021 if (nNumElemVert && (nElemSizeVert == 8 || nElemSizeVert == 4))
1023 //check if there is enough data in the file to make the
1024 //record sane
1025 // coverity[tainted_data : FALSE] - nElemSizeVert is either 8 or 4 so it has been sanitized
1026 bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert;
1028 if (bOk)
1030 pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
1031 for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
1033 sal_Int32 nX(0), nY(0);
1034 if (nElemSizeVert == 8)
1035 rSt.ReadInt32( nX ).ReadInt32( nY );
1036 else
1038 sal_Int16 nSmallX(0), nSmallY(0);
1039 rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
1040 nX = nSmallX;
1041 nY = nSmallY;
1043 (*(pImpRec->pWrapPolygon))[i].setX( nX );
1044 (*(pImpRec->pWrapPolygon))[i].setY( nY );
1049 pImpRec->nCropFromTop = GetPropertyValue(
1050 DFF_Prop_cropFromTop, 0 );
1051 pImpRec->nCropFromBottom = GetPropertyValue(
1052 DFF_Prop_cropFromBottom, 0 );
1053 pImpRec->nCropFromLeft = GetPropertyValue(
1054 DFF_Prop_cropFromLeft, 0 );
1055 pImpRec->nCropFromRight = GetPropertyValue(
1056 DFF_Prop_cropFromRight, 0 );
1058 sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
1060 if ( !IsHardAttribute( DFF_Prop_fLine ) &&
1061 pImpRec->eShapeType == mso_sptPictureFrame )
1063 nLineFlags &= ~0x08;
1066 pImpRec->eLineStyle = (nLineFlags & 8)
1067 ? static_cast<MSO_LineStyle>(GetPropertyValue(
1068 DFF_Prop_lineStyle,
1069 mso_lineSimple ))
1070 : MSO_LineStyle(USHRT_MAX);
1071 pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
1072 DFF_Prop_lineDashing, mso_lineSolid ));
1074 pImpRec->nFlags = rObjData.nSpFlags;
1076 if( pImpRec->nShapeId )
1078 auto nShapeId = pImpRec->nShapeId;
1079 auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16)
1080 + pImpRec->aTextId.nSequence;
1081 // Complement Import Record List
1082 pImpRec->pObj = pObj;
1083 rImportData.insert(std::move(pImpRec));
1085 // Complement entry in Z Order List with a pointer to this Object
1086 // Only store objects which are not deep inside the tree
1087 if( ( rObjData.nCalledByGroup == 0 )
1089 ( (rObjData.nSpFlags & ShapeFlag::Group)
1090 && (rObjData.nCalledByGroup < 2) )
1093 StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
1096 else
1097 pImpRec.reset();
1100 sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
1101 if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
1103 SvMemoryStream aMemStream;
1104 struct HyperLinksTable hlStr;
1105 aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
1107 // copy from DFF stream to memory stream
1108 std::vector< sal_uInt8 > aBuffer( nBufferSize );
1109 if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
1111 aMemStream.WriteBytes(aBuffer.data(), nBufferSize);
1112 sal_uInt64 nStreamSize = aMemStream.TellEnd();
1113 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
1114 bool bRet = 4 <= nStreamSize;
1115 sal_uInt16 nRawRecId,nRawRecSize;
1116 if( bRet )
1117 aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize );
1118 SwDocShell* pDocShell = m_rReader.m_pDocShell;
1119 if (pDocShell)
1121 m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
1125 if (pObj && !hlStr.hLinkAddr.isEmpty())
1127 SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
1128 if( pInfo )
1130 pInfo->SetShapeId( rObjData.nShapeId );
1131 pInfo->SetHlink( hlStr.hLinkAddr );
1132 if (!hlStr.tarFrame.isEmpty())
1133 pInfo->SetTarFrame( hlStr.tarFrame );
1134 OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt );
1135 if (!aNameStr.isEmpty())
1136 pInfo->SetName( aNameStr );
1141 return pObj;
1145 * Special FastSave - Attributes
1147 void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
1149 if (nLen < 0)
1151 m_bCpxStyle = false;
1152 return;
1154 sal_uInt16 nColl = 0;
1155 if (m_xWwFib->GetFIBVersion() <= ww::eWW2)
1156 nColl = *pData;
1157 else
1158 nColl = SVBT16ToUInt16(pData);
1159 if (nColl < m_vColl.size())
1161 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[nColl] );
1162 m_bCpxStyle = true;
1167 * Read_Majority is for Majority (103) and Majority50 (108)
1169 void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
1174 * Stack
1176 void SwWW8FltControlStack::NewAttr(const SwPosition& rPos,
1177 const SfxPoolItem& rAttr)
1179 OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put"
1180 "fields into the control stack");
1181 OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put"
1182 "input fields into the control stack");
1183 OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put"
1184 "annotations into the control stack");
1185 OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put"
1186 "redlines into the control stack");
1187 SwFltControlStack::NewAttr(rPos, rAttr);
1190 SwFltStackEntry* SwWW8FltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId,
1191 bool bTstEnd, tools::Long nHand, bool )
1193 SwFltStackEntry *pRet = nullptr;
1194 // Doing a textbox, and using the control stack only as a temporary
1195 // collection point for properties which will are not to be set into
1196 // the real document
1197 if (m_rReader.m_xPlcxMan && m_rReader.m_xPlcxMan->GetDoingDrawTextBox())
1199 size_t nCnt = size();
1200 size_t i = 0;
1201 while (i < nCnt)
1203 SwFltStackEntry& rEntry = (*this)[i];
1204 if (nAttrId == rEntry.m_pAttr->Which())
1206 DeleteAndDestroy(i);
1207 --nCnt;
1208 break;
1210 ++i;
1213 else // Normal case, set the attribute into the document
1214 pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
1215 return pRet;
1218 tools::Long GetListFirstLineIndent(const SwNumFormat &rFormat)
1220 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1221 "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1223 SvxAdjust eAdj = rFormat.GetNumAdjust();
1224 tools::Long nReverseListIndented;
1225 if (eAdj == SvxAdjust::Right)
1226 nReverseListIndented = -rFormat.GetCharTextDistance();
1227 else if (eAdj == SvxAdjust::Center)
1228 nReverseListIndented = rFormat.GetFirstLineOffset()/2;
1229 else
1230 nReverseListIndented = rFormat.GetFirstLineOffset();
1231 return nReverseListIndented;
1234 static tools::Long lcl_GetTrueMargin(SvxFirstLineIndentItem const& rFirstLine,
1235 SvxTextLeftMarginItem const& rLeftMargin, const SwNumFormat &rFormat,
1236 tools::Long &rFirstLinePos)
1238 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1239 "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1241 const tools::Long nBodyIndent = rLeftMargin.ResolveTextLeft({});
1242 const tools::Long nFirstLineDiff = rFirstLine.ResolveTextFirstLineOffset({});
1243 rFirstLinePos = nBodyIndent + nFirstLineDiff;
1245 const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
1246 const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
1247 tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
1249 return std::max<tools::Long>(nExtraListIndent, 0);
1252 // #i103711#
1253 // #i105414#
1254 void SyncIndentWithList( SvxFirstLineIndentItem & rFirstLine,
1255 SvxTextLeftMarginItem & rLeftMargin,
1256 const SwNumFormat &rFormat,
1257 const bool bFirstLineOfstSet,
1258 const bool bLeftIndentSet )
1260 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
1262 tools::Long nWantedFirstLinePos;
1263 tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, rLeftMargin, rFormat, nWantedFirstLinePos);
1264 rLeftMargin.SetTextLeft(SvxIndentValue::twips(nWantedFirstLinePos - nExtraListIndent));
1265 rFirstLine.SetTextFirstLineOffset(SvxIndentValue::zero());
1267 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
1269 if ( !bFirstLineOfstSet && bLeftIndentSet &&
1270 rFormat.GetFirstLineIndent() != 0 )
1272 rFirstLine.SetTextFirstLineOffset(
1273 SvxIndentValue{ static_cast<double>(rFormat.GetFirstLineIndent()),
1274 rFormat.GetFirstLineIndentUnit() });
1276 else if ( bFirstLineOfstSet && !bLeftIndentSet &&
1277 rFormat.GetIndentAt() != 0 )
1279 rLeftMargin.SetTextLeft(SvxIndentValue::twips(rFormat.GetIndentAt()));
1281 else if (!bFirstLineOfstSet && !bLeftIndentSet )
1283 if ( rFormat.GetFirstLineIndent() != 0 )
1285 rFirstLine.SetTextFirstLineOffset(
1286 SvxIndentValue{ static_cast<double>(rFormat.GetFirstLineIndent()),
1287 rFormat.GetFirstLineIndentUnit() });
1289 if ( rFormat.GetIndentAt() != 0 )
1291 rLeftMargin.SetTextLeft(SvxIndentValue::twips(rFormat.GetIndentAt()));
1297 const SwNumFormat* SwWW8FltControlStack::GetNumFormatFromStack(const SwPosition &rPos,
1298 const SwTextNode &rTextNode)
1300 const SwNumFormat *pRet = nullptr;
1301 const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
1302 if (pItem && rTextNode.GetNumRule())
1304 if (rTextNode.IsCountedInList())
1306 OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue());
1307 const SwNumRule *pRule = m_rDoc.FindNumRulePtr(sName);
1308 if (pRule)
1309 pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel());
1312 return pRet;
1315 void SwWW8ReferencedFltEndStack::SetAttrInDoc( const SwPosition& rTmpPos,
1316 SwFltStackEntry& rEntry )
1318 switch( rEntry.m_pAttr->Which() )
1320 case RES_FLTR_BOOKMARK:
1322 // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content
1323 // and which is not referenced.
1324 bool bInsertBookmarkIntoDoc = true;
1326 SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
1327 if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
1329 const OUString& rName = pFltBookmark->GetName();
1330 std::set< OUString, SwWW8::ltstr >::const_iterator aResult = m_aReferencedTOCBookmarks.find(rName);
1331 if ( aResult == m_aReferencedTOCBookmarks.end() )
1333 bInsertBookmarkIntoDoc = false;
1336 if ( bInsertBookmarkIntoDoc )
1338 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1340 break;
1342 default:
1343 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1344 break;
1349 void SwWW8FltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
1350 SwFltStackEntry& rEntry)
1352 switch (rEntry.m_pAttr->Which())
1354 case RES_LR_SPACE:
1355 assert(false);
1356 break;
1357 case RES_MARGIN_FIRSTLINE:
1358 case RES_MARGIN_TEXTLEFT:
1361 Loop over the affected nodes and
1362 a) convert the word style absolute indent to indent relative
1363 to any numbering indent active on the nodes
1364 b) adjust the writer style tabstops relative to the old
1365 paragraph indent to be relative to the new paragraph indent
1367 SwPaM aRegion(rTmpPos);
1368 if (rEntry.MakeRegion(aRegion, SwFltStackEntry::RegionMode::NoCheck))
1370 SvxFirstLineIndentItem firstLineNew(RES_MARGIN_FIRSTLINE);
1371 SvxTextLeftMarginItem leftMarginNew(RES_MARGIN_TEXTLEFT);
1372 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1374 SvxFirstLineIndentItem const firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
1375 firstLineNew.SetTextFirstLineOffset(
1376 firstLineEntry.GetTextFirstLineOffset(),
1377 firstLineEntry.GetPropTextFirstLineOffset());
1378 firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
1380 else
1382 SvxTextLeftMarginItem const leftMarginEntry(*static_cast<SvxTextLeftMarginItem*>(rEntry.m_pAttr.get()));
1383 leftMarginNew.SetTextLeft(leftMarginEntry.GetTextLeft(), leftMarginEntry.GetPropLeft());
1385 SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
1386 SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
1387 for(; nStart <= nEnd; ++nStart)
1389 SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
1390 if (!pNode || !pNode->IsTextNode())
1391 continue;
1393 SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
1394 SvxFirstLineIndentItem firstLineOld(pNd->GetAttr(RES_MARGIN_FIRSTLINE));
1395 SvxTextLeftMarginItem leftMarginOld(pNd->GetAttr(RES_MARGIN_TEXTLEFT));
1396 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1398 leftMarginNew.SetTextLeft(leftMarginOld.GetTextLeft(), leftMarginOld.GetPropLeft());
1400 else
1402 firstLineNew.SetTextFirstLineOffset(
1403 firstLineOld.GetTextFirstLineOffset(),
1404 firstLineOld.GetPropTextFirstLineOffset());
1405 firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
1408 SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
1410 const SwNumFormat* pNum
1411 = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
1412 if (!pNum)
1414 pNum = GetNumFormatFromTextNode(*pTextNode);
1417 if ( pNum )
1419 // #i103711#
1420 const bool bFirstLineIndentSet =
1421 ( m_rReader.m_aTextNodesHavingFirstLineOfstSet.end() !=
1422 m_rReader.m_aTextNodesHavingFirstLineOfstSet.find( pNode ) );
1423 // #i105414#
1424 const bool bLeftIndentSet =
1425 ( m_rReader.m_aTextNodesHavingLeftIndentSet.end() !=
1426 m_rReader.m_aTextNodesHavingLeftIndentSet.find( pNode ) );
1427 SyncIndentWithList(firstLineNew, leftMarginNew, *pNum,
1428 bFirstLineIndentSet,
1429 bLeftIndentSet );
1432 if (firstLineNew != firstLineOld)
1434 if (nStart == aRegion.Start()->GetNodeIndex())
1436 pNd->SetAttr(firstLineNew);
1439 if (leftMarginNew != leftMarginOld)
1441 pNd->SetAttr(leftMarginNew);
1446 break;
1448 case RES_TXTATR_FIELD:
1449 OSL_ENSURE(false, "What is a field doing in the control stack,"
1450 "probably should have been in the endstack");
1451 break;
1453 case RES_TXTATR_ANNOTATION:
1454 OSL_ENSURE(false, "What is an annotation doing in the control stack,"
1455 "probably should have been in the endstack");
1456 break;
1458 case RES_TXTATR_INPUTFIELD:
1459 OSL_ENSURE(false, "What is an input field doing in the control stack,"
1460 "probably should have been in the endstack");
1461 break;
1463 case RES_TXTATR_INETFMT:
1465 SwPaM aRegion(rTmpPos);
1466 if (rEntry.MakeRegion(aRegion, SwFltStackEntry::RegionMode::NoCheck))
1468 SwFrameFormat *pFrame;
1469 // If we have just one single inline graphic then
1470 // don't insert a field for the single frame, set
1471 // the frames hyperlink field attribute directly.
1472 pFrame = SwWW8ImplReader::ContainsSingleInlineGraphic(aRegion);
1473 if (nullptr != pFrame)
1475 const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
1476 rEntry.m_pAttr.get());
1477 SwFormatURL aURL;
1478 aURL.SetURL(pAttr->GetValue(), false);
1479 aURL.SetTargetFrameName(pAttr->GetTargetFrame());
1480 pFrame->SetFormatAttr(aURL);
1482 else
1484 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
1488 break;
1489 default:
1490 SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
1491 break;
1495 const SfxPoolItem* SwWW8FltControlStack::GetFormatAttr(const SwPosition& rPos,
1496 sal_uInt16 nWhich)
1498 const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
1499 if (!pItem)
1501 SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
1502 if (!pNd)
1503 pItem = &m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(nWhich);
1504 else
1507 If we're hunting for the indent on a paragraph and need to use the
1508 parent style indent, then return the indent in msword format, and
1509 not writer format, because that's the style that the filter works
1510 in (naturally)
1512 if (nWhich == RES_MARGIN_FIRSTLINE
1513 || nWhich == RES_MARGIN_TEXTLEFT
1514 || nWhich == RES_MARGIN_RIGHT)
1516 SfxItemState eState = SfxItemState::DEFAULT;
1517 if (const SfxItemSet *pSet = pNd->GetpSwAttrSet())
1518 eState = pSet->GetItemState(nWhich, false);
1519 if (eState != SfxItemState::SET && m_rReader.m_nCurrentColl < m_rReader.m_vColl.size())
1521 switch (nWhich)
1523 case RES_MARGIN_FIRSTLINE:
1524 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordFirstLine.get();
1525 break;
1526 case RES_MARGIN_TEXTLEFT:
1527 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordLeftMargin.get();
1528 break;
1529 case RES_MARGIN_RIGHT:
1530 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordRightMargin.get();
1531 break;
1537 If we're hunting for a character property, try and exact position
1538 within the text node for lookup
1540 if (pNd->IsTextNode())
1542 const sal_Int32 nPos = rPos.GetContentIndex();
1543 m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
1544 if (pNd->GetTextNode()->GetParaAttr(*m_xScratchSet, nPos, nPos))
1545 pItem = m_xScratchSet->GetItem(nWhich);
1548 if (!pItem)
1549 pItem = &pNd->GetAttr(nWhich);
1552 return pItem;
1555 const SfxPoolItem* SwWW8FltControlStack::GetStackAttr(const SwPosition& rPos,
1556 sal_uInt16 nWhich)
1558 SwFltPosition aFltPos(rPos);
1560 size_t nSize = size();
1561 while (nSize)
1563 const SwFltStackEntry& rEntry = (*this)[ --nSize ];
1564 if (rEntry.m_pAttr->Which() == nWhich)
1566 if ( (rEntry.m_bOpen) ||
1568 (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) &&
1569 (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) &&
1570 (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) &&
1571 (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent)
1575 * e.g. half-open range [0-3) so asking for properties at 3
1576 * means props that end at 3 are not included
1579 return rEntry.m_pAttr.get();
1583 return nullptr;
1586 bool SwWW8FltRefStack::IsFootnoteEdnBkmField(
1587 const SwFormatField& rFormatField,
1588 sal_uInt16& rBkmNo)
1590 const SwField* pField = rFormatField.GetField();
1591 sal_uInt16 nSubType;
1592 if(pField && (SwFieldIds::GetRef == pField->Which())
1593 && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType))
1594 && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty())
1596 const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
1597 auto ppBkmk =
1598 pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
1599 if(ppBkmk != pMarkAccess->getAllMarksEnd())
1601 // find Sequence No of corresponding Foot-/Endnote
1602 rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
1603 return true;
1606 return false;
1609 void SwWW8FltRefStack::SetAttrInDoc(const SwPosition& rTmpPos,
1610 SwFltStackEntry& rEntry)
1612 switch (rEntry.m_pAttr->Which())
1615 Look up these in our lists of bookmarks that were changed to
1616 variables, and replace the ref field with a var field, otherwise
1617 do normal (?) strange stuff
1619 case RES_TXTATR_FIELD:
1620 case RES_TXTATR_ANNOTATION:
1621 case RES_TXTATR_INPUTFIELD:
1623 SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
1625 SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
1626 SwField* pField = rFormatField.GetField();
1628 if (!RefToVar(pField, rEntry))
1630 sal_uInt16 nBkmNo;
1631 if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
1633 ::sw::mark::MarkBase const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
1635 const SwPosition& rBkMrkPos = pMark->GetMarkPos();
1637 SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
1638 if( pText && rBkMrkPos.GetContentIndex() )
1640 SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
1641 rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
1642 if( pFootnote )
1644 sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
1646 static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
1648 if( pFootnote->GetFootnote().IsEndNote() )
1649 static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
1655 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aPaM, *rEntry.m_pAttr);
1656 MoveAttrs(*aPaM.GetPoint());
1658 break;
1659 case RES_FLTR_TOX:
1660 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1661 break;
1662 default:
1663 case RES_FLTR_BOOKMARK:
1664 OSL_ENSURE(false, "EndStck used with non field, not what we want");
1665 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1666 break;
1671 For styles we will do our tabstop arithmetic in word style and adjust them to
1672 writer style after all the styles have been finished and the dust settles as
1673 to what affects what.
1675 For explicit attributes we turn the adjusted writer tabstops back into 0 based
1676 word indexes and we'll turn them back into writer indexes when setting them
1677 into the document. If explicit left indent exist which affects them, then this
1678 is handled when the explicit left indent is set into the document
1680 void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
1682 if (nLen < 0)
1684 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_TABSTOP);
1685 return;
1688 sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
1689 const sal_uInt8* pDel = pData + 1; // Del - Array
1691 sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
1692 const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
1694 short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
1695 if (nRequiredLength > nLen)
1697 // would require more data than available to describe!
1698 // discard invalid record
1699 nIns = 0;
1700 nDel = 0;
1703 WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
1705 std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
1707 const SwFormat * pSty = nullptr;
1708 sal_uInt16 nTabBase;
1709 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
1711 nTabBase = m_vColl[m_nCurrentColl].m_nBase;
1712 if (nTabBase < m_vColl.size()) // Based On
1713 pSty = m_vColl[nTabBase].m_pFormat;
1715 else
1716 { // Text
1717 nTabBase = m_nCurrentColl;
1718 if (m_nCurrentColl < m_vColl.size())
1719 pSty = m_vColl[m_nCurrentColl].m_pFormat;
1720 //TODO: figure out else here
1723 bool bFound = false;
1724 std::unordered_set<size_t> aLoopWatch;
1725 while (pSty && !bFound)
1727 const SvxTabStopItem* pTabs;
1728 bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
1729 &pTabs) == SfxItemState::SET;
1730 if( bFound )
1732 aAttr.reset(pTabs->Clone());
1734 else
1736 sal_uInt16 nOldTabBase = nTabBase;
1737 // If based on another
1738 if (nTabBase < m_vColl.size())
1739 nTabBase = m_vColl[nTabBase].m_nBase;
1741 if (
1742 nTabBase < m_vColl.size() &&
1743 nOldTabBase != nTabBase &&
1744 nTabBase != ww::stiNil
1747 // #i61789: Stop searching when next style is the same as the
1748 // current one (prevent loop)
1749 aLoopWatch.insert(reinterpret_cast<size_t>(pSty));
1750 if (nTabBase < m_vColl.size())
1751 pSty = m_vColl[nTabBase].m_pFormat;
1752 //TODO figure out the else branch
1754 if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
1755 aLoopWatch.end())
1756 pSty = nullptr;
1758 else
1759 pSty = nullptr; // Give up on the search
1763 SvxTabStop aTabStop;
1764 for (short i=0; i < nDel; ++i)
1766 sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
1767 if( nPos != SVX_TAB_NOTFOUND )
1768 aAttr->Remove( nPos );
1771 for (short i=0; i < nIns; ++i)
1773 short nPos = SVBT16ToUInt16(pIns + i*2);
1774 aTabStop.GetTabPos() = nPos;
1775 switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
1777 case 0:
1778 aTabStop.GetAdjustment() = SvxTabAdjust::Left;
1779 break;
1780 case 1:
1781 aTabStop.GetAdjustment() = SvxTabAdjust::Center;
1782 break;
1783 case 2:
1784 aTabStop.GetAdjustment() = SvxTabAdjust::Right;
1785 break;
1786 case 3:
1787 aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
1788 break;
1789 case 4:
1790 continue; // Ignore Bar
1793 switch( pTyp[i].aBits1 >> 3 & 0x7 )
1795 case 0:
1796 aTabStop.GetFill() = ' ';
1797 break;
1798 case 1:
1799 aTabStop.GetFill() = '.';
1800 break;
1801 case 2:
1802 aTabStop.GetFill() = '-';
1803 break;
1804 case 3:
1805 case 4:
1806 aTabStop.GetFill() = '_';
1807 break;
1810 sal_uInt16 nPos2 = aAttr->GetPos( nPos );
1811 if (nPos2 != SVX_TAB_NOTFOUND)
1812 aAttr->Remove(nPos2); // Or else Insert() refuses
1813 aAttr->Insert(aTabStop);
1816 if (nIns || nDel)
1817 NewAttr(*aAttr);
1818 else
1820 // Here we have a tab definition which inserts no extra tabs, or deletes
1821 // no existing tabs. An older version of writer is probably the creator
1822 // of the document :-( . So if we are importing a style we can just
1823 // ignore it. But if we are importing into text we cannot as during
1824 // text SwWW8ImplReader::Read_Tab is called at the begin and end of
1825 // the range the attrib affects, and ignoring it would upset the
1826 // balance
1827 if (!m_pCurrentColl) // not importing into a style
1829 SvxTabStopItem aOrig = pSty ?
1830 pSty->GetFormatAttr(RES_PARATR_TABSTOP) :
1831 m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(RES_PARATR_TABSTOP);
1832 NewAttr(aOrig);
1838 * DOP
1840 void SwWW8ImplReader::ImportDop()
1842 // correct the LastPrinted date in DocumentProperties
1843 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1844 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1845 uno::Reference<document::XDocumentProperties> xDocuProps(
1846 xDPS->getDocumentProperties());
1847 OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null");
1848 if (xDocuProps.is())
1850 DateTime aLastPrinted(
1851 msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
1852 ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
1853 xDocuProps->setPrintDate(uDT);
1856 // COMPATIBILITY FLAGS START
1858 // #i78951# - remember the unknown compatibility options
1859 // so as to export them out
1860 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions1(m_xWDop->GetCompatibilityOptions());
1861 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions2(m_xWDop->GetCompatibilityOptions2());
1863 // The distance between two paragraphs is the sum of the bottom distance of
1864 // the first paragraph and the top distance of the second one
1865 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, m_xWDop->fDontUseHTMLAutoSpacing);
1866 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, true );
1867 // move tabs on alignment
1868 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, true);
1869 // #i24363# tab stops relative to indent
1870 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, false);
1871 // tdf#117923
1872 m_rDoc.getIDocumentSettingAccess().set(
1873 DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true);
1874 m_rDoc.getIDocumentSettingAccess().set(
1875 DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true);
1876 // tdf#128195
1877 m_rDoc.getIDocumentSettingAccess().set(
1878 DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true);
1879 m_rDoc.getIDocumentSettingAccess().set(
1880 DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA, true);
1881 m_rDoc.getIDocumentSettingAccess().set(
1882 DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND, true);
1883 m_rDoc.getIDocumentSettingAccess().set(
1884 DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, false);
1885 // tdf#155229 calculate minimum row height including horizontal border width
1886 m_rDoc.getIDocumentSettingAccess().set(
1887 DocumentSettingId::MIN_ROW_HEIGHT_INCL_BORDER, true);
1888 // tdf#129808 use Word-compatible CJK text grid metrics
1889 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::MS_WORD_COMP_GRID_METRICS, true);
1891 // Import Default Tabs
1892 tools::Long nDefTabSiz = m_xWDop->dxaTab;
1893 if( nDefTabSiz < 56 )
1894 nDefTabSiz = 709;
1896 // We want exactly one DefaultTab
1897 SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP );
1898 const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default;
1900 m_rDoc.GetAttrPool().SetUserDefaultItem( aNewTab );
1902 // Import zoom factor
1903 if (m_xWDop->wScaleSaved)
1905 //Import zoom type
1906 sal_Int16 nZoomType;
1907 switch (m_xWDop->zkSaved) {
1908 case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break;
1909 case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break;
1910 case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break;
1911 default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break;
1913 uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({
1914 { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) },
1915 { "VisibleBottom", uno::Any(sal_Int32(0)) },
1916 { "ZoomType", uno::Any(nZoomType) }
1917 }));
1919 rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
1920 xBox->insertByIndex(sal_Int32(0), uno::Any(aViewProps));
1921 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
1922 xViewDataSupplier->setViewData(xBox);
1925 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_VIRTUAL_DEVICE, !m_xWDop->fUsePrinterMetrics);
1926 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, true);
1927 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, true );
1929 // No vertical offsets would lead to e.g. overlap of table and fly frames.
1930 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS, true );
1932 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, !m_xWDop->fNoLeading);
1933 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, false);
1934 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, false); // #i47448#
1935 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER, true); // tdf#159382
1936 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, !m_xWDop->fExpShRtn); // #i49277#, #i56856#
1937 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, false); // #i53199#
1938 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, false);
1940 // #i25901# - set new compatibility option
1941 // 'Add paragraph and table spacing at bottom of table cells'
1942 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, true);
1943 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, true);
1945 // #i11860# - set new compatibility option
1946 // 'Use former object positioning' to <false>
1947 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, false);
1949 // #i27767# - set new compatibility option
1950 // 'Consider Wrapping mode when positioning object' to <true>
1951 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, true);
1953 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, false); // #i13832#, #i24135#
1955 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
1957 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, true); // #i3952#
1959 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, true);
1960 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, true);
1961 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, true);
1962 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, true);
1963 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
1964 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true);
1965 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true);
1966 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
1967 // rely on default for HYPHENATE_URLS=false
1968 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH, true);
1969 // rely on default for IGNORE_HIDDEN_CHARS_FOR_LINE_CALCULATION=true
1971 IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
1972 if (m_xWDop->fDontBreakWrappedTables)
1974 rIDSA.set(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES, true);
1977 // COMPATIBILITY FLAGS END
1979 // Import magic doptypography information, if it's there
1980 if (m_xWwFib->m_nFib > 105)
1981 ImportDopTypography(m_xWDop->doptypography);
1983 // disable form design mode to be able to use imported controls directly
1984 // #i31239# always disable form design mode, not only in protected docs
1985 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1986 if (xDocProps.is())
1988 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1989 if (xInfo.is())
1991 if (xInfo->hasPropertyByName(u"ApplyFormDesignMode"_ustr))
1992 xDocProps->setPropertyValue(u"ApplyFormDesignMode"_ustr, css::uno::Any(false));
1995 // for the benefit of DOCX - if this is ever saved in that format.
1996 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(u"InteropGrabBag"_ustr));
1997 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1998 { "name", uno::Any(u"compatibilityMode"_ustr) },
1999 { "uri", uno::Any(u"http://schemas.microsoft.com/office/word"_ustr) },
2000 { "val", uno::Any(u"11"_ustr) } //11: Use features specified in MS-DOC.
2001 }));
2003 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
2004 { "compatSetting", uno::Any(aCompatSetting) }
2005 }));
2007 aGrabBag[u"CompatSettings"_ustr] <<= aValue;
2008 xDocProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag.getAsConstPropertyValueList()));
2011 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
2012 // Treat comments-only like read-only since Writer has no support for that.
2013 // Still allow editing of form fields, without requiring the password.
2014 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
2015 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
2016 m_pDocShell->SetModifyPasswordHash(m_xWDop->lKeyProtDoc);
2017 else if ( xDocProps.is() )
2019 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(u"InteropGrabBag"_ustr));
2020 aGrabBag[u"FormPasswordHash"_ustr] <<= m_xWDop->lKeyProtDoc;
2021 xDocProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag.getAsConstPropertyValueList()));
2024 if (officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get())
2025 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, m_xWDop->fProtEnabled );
2027 if (m_xWDop->iGutterPos)
2029 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::GUTTER_AT_TOP, true);
2033 void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo)
2035 switch (rTypo.m_iLevelOfKinsoku)
2037 case 2: // custom
2039 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
2040 OUString(+rTypo.m_rgxchLPunct));
2041 // unary + makes sure not to accidentally call the deleted
2042 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
2043 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
2044 // prefix leading up to the first NUL
2045 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(rTypo.GetConvertedLang(),
2046 aForbidden);
2047 // Obviously cannot set the standard level 1 for japanese, so
2048 // bail out now while we can.
2049 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
2050 return;
2052 break;
2053 default:
2054 break;
2058 This MS hack means that level 2 of japanese is not in operation, so we put
2059 in what we know are the MS defaults, there is a complementary reverse
2060 hack in the writer. Its our default as well, but we can set it anyway
2061 as a flag for later.
2063 if (!rTypo.m_reserved2)
2065 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
2066 WW8DopTypography::JapanNotEndLevel1);
2067 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(LANGUAGE_JAPANESE,aForbidden);
2070 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bool(rTypo.m_fKerningPunct));
2071 m_rDoc.getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(rTypo.m_iJustification));
2075 * Footnotes and Endnotes
2077 WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) :
2078 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
2079 mxOldStck(std::move(pRdr->m_xCtrlStck)),
2080 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2081 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2082 mxOldPlcxMan(pRdr->m_xPlcxMan),
2083 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2084 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2085 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2086 mpPrevNumRule(pRdr->m_pPrevNumRule),
2087 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2088 mnInTable(pRdr->m_nInTable),
2089 mnCurrentColl(pRdr->m_nCurrentColl),
2090 mcSymbol(pRdr->m_cSymbol),
2091 mbIgnoreText(pRdr->m_bIgnoreText),
2092 mbSymbol(pRdr->m_bSymbol),
2093 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2094 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2095 mbAnl(pRdr->m_bAnl),
2096 mbInHyperlink(pRdr->m_bInHyperlink),
2097 mbPgSecBreak(pRdr->m_bPgSecBreak),
2098 mbWasParaEnd(pRdr->m_bWasParaEnd),
2099 mbHasBorder(pRdr->m_bHasBorder),
2100 mbFirstPara(pRdr->m_bFirstPara)
2102 pRdr->m_bSymbol = false;
2103 pRdr->m_bHdFtFootnoteEdn = true;
2104 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2105 = pRdr->m_bHasBorder = false;
2106 pRdr->m_bFirstPara = true;
2107 pRdr->m_nInTable = 0;
2108 pRdr->m_pPreviousNumPaM = nullptr;
2109 pRdr->m_pPrevNumRule = nullptr;
2110 pRdr->m_nCurrentColl = 0;
2112 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2113 *pRdr));
2115 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2117 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2119 // Save the attribute manager: we need this as the newly created PLCFx Manager
2120 // access the same FKPs as the old one and their Start-End position changes.
2121 if (pRdr->m_xPlcxMan)
2122 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2124 if (nStartCp != -1)
2126 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2127 mxOldPlcxMan->GetManType(), nStartCp);
2130 maOldApos.push_back(false);
2131 maOldApos.swap(pRdr->m_aApos);
2132 maOldFieldStack.swap(pRdr->m_aFieldStack);
2135 void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr )
2137 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2138 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2139 pRdr->m_pPreviousNumPaM = mpPreviousNumPaM;
2140 pRdr->m_pPrevNumRule = mpPrevNumRule;
2141 pRdr->m_xTableDesc = std::move(mxTableDesc);
2142 pRdr->m_cSymbol = mcSymbol;
2143 pRdr->m_bSymbol = mbSymbol;
2144 pRdr->m_bIgnoreText = mbIgnoreText;
2145 pRdr->m_bHdFtFootnoteEdn = mbHdFtFootnoteEdn;
2146 pRdr->m_bTxbxFlySection = mbTxbxFlySection;
2147 pRdr->m_nInTable = mnInTable;
2148 pRdr->m_bAnl = mbAnl;
2149 pRdr->m_bInHyperlink = mbInHyperlink;
2150 pRdr->m_bWasParaEnd = mbWasParaEnd;
2151 pRdr->m_bPgSecBreak = mbPgSecBreak;
2152 pRdr->m_nCurrentColl = mnCurrentColl;
2153 pRdr->m_bHasBorder = mbHasBorder;
2154 pRdr->m_bFirstPara = mbFirstPara;
2156 // Close all attributes as attributes could be created that extend the Fly
2157 pRdr->DeleteCtrlStack();
2158 pRdr->m_xCtrlStck = std::move(mxOldStck);
2160 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2162 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2163 // place, or somewhere close if that place got destroyed
2164 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2165 pRdr->m_oLastAnchorPos.reset();
2167 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2169 if (xLastAnchorCursor)
2170 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2172 pRdr->DeleteAnchorStack();
2173 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2175 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2177 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2178 pRdr->m_xPlcxMan = mxOldPlcxMan;
2179 if (pRdr->m_xPlcxMan)
2180 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2181 pRdr->m_aApos.swap(maOldApos);
2182 pRdr->m_aFieldStack.swap(maOldFieldStack);
2185 void SwWW8ImplReader::Read_HdFtFootnoteText( const SwNodeIndex* pSttIdx,
2186 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2188 if (nStartCp < 0 || nLen < 0)
2189 return;
2191 // Saves Flags (amongst other things) and resets them
2192 WW8ReaderSave aSave( this );
2194 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2196 // Read Text for Header, Footer or Footnote
2197 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2198 aSave.Restore( this );
2202 * Use authornames, if not available fall back to initials.
2204 tools::Long SwWW8ImplReader::Read_And(WW8PLCFManResult* pRes)
2206 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2207 if (!pSD)
2208 return 0;
2210 const void* pData = pSD->GetData();
2211 if (!pData)
2212 return 0;
2214 OUString sAuthor;
2215 OUString sInitials;
2216 if( m_bVer67 )
2218 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2219 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2220 if (pA)
2221 sAuthor = *pA;
2222 else
2224 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2225 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2226 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2229 else
2231 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2233 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2234 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2235 OUStringBuffer aBuf;
2236 aBuf.setLength(nLen);
2237 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2238 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2239 sInitials = aBuf.makeStringAndClear();
2242 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2243 sAuthor = *pA;
2244 else
2245 sAuthor = sInitials;
2248 sal_uInt32 nDateTime = 0;
2250 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2252 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2253 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2254 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2257 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2259 OUString sText;
2260 std::optional<OutlinerParaObject> pOutliner = ImportAsOutliner( sText, pRes->nCp2OrIdx,
2261 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2263 m_xFormatOfJustInsertedApo.reset();
2264 SwPostItField aPostIt(
2265 static_cast<SwPostItFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), sAuthor,
2266 sText, sInitials, OUString(), aDate );
2267 aPostIt.SetTextObject(std::move(pOutliner));
2269 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2270 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2271 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aEnd, SwFormatField(aPostIt));
2272 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2273 // If this is a range, make sure that it ends after the just inserted character, not before it.
2274 m_xReffedStck->MoveAttrs(*aEnd.GetPoint(), SwFltControlStack::MoveAttrsMode::POSTIT_INSERTED);
2276 return 0;
2279 void SwWW8ImplReader::Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen,
2280 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2282 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2283 OSL_ENSURE(pSttIdx, "impossible");
2284 if (!pSttIdx)
2285 return;
2287 SwPosition aTmpPos(*m_pPaM->GetPoint());
2289 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2291 // tdf#122425: Explicitly remove borders and spacing
2292 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aFlySet(m_rDoc.GetAttrPool());
2293 Reader::ResetFrameFormatAttrs(aFlySet);
2295 SwFlyFrameFormat* pFrame
2296 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2298 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2299 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2300 pFrame->SetFormatAttr( aAnch );
2301 SwFormatFrameSize aSz(SwFrameSize::Minimum, nPageWidth, MINLAY);
2302 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2303 if( eFrameSize != aSz.GetWidthSizeType() )
2304 aSz.SetWidthSizeType( eFrameSize );
2305 pFrame->SetFormatAttr(aSz);
2306 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2307 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2309 // #i43427# - send frame for header/footer into background.
2310 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2311 SdrObject* pFrameObj = CreateContactObject( pFrame );
2312 OSL_ENSURE( pFrameObj,
2313 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2314 if ( pFrameObj )
2316 pFrameObj->SetOrdNum( 0 );
2318 MoveInsideFly(pFrame);
2320 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2322 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2324 MoveOutsideFly(pFrame, aTmpPos);
2327 void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2329 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2330 if (!pSttIdx)
2331 return;
2333 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2335 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2337 *m_pPaM->GetPoint() = std::move(aTmpPos);
2340 bool SwWW8ImplReader::isValid_HdFt_CP(WW8_CP nHeaderCP) const
2342 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2343 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2346 bool SwWW8ImplReader::HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt,
2347 int nSect)
2349 if (m_xHdFt)
2351 WW8_CP nStart, nLen;
2352 sal_uInt8 nNumber = 5;
2354 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2356 if (nI & nWhichItems)
2358 bool bOk = true;
2359 if( m_bVer67 )
2360 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2361 else
2363 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2364 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2367 if (bOk)
2368 return true;
2372 return false;
2375 void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2376 const wwSection &rSection)
2378 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2379 SwPageDesc *pPD = rSection.mpPage;
2381 if( !m_xHdFt )
2382 return;
2384 WW8_CP nStart, nLen;
2385 sal_uInt8 nNumber = 5;
2387 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2388 // corresponding to bit fields in grpfIhdt indicating which
2389 // header/footer(s) are present in this section
2390 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2392 if (nI & grpfIhdt)
2394 bool bOk = true;
2395 if( m_bVer67 )
2396 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2397 else
2399 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2400 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2403 bool bUseLeft
2404 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2405 bool bUseFirst
2406 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2408 // If we are loading a first-page header/footer which is not
2409 // actually enabled in this section (it still needs to be
2410 // loaded as it may be inherited by a later section)
2411 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2413 bool bFooter
2414 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2416 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2417 : bUseFirst ? pPD->GetFirstMaster()
2418 : pPD->GetMaster();
2420 SwFrameFormat* pHdFtFormat;
2421 // If we have empty first page header and footer.
2422 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2423 if (bFooter)
2425 m_bIsFooter = true;
2426 //#i17196# Cannot have left without right
2427 if (!bDisabledFirst
2428 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2429 pPD->GetMaster().SetFormatAttr(SwFormatFooter(true));
2430 if (bUseLeft)
2431 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2432 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2433 pPD->GetFirstMaster().SetFormatAttr(SwFormatFooter(true));
2434 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2436 else
2438 m_bIsHeader = true;
2439 //#i17196# Cannot have left without right
2440 if (!bDisabledFirst
2441 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2442 pPD->GetMaster().SetFormatAttr(SwFormatHeader(true));
2443 if (bUseLeft)
2444 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2445 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2446 pPD->GetFirstMaster().SetFormatAttr(SwFormatHeader(true));
2447 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2450 if (bOk)
2452 bool bHackRequired = false;
2453 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2454 bHackRequired = true;
2455 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2456 bHackRequired = true;
2458 if (bHackRequired)
2460 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2461 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2463 else
2464 Read_HdFtText(nStart, nLen, pHdFtFormat);
2466 else if (pPrev)
2467 CopyPageDescHdFt(pPrev, pPD, nI);
2469 m_bIsHeader = m_bIsFooter = false;
2474 bool wwSectionManager::SectionIsProtected(const wwSection &rSection) const
2476 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2479 void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2480 const wwSection *pPrevious)
2482 // Header/Footer not present
2483 if (!rSection.maSep.grpfIhdt)
2484 return;
2486 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2487 if (rSection.mpPage)
2489 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2490 rSection);
2493 // Header/Footer - Update Index
2494 // So that the index is still valid later on
2495 if (mrReader.m_xHdFt)
2496 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2500 void SwWW8ImplReader::FinalizeTextNode(SwPosition& rPos, bool bAddNew)
2502 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
2504 const SwNumRule* pRule = nullptr;
2506 if (pText != nullptr)
2507 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2509 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2510 // set it as a separate paragraph property, just like we do for DOCX.
2511 // This is only being used for numbering currently, so limiting to that context.
2512 if (pRule)
2514 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1, RES_TXTATR_CHARFMT,
2515 RES_TXTATR_CHARFMT, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2516 items(m_pPaM->GetDoc().GetAttrPool());
2518 SfxWhichIter aIter(items);
2519 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2521 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2522 if (pItem)
2523 items.Put(*pItem);
2525 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
2526 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2527 pText->SetAttr(item);
2530 if (
2531 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2532 (m_bParaAutoBefore || m_bParaAutoAfter)
2535 // If after spacing is set to auto, set the after space to 0
2536 if (m_bParaAutoAfter)
2537 SetLowerSpacing(*m_pPaM, 0);
2539 // If the previous textnode had numbering and
2540 // and before spacing is set to auto, set before space to 0
2541 if(m_pPrevNumRule && m_bParaAutoBefore)
2542 SetUpperSpacing(*m_pPaM, 0);
2544 // If the previous numbering rule was different we need
2545 // to insert a space after the previous paragraph
2546 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2547 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2549 // cache current paragraph
2550 if(m_pPreviousNumPaM)
2552 delete m_pPreviousNumPaM;
2553 m_pPreviousNumPaM = nullptr;
2556 m_pPreviousNumPaM = new SwPaM(*m_pPaM, m_pPaM);
2557 m_pPrevNumRule = pRule;
2559 else if(!pRule && m_pPreviousNumPaM)
2561 // If the previous paragraph has numbering but the current one does not
2562 // we need to add a space after the previous paragraph
2563 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2564 delete m_pPreviousNumPaM;
2565 m_pPreviousNumPaM = nullptr;
2566 m_pPrevNumRule = nullptr;
2568 else
2570 // clear paragraph cache
2571 if(m_pPreviousNumPaM)
2573 delete m_pPreviousNumPaM;
2574 m_pPreviousNumPaM = nullptr;
2576 m_pPrevNumRule = pRule;
2579 // If this is the first paragraph in the document and
2580 // Auto-spacing before paragraph is set,
2581 // set the upper spacing value to 0
2582 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2583 SetUpperSpacing(*m_pPaM, 0);
2585 m_bFirstPara = false;
2587 if (bAddNew)
2588 m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos);
2590 // We can flush all anchored graphics at the end of a paragraph.
2591 m_xAnchorStck->Flush();
2594 bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2596 bool bRet = false;
2597 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2599 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2601 if(pULSpaceItem != nullptr)
2603 SvxULSpaceItem aUL(*pULSpaceItem);
2605 if(bIsUpper)
2606 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2607 else
2608 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2610 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2611 rMyPam.GetPoint()->SetContent(0);
2612 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2613 rMyPam.GetPoint()->SetContent(nEnd);
2614 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2615 bRet = true;
2617 return bRet;
2620 bool SwWW8ImplReader::SetLowerSpacing(SwPaM &rMyPam, int nSpace)
2622 return SetSpacing(rMyPam, nSpace, false);
2625 bool SwWW8ImplReader::SetUpperSpacing(SwPaM &rMyPam, int nSpace)
2627 return SetSpacing(rMyPam, nSpace, true);
2630 sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2632 if (m_bVer67)
2633 return NS_sprm::v6::sprmPTtp;
2634 return nLevel ? NS_sprm::PFInnerTtp::val : NS_sprm::PFTtp::val;
2637 void SwWW8ImplReader::EndSpecial()
2639 // Frame/Table/Anl
2640 if (m_bAnl)
2641 StopAllAnl(); // -> bAnl = false
2643 while(m_aApos.size() > 1)
2645 StopTable();
2646 m_aApos.pop_back();
2647 --m_nInTable;
2648 if (m_aApos[m_nInTable])
2649 StopApo();
2652 if (m_aApos[0])
2653 StopApo();
2655 OSL_ENSURE(!m_nInTable, "unclosed table!");
2658 bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2660 // Frame/Table/Anl
2661 if (m_bInHyperlink)
2662 return false;
2664 rbReSync = false;
2666 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2668 // TabRowEnd
2669 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2671 // Unfortunately, for every paragraph we need to check first whether
2672 // they contain a sprm 29 (0x261B), which starts an APO.
2673 // All other sprms then refer to that APO and not to the normal text
2674 // surrounding it.
2675 // The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2677 // WW: Table in APO is possible (Both Start-Ends occur at the same time)
2678 // WW: APO in Table not possible
2680 // This mean that of a Table is the content of an APO, the APO start needs
2681 // to be edited first, so that the Table remains in the APO and not the
2682 // other way around.
2683 // At the End, however, we need to edit the Table End first as the APO
2684 // must end after that Table (or else we never find the APO End).
2686 // The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2688 // If the Table is within an APO the TabRowEnd Area misses the
2689 // APO settings.
2690 // To not end the APO there, we do not call ProcessApo
2692 // KHZ: When there is a table inside the Apo the Apo-flags are also
2693 // missing for the 2nd, 3rd... paragraphs of each cell.
2695 // 1st look for in-table flag, for 2000+ there is a subtable flag to
2696 // be considered, the sprm 6649 gives the level of the table
2697 sal_uInt8 nCellLevel = 0;
2699 if (m_bVer67)
2700 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2701 else
2703 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2704 if (!nCellLevel)
2705 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2709 WW8_TablePos *pTabPos=nullptr;
2710 WW8_TablePos aTabPos;
2711 if(nCellLevel && !m_bVer67)
2713 WW8PLCFxSave1 aSave;
2714 m_xPlcxMan->GetPap()->Save( aSave );
2715 rbReSync = true;
2716 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2717 WW8_CP nMyStartCp=nStartCp;
2719 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2720 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2721 nCellLevel = *aLevel.pSprm;
2723 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2725 // Bad Table, remain unchanged in level, e.g. #i19667#
2726 if (!bHasRowEnd)
2727 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2729 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2730 pTabPos = &aTabPos;
2732 m_xPlcxMan->GetPap()->Restore( aSave );
2735 // Then look if we are in an Apo
2737 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2739 // Look to see if we are in a Table, but Table in foot/end note not allowed
2740 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2742 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2744 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2745 // WW8TabDesc::TableCellEnd() from making nonsense
2747 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2748 bStopTab = bStartTab = true; // Required to stop and start table
2750 // Test for Anl (Numbering) and process all events in the right order
2751 if( m_bAnl && !bTableRowEnd )
2753 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2754 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2755 if (pSprm13 && aSprm13.nRemainingData >= 1)
2756 { // Still Anl left?
2757 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2758 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2759 || aApo.HasStartStop() // Forced Anl end
2760 || bStopTab || bStartTab )
2762 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2764 else
2766 NextAnlLine( pSprm13 ); // Next Anl Line
2769 else
2770 { // Regular Anl end
2771 StopAllAnl(); // Actual end
2774 if (bStopTab)
2776 StopTable();
2777 m_aApos.pop_back();
2778 --m_nInTable;
2780 if (aApo.mbStopApo)
2782 StopApo();
2783 m_aApos[m_nInTable] = false;
2786 if (aApo.mbStartApo)
2788 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2789 // We need an ReSync after StartApo
2790 // (actually only if the Apo extends past a FKP border)
2791 rbReSync = true;
2793 if (bStartTab)
2795 WW8PLCFxSave1 aSave;
2796 m_xPlcxMan->GetPap()->Save( aSave );
2798 // Numbering for cell borders causes a crash -> no Anls in Tables
2799 if (m_bAnl)
2800 StopAllAnl();
2802 if(m_nInTable < nCellLevel)
2804 if (StartTable(nStartCp))
2805 ++m_nInTable;
2806 else
2807 break;
2808 m_aApos.push_back(false);
2811 if(m_nInTable >= nCellLevel)
2813 // We need an ReSync after StartTable
2814 // (actually only if the Apo extends past a FKP border)
2815 rbReSync = true;
2816 m_xPlcxMan->GetPap()->Restore( aSave );
2819 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2820 return bTableRowEnd;
2823 rtl_TextEncoding SwWW8ImplReader::GetCharSetFromLanguage()
2826 #i22206#/#i52786#
2827 The (default) character set used for a run of text is the default
2828 character set for the version of Word that last saved the document.
2830 This is a bit tentative, more might be required if the concept is correct.
2831 When later version of word write older 6/95 documents the charset is
2832 correctly set in the character runs involved, so it's hard to reproduce
2833 documents that require this to be sure of the process involved.
2835 const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_LANGUAGE);
2836 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2837 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2838 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2841 rtl_TextEncoding SwWW8ImplReader::GetCJKCharSetFromLanguage()
2844 #i22206#/#i52786#
2845 The (default) character set used for a run of text is the default
2846 character set for the version of Word that last saved the document.
2848 This is a bit tentative, more might be required if the concept is correct.
2849 When later version of word write older 6/95 documents the charset is
2850 correctly set in the character runs involved, so it's hard to reproduce
2851 documents that require this to be sure of the process involved.
2853 const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
2854 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2855 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2856 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2859 rtl_TextEncoding SwWW8ImplReader::GetCurrentCharSet()
2862 #i2015
2863 If the hard charset is set use it, if not see if there is an open
2864 character run that has set the charset, if not then fallback to the
2865 current underlying paragraph style.
2867 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2868 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2870 if (!m_bVer67)
2871 eSrcCharSet = GetCharSetFromLanguage();
2872 else if (!m_aFontSrcCharSets.empty())
2873 eSrcCharSet = m_aFontSrcCharSets.top();
2874 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2875 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2876 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2877 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2878 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2879 eSrcCharSet = GetCharSetFromLanguage();
2881 return eSrcCharSet;
2884 //Takashi Ono for CJK
2885 rtl_TextEncoding SwWW8ImplReader::GetCurrentCJKCharSet()
2888 #i2015
2889 If the hard charset is set use it, if not see if there is an open
2890 character run that has set the charset, if not then fallback to the
2891 current underlying paragraph style.
2893 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2894 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2896 if (!m_aFontSrcCJKCharSets.empty())
2897 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2898 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2899 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2900 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2901 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2902 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2903 eSrcCharSet = GetCJKCharSetFromLanguage();
2905 return eSrcCharSet;
2908 void SwWW8ImplReader::PostProcessAttrs()
2910 if (m_pPostProcessAttrsInfo == nullptr)
2911 return;
2913 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2915 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2917 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2918 *pItem);
2919 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2920 pItem->Which());
2923 m_pPostProcessAttrsInfo.reset();
2927 #i9240#
2928 It appears that some documents that are in a baltic 8 bit encoding which has
2929 some undefined characters can have use made of those characters, in which
2930 case they default to CP1252. If not then it's perhaps that the font encoding
2931 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2932 is always 1252.
2934 So an encoding converter that on an undefined character attempts to
2935 convert from 1252 on the undefined character
2937 static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2938 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2940 const sal_uInt32 nFlags =
2941 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2942 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2943 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2944 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2946 const sal_uInt32 nFlags2 =
2947 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2948 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2949 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2950 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2952 std::size_t nDestChars=0;
2953 std::size_t nConverted=0;
2957 sal_uInt32 nInfo = 0;
2958 sal_Size nThisConverted=0;
2960 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2961 pIn+nConverted, nInLen-nConverted,
2962 pOut+nDestChars, nOutLen-nDestChars,
2963 nFlags, &nInfo, &nThisConverted);
2965 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2967 nConverted += nThisConverted;
2969 if (
2970 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2971 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2974 sal_Size nOtherConverted;
2975 rtl_TextToUnicodeConverter hCP1252Converter =
2976 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2977 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2978 pIn+nConverted, 1,
2979 pOut+nDestChars, nOutLen-nDestChars,
2980 nFlags2, &nInfo, &nOtherConverted);
2981 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2982 nConverted+=1;
2984 } while (nConverted < nInLen);
2986 return nDestChars;
2989 bool SwWW8ImplReader::LangUsesHindiNumbers(LanguageType nLang)
2991 bool bResult = false;
2993 switch (static_cast<sal_uInt16>(nLang))
2995 case 0x1401: // Arabic(Algeria)
2996 case 0x3c01: // Arabic(Bahrain)
2997 case 0xc01: // Arabic(Egypt)
2998 case 0x801: // Arabic(Iraq)
2999 case 0x2c01: // Arabic (Jordan)
3000 case 0x3401: // Arabic(Kuwait)
3001 case 0x3001: // Arabic(Lebanon)
3002 case 0x1001: // Arabic(Libya)
3003 case 0x1801: // Arabic(Morocco)
3004 case 0x2001: // Arabic(Oman)
3005 case 0x4001: // Arabic(Qatar)
3006 case 0x401: // Arabic(Saudi Arabia)
3007 case 0x2801: // Arabic(Syria)
3008 case 0x1c01: // Arabic(Tunisia)
3009 case 0x3801: // Arabic(U.A.E)
3010 case 0x2401: // Arabic(Yemen)
3011 bResult = true;
3012 break;
3013 default:
3014 break;
3017 return bResult;
3020 sal_Unicode SwWW8ImplReader::TranslateToHindiNumbers(sal_Unicode nChar)
3022 if (nChar >= 0x0030 && nChar <= 0x0039)
3023 return nChar + 0x0630;
3025 return nChar;
3028 namespace
3030 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3032 //if read len was in or around that of allocated len, just reuse pStr
3033 if (nAllocLen < pStr->length + 256)
3034 return OUString(pStr, SAL_NO_ACQUIRE);
3035 //otherwise copy the shorter used section to release extra mem
3036 OUString sRet(pStr->buffer, pStr->length);
3037 rtl_uString_release(pStr);
3038 return sRet;
3043 * Return value: true for non special chars
3045 bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3047 sal_Int32 nRequestedStrLen = nEnd - rPos;
3049 OSL_ENSURE(nRequestedStrLen, "String is 0");
3050 if (nRequestedStrLen <= 0)
3051 return true;
3053 WW8_CP nCp;
3054 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3055 if (bFail)
3057 rPos+=nRequestedStrLen;
3058 return true;
3061 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3062 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3063 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3064 if (!bValidPos)
3066 // Swallow missing range, e.g. #i95550#
3067 rPos+=nRequestedStrLen;
3068 return true;
3071 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3072 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3073 if (!nAvailableStrLen)
3075 // Swallow missing range, e.g. #i95550#
3076 rPos+=nRequestedStrLen;
3077 return true;
3080 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3082 // Reset Unicode flag and correct FilePos if needed.
3083 // Note: Seek is not expensive, as we're checking inline whether or not
3084 // the correct FilePos has already been reached.
3085 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3087 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3088 RTL_TEXTENCODING_MS_1252;
3089 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3092 fdo#82904
3094 Older documents exported as word 95 that use unicode aware fonts will
3095 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3096 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3097 pain.
3099 We will try and use a fallback encoding if the conversion from
3100 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3101 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3102 valid RTL_TEXTENCODING_MS_932 by chance :-(
3104 We're not the only ones that struggle with this: Here's the help from
3105 MSOffice 2003 on the topic:
3108 Earlier versions of Microsoft Word were sometimes used in conjunction with
3109 third-party language-processing add-in programs designed to support Chinese or
3110 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3111 results in incorrect text display in more recent versions of Word.
3113 However, you can set options to convert these documents so that text is
3114 displayed correctly. On the Tools menu, click Options, and then click the
3115 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3116 text (to have Word interpret the text as Asian code page data, regardless of
3117 its font) or Automatically detect Asian text (to have Word attempt to determine
3118 which parts of the text are meant to be Asian).
3121 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3122 the language is not Japanese
3125 const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
3126 if (pItem != nullptr && LANGUAGE_JAPANESE != pItem->GetLanguage())
3128 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3129 eSrcCharSet = GetCharSetFromLanguage();
3132 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3133 RTL_TEXTENCODING_MS_1252;
3135 // allocate unicode string data
3136 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3137 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3138 sal_Unicode* pBuffer = xStr->buffer;
3139 sal_Unicode* pWork = pBuffer;
3141 std::unique_ptr<char[]> p8Bits;
3143 rtl_TextToUnicodeConverter hConverter = nullptr;
3144 if (!m_bIsUnicode || m_bVer67)
3145 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3147 if (!m_bIsUnicode)
3148 p8Bits.reset( new char[nStrLen] );
3150 // read the stream data
3151 sal_uInt8 nBCode = 0;
3152 sal_uInt16 nUCode;
3154 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3155 const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CTL_LANGUAGE);
3156 if (pItem != nullptr)
3157 nCTLLang = pItem->GetLanguage();
3159 sal_Int32 nL2;
3160 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3162 if (m_bIsUnicode)
3163 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3164 else
3166 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3167 nUCode = nBCode;
3170 if (!m_pStrm->good())
3172 rPos = WW8_CP_MAX-10; // -> eof or other error
3173 return true;
3176 if ((32 > nUCode) || (0xa0 == nUCode))
3178 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3179 break; // Special character < 32, == 0xa0 found
3182 if (m_bIsUnicode)
3184 if (!m_bVer67)
3185 *pWork++ = nUCode;
3186 else
3188 if (nUCode >= 0x3000) //0x8000 ?
3190 char aTest[2];
3191 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3192 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3193 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3194 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3195 *pWork++ = aTemp[0];
3197 else
3199 char cTest = static_cast< char >(nUCode & 0x00FF);
3200 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3204 else
3205 p8Bits[nL2] = nBCode;
3208 if (nL2)
3210 const sal_Int32 nEndUsed = !m_bIsUnicode
3211 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3212 : pWork - pBuffer;
3214 if (m_bRegardHindiDigits && m_bBidi && LangUsesHindiNumbers(nCTLLang))
3216 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3217 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3220 xStr->buffer[nEndUsed] = 0;
3221 xStr->length = nEndUsed;
3223 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3224 rPos += nL2;
3225 if (!m_aApos.back()) // a para end in apo doesn't count
3226 m_bWasParaEnd = false; // No CR
3229 if (hConverter)
3230 rtl_destroyTextToUnicodeConverter(hConverter);
3231 return nL2 >= nStrLen;
3234 #define MSASCII SAL_MAX_INT16
3236 namespace
3238 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
3239 sal_Int16 lcl_getScriptType(
3240 const uno::Reference<i18n::XBreakIterator>& rBI,
3241 const OUString &rString, sal_Int32 nPos)
3243 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3244 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3245 nScript = MSASCII;
3246 return nScript;
3249 // We want to know about WEAK segments, so endOfScript isn't
3250 // useful, and see lcl_getScriptType anyway
3251 sal_Int32 lcl_endOfScript(
3252 const uno::Reference<i18n::XBreakIterator>& rBI,
3253 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3255 while (nPos < rString.getLength())
3257 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3258 if (nScript != nNewScript)
3259 break;
3260 ++nPos;
3262 return nPos;
3265 sal_Int32 lcl_getWriterScriptType(
3266 const uno::Reference<i18n::XBreakIterator>& rBI,
3267 const OUString &rString, sal_Int32 nPos)
3269 sal_Int16 nScript = i18n::ScriptType::WEAK;
3271 if (rString.isEmpty())
3272 return nScript;
3274 while (nPos >= 0)
3276 nScript = rBI->getScriptType(rString, nPos);
3277 if (nScript != i18n::ScriptType::WEAK)
3278 break;
3279 --nPos;
3282 return nScript;
3285 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3287 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3290 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3292 // Ignoring CharSet, and ignoring unknown pitch
3293 return rA.GetFamilyName() == rB.GetFamilyName() &&
3294 rA.GetStyleName() == rB.GetStyleName() &&
3295 rA.GetFamily() == rB.GetFamily() &&
3296 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3300 // In writer we categorize text into CJK, CTL and "Western" for everything else.
3301 // Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3302 // NonEastAsian/HighAnsi, with some shared characters and some properties to
3303 // hint as to which way to bias those shared characters.
3305 // That's four categories, we however have three categories. Given that problem
3306 // here we would ideally find out "what would word do" to see what font/language
3307 // word would assign to characters based on the unicode range they fall into and
3308 // hack the word one onto the range we use. However it's unclear what word's
3309 // categorization is. So we don't do that here yet.
3311 // Additional to the categorization, when word encounters weak text for ambiguous
3312 // chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3313 // feature in writer.
3315 // So what we currently do here then is to split our text into non-weak/weak
3316 // sections and uses word's idcthint to determine what font it would use and
3317 // force that on for the segment. Following what we *do* know about word's
3318 // categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3319 // word, something we map to LATIN, so we consider all weak chars in that range
3320 // to auto-bias to LATIN.
3322 // See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
3323 void SwWW8ImplReader::emulateMSWordAddTextToParagraph(const OUString& rAddString)
3325 if (rAddString.isEmpty())
3326 return;
3328 if (m_bFuzzing)
3330 simpleAddTextToParagraph(rAddString);
3331 return;
3334 const uno::Reference<i18n::XBreakIterator>& xBI(g_pBreakIt->GetBreakIter());
3335 assert(xBI.is());
3337 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3338 sal_Int32 nLen = rAddString.getLength();
3340 OUString sParagraphText;
3341 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3342 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3343 if (pNd)
3344 sParagraphText = pNd->GetText();
3345 sal_Int32 nParaOffset = sParagraphText.getLength();
3346 sParagraphText = sParagraphText + rAddString;
3348 sal_Int32 nPos = 0;
3349 while (nPos < nLen)
3351 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3352 if (nEnd < 0)
3353 break;
3355 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3356 const TypedWhichId<SvxFontItem> aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3357 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3358 bool aForced[] = {false, false, false};
3360 int nLclIdctHint = 0xFF;
3361 if (nScript == i18n::ScriptType::WEAK)
3363 const SfxInt16Item *pIdctHint = GetFormatAttr(RES_CHRATR_IDCTHINT);
3364 nLclIdctHint = pIdctHint->GetValue();
3366 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3367 nLclIdctHint = 0;
3369 TypedWhichId<SvxFontItem> nForceFromFontId(0);
3370 if (nLclIdctHint != 0xFF)
3372 switch (nLclIdctHint)
3374 case 0:
3375 nForceFromFontId = RES_CHRATR_FONT;
3376 break;
3377 case 1:
3378 nForceFromFontId = RES_CHRATR_CJK_FONT;
3379 break;
3380 case 2:
3381 nForceFromFontId = RES_CHRATR_CTL_FONT;
3382 break;
3383 default:
3384 break;
3388 if (sal_uInt16(nForceFromFontId) != 0)
3390 // Now we know that word would use the nForceFromFontId font for this range
3391 // Try and determine what script writer would assign this range to
3393 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3394 nPos + nParaOffset);
3396 bool bWriterWillUseSameFontAsWordAutomatically = false;
3398 if (nWriterScript != i18n::ScriptType::WEAK)
3400 if (
3401 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3402 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3403 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3406 bWriterWillUseSameFontAsWordAutomatically = true;
3408 else
3410 const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
3411 TypedWhichId<SvxFontItem> nDestId = aIds[nWriterScript-1];
3412 const SvxFontItem *pDestFont = GetFormatAttr(nDestId);
3413 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3417 // Writer won't use the same font as word, so force the issue
3418 if (!bWriterWillUseSameFontAsWordAutomatically)
3420 const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
3422 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3424 const SvxFontItem *pDestFont = GetFormatAttr(aIds[i]);
3425 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3426 if (aForced[i])
3428 pOverriddenItems[i] =
3429 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3431 SvxFontItem aForceFont(*pSourceFont);
3432 aForceFont.SetWhich(aIds[i]);
3433 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3439 simpleAddTextToParagraph(sChunk);
3441 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3443 if (aForced[i])
3445 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3446 if (pOverriddenItems[i])
3447 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3451 nPos = nEnd;
3452 if (nPos < nLen)
3453 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3457 namespace sw {
3459 auto FilterControlChars(std::u16string_view aString) -> OUString
3461 OUStringBuffer buf(aString.size());
3462 for (size_t i = 0; i < aString.size(); ++i)
3464 sal_Unicode const ch(aString[i]);
3465 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3467 buf.append(ch);
3469 else
3471 SAL_INFO("sw.ww8", "filtering control character");
3474 return buf.makeStringAndClear();
3477 } // namespace sw
3479 void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
3481 OUString const addString(sw::FilterControlChars(aAddString));
3483 if (addString.isEmpty())
3484 return;
3486 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3487 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3489 OSL_ENSURE(pNd, "What the hell, where's my text node");
3491 if (!pNd)
3492 return;
3494 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3495 if (nCharsLeft > 0)
3497 if (addString.getLength() <= nCharsLeft)
3499 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3501 else
3503 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3504 FinalizeTextNode(*m_pPaM->GetPoint());
3505 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3508 else
3510 FinalizeTextNode(*m_pPaM->GetPoint());
3511 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3514 m_bReadTable = false;
3518 * Return value: true for para end
3520 bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3521 tools::Long nCpOfs)
3523 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3525 if (m_bSymbol || m_bIgnoreText)
3527 WW8_CP nRequested = nEnd - rPos;
3528 if (m_bSymbol) // Insert special chars
3530 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3531 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3533 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3534 nRequested = nMaxPossible;
3537 if (!linguistic::IsControlChar(m_cSymbol)
3538 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3540 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3542 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(m_cSymbol));
3544 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
3545 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CJK_FONT);
3546 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CTL_FONT);
3549 m_pStrm->SeekRel(nRequested);
3550 rPos = nEnd; // Ignore until attribute end
3551 return false;
3554 while (true)
3556 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3557 return false; // Done
3559 bool bStartLine = ReadChar(rPos, nCpOfs);
3560 rPos++;
3561 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3563 return bStartLine;
3568 bool SwWW8ImplReader::HandlePageBreakChar()
3570 bool bParaEndAdded = false;
3571 // #i1909# section/page breaks should not occur in tables, word
3572 // itself ignores them in this case.
3573 if (!m_nInTable)
3575 bool IsTemp=true;
3576 SwTextNode* pTemp = m_pPaM->GetPointNode().GetTextNode();
3577 if (pTemp && pTemp->GetText().isEmpty()
3578 && (m_bFirstPara || m_bFirstParaOfPage))
3580 IsTemp = false;
3581 FinalizeTextNode(*m_pPaM->GetPoint());
3582 pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
3585 m_bPgSecBreak = true;
3586 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3588 If it's a 0x0c without a paragraph end before it, act like a
3589 paragraph end, but nevertheless, numbering (and perhaps other
3590 similar constructs) do not exist on the para.
3592 if (!m_bWasParaEnd && IsTemp)
3594 bParaEndAdded = true;
3595 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3597 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3599 pTextNode->SetAttr(
3600 *GetDfltAttr(RES_PARATR_NUMRULE));
3605 return bParaEndAdded;
3608 bool SwWW8ImplReader::ReadChar(tools::Long nPosCp, tools::Long nCpOfs)
3610 bool bNewParaEnd = false;
3611 // Reset Unicode flag and correct FilePos if needed.
3612 // Note: Seek is not expensive, as we're checking inline whether or not
3613 // the correct FilePos has already been reached.
3614 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3615 if (!checkSeek(*m_pStrm, nRequestedPos))
3616 return false;
3618 sal_uInt16 nWCharVal(0);
3619 if( m_bIsUnicode )
3620 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3621 else
3623 sal_uInt8 nBCode(0);
3624 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3625 nWCharVal = nBCode;
3628 sal_Unicode cInsert = '\x0';
3629 bool bParaMark = false;
3631 if ( 0xc != nWCharVal )
3632 m_bFirstParaOfPage = false;
3634 switch (nWCharVal)
3636 case 0:
3637 if (!m_bFuzzing)
3639 // Page number
3640 SwPageNumberField aField(
3641 static_cast<SwPageNumberFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(
3642 SwFieldIds::PageNumber )), PG_RANDOM, SVX_NUM_ARABIC);
3643 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
3645 else
3647 // extremely slow, so skip for fuzzing, and insert a space instead
3648 cInsert = ' ';
3650 break;
3651 case 0xe:
3652 // if there is only one column word treats a column break like a pagebreak.
3653 if (m_aSectionManager.CurrentSectionColCount() < 2)
3654 bParaMark = HandlePageBreakChar();
3655 else if (!m_nInTable)
3657 // Always insert a txtnode for a column break, e.g. ##
3658 SwContentNode *pCntNd=m_pPaM->GetPointContentNode();
3659 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3660 FinalizeTextNode(*m_pPaM->GetPoint());
3661 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
3663 break;
3664 case 0x7:
3666 bNewParaEnd = true;
3667 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3668 //The last paragraph of each cell is terminated by a special
3669 //paragraph mark called a cell mark. Following the cell mark
3670 //that ends the last cell of a table row, the table row is
3671 //terminated by a special paragraph mark called a row mark
3673 //So the 0x7 should be right at the end of the previous
3674 //range to be a real cell-end.
3675 if (pPap->nOrigStartPos == nPosCp+1 ||
3676 pPap->nOrigStartPos == WW8_CP_MAX)
3678 TabCellEnd(); // Table cell/row end
3680 else
3681 bParaMark = true;
3683 break;
3684 case 0xf:
3685 if( !m_bSpec ) // "Satellite"
3686 cInsert = u'\x00a4';
3687 break;
3688 case 0x14:
3689 if( !m_bSpec ) // "Para End" char
3690 cInsert = u'\x00b5';
3691 //TODO: should this be U+00B6 PILCROW SIGN rather than
3692 // U+00B5 MICRO SIGN?
3693 break;
3694 case 0x15:
3695 if( !m_bSpec ) // Juristenparagraph
3697 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3698 if (aItr == m_aTOXEndCps.end())
3699 cInsert = u'\x00a7';
3700 else
3701 m_aTOXEndCps.erase(aItr);
3703 break;
3704 case 0x9:
3705 cInsert = '\x9'; // Tab
3706 break;
3707 case 0xb:
3708 cInsert = '\xa'; // Hard NewLine
3709 break;
3710 case 0xc:
3711 bParaMark = HandlePageBreakChar();
3712 break;
3713 case 0x1e: // Non-breaking hyphen
3714 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDHYPHEN) );
3715 break;
3716 case 0x1f: // Non-required hyphens
3717 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_SOFTHYPHEN) );
3718 break;
3719 case 0xa0: // Non-breaking spaces
3720 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDBLANK) );
3721 break;
3722 case 0x1:
3724 Current thinking is that if bObj is set then we have a
3725 straightforward "traditional" ole object, otherwise we have a
3726 graphic preview of an associated ole2 object (or a simple
3727 graphic of course)
3729 normally in the canvas field, the code is 0x8 0x1.
3730 in a special case, the code is 0x1 0x1, which yields a simple picture
3733 bool bReadObj = IsInlineEscherHack();
3734 if( bReadObj )
3736 sal_uInt64 nCurPos = m_pStrm->Tell();
3737 sal_uInt16 nWordCode(0);
3739 if( m_bIsUnicode )
3740 m_pStrm->ReadUInt16( nWordCode );
3741 else
3743 sal_uInt8 nByteCode(0);
3744 m_pStrm->ReadUChar( nByteCode );
3745 nWordCode = nByteCode;
3747 if( nWordCode == 0x1 )
3748 bReadObj = false;
3749 m_pStrm->Seek( nCurPos );
3751 if( !bReadObj )
3753 SwFrameFormat *pResult = nullptr;
3754 if (m_bObj)
3755 pResult = ImportOle();
3756 else if (m_bSpec)
3758 SwFrameFormat* pAsCharFlyFormat =
3759 m_rDoc.MakeFrameFormat(OUString(), m_rDoc.GetDfltFrameFormat(), true);
3760 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3761 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3762 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3763 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3767 // If we have a bad 0x1 insert a space instead.
3768 if (!pResult)
3770 cInsert = ' ';
3771 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3772 "WW8: Please report this document, it may have a "
3773 "missing graphic");
3775 else
3777 // reset the flags.
3778 m_bObj = m_bEmbeddObj = false;
3779 m_nObjLocFc = 0;
3783 break;
3784 case 0x8:
3785 if( !m_bObj )
3786 Read_GrafLayer( nPosCp );
3787 break;
3788 case 0xd:
3789 bNewParaEnd = bParaMark = true;
3790 if (m_nInTable > 1)
3793 #i9666#/#i23161#
3794 Yes complex, if there is an entry in the undocumented PLCF
3795 which I believe to be a record of cell and row boundaries
3796 see if the magic bit which I believe to mean cell end is
3797 set. I also think btw that the third byte of the 4 byte
3798 value is the level of the cell
3800 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3801 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3802 pTest->Where() == nPosCp+1+nCpOfs)
3804 WW8_FC nPos;
3805 void *pData;
3806 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3807 : 0;
3808 if (nData & 0x2) // Might be how it works
3810 TabCellEnd();
3811 bParaMark = false;
3814 // tdf#106799: We expect TTP marks to be also cell marks,
3815 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3816 else if (m_bWasTabCellEnd || m_bWasTabRowEnd)
3818 TabCellEnd();
3819 bParaMark = false;
3823 m_bWasTabCellEnd = false;
3825 break; // line end
3826 case 0x5: // Annotation reference
3827 case 0x13:
3828 break;
3829 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3830 if (!m_aFootnoteStack.empty())
3831 cInsert = '?';
3832 break;
3833 default:
3834 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3835 break;
3838 if( '\x0' != cInsert )
3840 OUString sInsert(cInsert);
3841 emulateMSWordAddTextToParagraph(sInsert);
3843 if (!m_aApos.back()) // a para end in apo doesn't count
3844 m_bWasParaEnd = bNewParaEnd;
3845 return bParaMark;
3848 void SwWW8ImplReader::ProcessCurrentCollChange(WW8PLCFManResult& rRes,
3849 bool* pStartAttr, bool bCallProcessSpecial)
3851 sal_uInt16 nOldColl = m_nCurrentColl;
3852 m_nCurrentColl = m_xPlcxMan->GetColl();
3854 // Invalid Style-Id
3855 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3857 m_nCurrentColl = 0;
3858 m_bParaAutoBefore = false;
3859 m_bParaAutoAfter = false;
3861 else
3863 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3864 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3867 if (nOldColl >= m_vColl.size())
3868 nOldColl = 0; // guess! TODO make sure this is what we want
3870 bool bTabRowEnd = false;
3871 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3873 bool bReSync;
3874 // Frame/Table/Autonumbering List Level
3875 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3876 if( bReSync )
3877 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3880 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3882 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[ m_nCurrentColl ]);
3883 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3884 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3885 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3889 tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3891 tools::Long nSkipChars = 0;
3892 WW8PLCFManResult aRes;
3894 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3895 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3896 aRes.nCurrentCp = rTextPos; // Current Cp position
3898 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3899 if ( bNewSection ) // New Section
3901 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3902 // Create PageDesc and fill it
3903 m_aSectionManager.CreateSep(rTextPos);
3904 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3905 // Create PageDesc and fill it
3906 m_bPgSecBreak = false;
3907 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3910 // New paragraph over Plcx.Fkp.papx
3911 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3913 ProcessCurrentCollChange( aRes, &bStartAttr,
3914 MAN_MASK_NEW_PAP == (aRes.nFlags & MAN_MASK_NEW_PAP) &&
3915 !m_bIgnoreText );
3916 rbStartLine = false;
3919 // position of last CP that's to be ignored
3920 tools::Long nSkipPos = -1;
3922 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3924 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3926 if( bStartAttr ) // WW attributes
3928 if( aRes.nMemLen >= 0 )
3929 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3931 else
3932 EndSprm( aRes.nSprmId ); // Switch off Attr
3934 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3936 if (bStartAttr)
3938 nSkipChars = ImportExtSprm(&aRes);
3939 if (
3940 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3941 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3944 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3945 // Skip Field/Footnote-/End-Note here
3946 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3947 nSkipPos = rTextPos-1;
3950 else
3951 EndExtSprm( aRes.nSprmId );
3955 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3956 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3957 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3959 // Find next Attr position (and Skip attributes of field contents if needed)
3960 if (nSkipChars && !m_bIgnoreText)
3961 m_xCtrlStck->MarkAllAttrsOld();
3962 bool bOldIgnoreText = m_bIgnoreText;
3963 m_bIgnoreText = true;
3964 sal_uInt16 nOldColl = m_nCurrentColl;
3965 bool bDoPlcxManPlusPLus = true;
3966 tools::Long nNext;
3969 if( bDoPlcxManPlusPLus )
3970 m_xPlcxMan->advance();
3971 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3973 if (m_pPostProcessAttrsInfo &&
3974 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3976 m_pPostProcessAttrsInfo->mbCopy = true;
3979 if( (0 <= nNext) && (nSkipPos >= nNext) )
3981 if (nDepthGuard >= 1024)
3983 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3984 nNext = nTextEnd;
3986 else
3987 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3988 bDoPlcxManPlusPLus = false;
3989 m_bIgnoreText = true;
3992 if (m_pPostProcessAttrsInfo &&
3993 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3995 m_pPostProcessAttrsInfo->mbCopy = false;
3998 while( nSkipPos >= nNext );
3999 m_bIgnoreText = bOldIgnoreText;
4000 if( nSkipChars )
4002 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
4003 if( nOldColl != m_xPlcxMan->GetColl() )
4004 ProcessCurrentCollChange(aRes, nullptr, false);
4007 return nNext;
4010 void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
4012 // Do we have attributes?
4013 if( rTextPos >= rNext )
4017 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
4018 if (rTextPos == rNext && rTextPos >= nTextEnd)
4019 break;
4021 while( rTextPos >= rNext );
4024 else if ( rbStartLine )
4026 /* No attributes, but still a new line.
4027 * If a line ends with a line break and paragraph attributes or paragraph templates
4028 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
4029 * is false.
4030 * Due to this we need to set the template here as a kind of special treatment.
4032 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4033 SetTextFormatCollAndListLevel(*m_pPaM, m_vColl[m_nCurrentColl]);
4034 rbStartLine = false;
4039 * CloseAttrEnds to only read the attribute ends at the end of a text or a
4040 * text area (Header, Footnote, ...).
4041 * We ignore attribute starts and fields.
4043 void SwWW8ImplReader::CloseAttrEnds()
4045 // If there are any unclosed sprms then copy them to
4046 // another stack and close the ones that must be closed
4047 std::stack<sal_uInt16> aStack;
4048 m_xPlcxMan->TransferOpenSprms(aStack);
4050 while (!aStack.empty())
4052 sal_uInt16 nSprmId = aStack.top();
4053 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4054 EndSprm(nSprmId);
4055 aStack.pop();
4058 EndSpecial();
4061 bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4063 bool bJoined=false;
4065 bool bStartLine = true;
4066 short nCrCount = 0;
4067 short nDistance = 0;
4069 m_bWasParaEnd = false;
4070 m_nCurrentColl = 0;
4071 m_xCurrentItemSet.reset();
4072 m_nCharFormat = -1;
4073 m_bSpec = false;
4074 m_bPgSecBreak = false;
4076 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4077 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4079 WW8_CP nNext = m_xPlcxMan->Where();
4080 m_xPreviousNode.reset();
4081 sal_uInt8 nDropLines = 0;
4082 SwCharFormat* pNewSwCharFormat = nullptr;
4083 const SwCharFormat* pFormat = nullptr;
4085 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4086 if (!bValidPos)
4087 return false;
4089 WW8_CP l = nStartCp;
4090 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4091 if (nTextLen > nMaxPossible)
4093 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4094 nTextLen = nMaxPossible;
4096 WW8_CP nTextEnd = nStartCp+nTextLen;
4097 while (l < nTextEnd)
4099 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4100 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4102 if (m_pPostProcessAttrsInfo != nullptr)
4103 PostProcessAttrs();
4105 if (l >= nTextEnd)
4106 break;
4108 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4110 // If the previous paragraph was a dropcap then do not
4111 // create a new txtnode and join the two paragraphs together
4112 if (bStartLine && !m_xPreviousNode) // Line end
4114 bool bSplit = true;
4115 if (m_bCareFirstParaEndInToc)
4117 m_bCareFirstParaEndInToc = false;
4118 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4119 bSplit = false;
4121 if (m_bCareLastParaEndInToc)
4123 m_bCareLastParaEndInToc = false;
4124 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4125 bSplit = false;
4127 if (bSplit)
4129 FinalizeTextNode(*m_pPaM->GetPoint());
4133 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4135 SwTextNode* pEndNd = m_pPaM->GetPointNode().GetTextNode();
4136 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4137 if (pEndNd)
4139 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4141 // Need to reset the font size and text position for the dropcap
4143 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4144 m_xCtrlStck->Delete(aTmp);
4147 // Get the default document dropcap which we can use as our template
4148 const SwFormatDrop* defaultDrop = GetFormatAttr(RES_PARATR_DROP);
4149 SwFormatDrop aDrop(*defaultDrop);
4151 aDrop.SetLines(nDropLines);
4152 aDrop.SetDistance(nDistance);
4153 aDrop.SetChars(writer_cast<sal_uInt8>(nDropCapLen));
4154 // Word has no concept of a "whole word dropcap"
4155 aDrop.SetWholeWord(false);
4157 if (pFormat)
4158 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4159 else if(pNewSwCharFormat)
4160 aDrop.SetCharFormat(pNewSwCharFormat);
4162 SwPosition aStart(*pEndNd);
4163 m_xCtrlStck->NewAttr(aStart, aDrop);
4164 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_DROP);
4166 m_xPreviousNode.reset();
4168 else if (m_bDropCap)
4170 // If we have found a dropcap store the textnode
4171 m_xPreviousNode.reset(new TextNodeListener(m_pPaM->GetPointNode().GetTextNode()));
4173 SprmResult aDCS;
4174 if (m_bVer67)
4175 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4176 else
4177 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4179 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4180 nDropLines = (*aDCS.pSprm) >> 3;
4181 else // There is no Drop Cap Specifier hence no dropcap
4182 m_xPreviousNode.reset();
4184 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4185 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4186 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4187 else
4188 nDistance = 0;
4190 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4192 if (m_xCurrentItemSet)
4193 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4195 if (pSwFormatCharFormat)
4196 pFormat = pSwFormatCharFormat->GetCharFormat();
4198 if (m_xCurrentItemSet && !pFormat)
4200 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4201 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4202 m_xCurrentItemSet->ClearItem(RES_CHRATR_ESCAPEMENT);
4203 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4206 m_xCurrentItemSet.reset();
4207 m_bDropCap=false;
4210 if (bStartLine || m_bWasTabRowEnd)
4212 // Call all 64 CRs; not for Header and the like
4213 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4215 if (nTextLen < WW8_CP_MAX/100)
4216 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4217 else
4218 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4219 m_xProgress->Update(m_nProgress); // Update
4223 // If we have encountered a 0x0c which indicates either section of
4224 // pagebreak then look it up to see if it is a section break, and
4225 // if it is not then insert a page break. If it is a section break
4226 // it will be handled as such in the ReadAttrs of the next loop
4227 if (m_bPgSecBreak)
4229 // We need only to see if a section is ending at this cp,
4230 // the plcf will already be sitting on the correct location
4231 // if it is there.
4232 WW8PLCFxDesc aTemp;
4233 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4234 if (m_xPlcxMan->GetSepPLCF())
4235 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4236 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4238 // #i39251# - insert text node for page break, if no one inserted.
4239 // #i43118# - refine condition: the anchor
4240 // control stack has to have entries, otherwise it's not needed
4241 // to insert a text node.
4242 if (!bStartLine && !m_xAnchorStck->empty())
4244 FinalizeTextNode(*m_pPaM->GetPoint());
4246 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM,
4247 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4248 m_bFirstParaOfPage = true;
4249 m_bPgSecBreak = false;
4254 m_xPreviousNode.reset();
4256 if (m_pPaM->GetPoint()->GetContentIndex())
4257 FinalizeTextNode(*m_pPaM->GetPoint());
4259 if (!m_bInHyperlink)
4260 bJoined = JoinNode(*m_pPaM);
4262 CloseAttrEnds();
4264 m_xPlcxMan.reset();
4265 return bJoined;
4268 SwWW8ImplReader::SwWW8ImplReader(sal_uInt8 nVersionPara, SotStorage* pStorage,
4269 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4270 : m_pDocShell(rD.GetDocShell())
4271 , m_pStg(pStorage)
4272 , m_pStrm(pSt)
4273 , m_pTableStream(nullptr)
4274 , m_pDataStream(nullptr)
4275 , m_rDoc(rD)
4276 , m_pPaM(nullptr)
4277 , m_aSectionManager(*this)
4278 , m_aExtraneousParas(rD)
4279 , m_aInsertedTables(rD)
4280 , m_aSectionNameGenerator(rD, u"WW"_ustr)
4281 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4282 , m_aParaStyleMapper(rD)
4283 , m_aCharStyleMapper(rD)
4284 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4285 , m_pPreviousNumPaM(nullptr)
4286 , m_pPrevNumRule(nullptr)
4287 , m_pCurrentColl(nullptr)
4288 , m_pDfltTextFormatColl(nullptr)
4289 , m_pStandardFormatColl(nullptr)
4290 , m_pDrawModel(nullptr)
4291 , m_pDrawPg(nullptr)
4292 , m_pNumFieldType(nullptr)
4293 , m_sBaseURL(std::move(aBaseURL))
4294 , m_nIniFlags(0)
4295 , m_nIniFlags1(0)
4296 , m_nFieldFlags(0)
4297 , m_bRegardHindiDigits( false )
4298 , m_bDrawCpOValid( false )
4299 , m_nDrawCpO(0)
4300 , m_nPicLocFc(0)
4301 , m_nObjLocFc(0)
4302 , m_nIniFlyDx(0)
4303 , m_nIniFlyDy(0)
4304 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4305 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4306 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4307 , m_nProgress(0)
4308 , m_nCurrentColl(0)
4309 , m_nFieldNum(0)
4310 , m_nLFOPosition(USHRT_MAX)
4311 , m_nCharFormat(0)
4312 , m_nDrawXOfs(0)
4313 , m_nDrawYOfs(0)
4314 , m_nDrawXOfs2(0)
4315 , m_nDrawYOfs2(0)
4316 , m_cSymbol(0)
4317 , m_nWantedVersion(nVersionPara)
4318 , m_nSwNumLevel(0xff)
4319 , m_nWwNumType(0xff)
4320 , m_pChosenWW8OutlineStyle(nullptr)
4321 , m_nListLevel(MAXLEVEL)
4322 , m_bNewDoc(bNewDoc)
4323 , m_bSkipImages(bSkipImages)
4324 , m_bReadNoTable(false)
4325 , m_bPgSecBreak(false)
4326 , m_bSpec(false)
4327 , m_bObj(false)
4328 , m_bTxbxFlySection(false)
4329 , m_bHasBorder(false)
4330 , m_bSymbol(false)
4331 , m_bIgnoreText(false)
4332 , m_nInTable(0)
4333 , m_bWasTabRowEnd(false)
4334 , m_bWasTabCellEnd(false)
4335 , m_bAnl(false)
4336 , m_bHdFtFootnoteEdn(false)
4337 , m_bFootnoteEdn(false)
4338 , m_bIsHeader(false)
4339 , m_bIsFooter(false)
4340 , m_bIsUnicode(false)
4341 , m_bCpxStyle(false)
4342 , m_bStyNormal(false)
4343 , m_bWWBugNormal(false)
4344 , m_bNoAttrImport(false)
4345 , m_bInHyperlink(false)
4346 , m_bWasParaEnd(false)
4347 , m_bVer67(false)
4348 , m_bVer6(false)
4349 , m_bVer7(false)
4350 , m_bVer8(false)
4351 , m_bEmbeddObj(false)
4352 , m_bCurrentAND_fNumberAcross(false)
4353 , m_bNoLnNumYet(true)
4354 , m_bFirstPara(true)
4355 , m_bFirstParaOfPage(false)
4356 , m_bParaAutoBefore(false)
4357 , m_bParaAutoAfter(false)
4358 , m_bDropCap(false)
4359 , m_nDropCap(0)
4360 , m_bBidi(false)
4361 , m_bReadTable(false)
4362 , m_bLoadingTOXCache(false)
4363 , m_nEmbeddedTOXLevel(0)
4364 , m_bLoadingTOXHyperlink(false)
4365 , m_bCareFirstParaEndInToc(false)
4366 , m_bCareLastParaEndInToc(false)
4367 , m_bNotifyMacroEventRead(false)
4368 , m_bFuzzing(comphelper::IsFuzzing())
4370 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4371 m_aApos.push_back(false);
4373 mpCursor = m_rDoc.CreateUnoCursor(rPos);
4376 SwWW8ImplReader::~SwWW8ImplReader()
4380 void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4382 if( pStck )
4384 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4385 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4387 else
4389 OSL_ENSURE( false, "WW stack already deleted" );
4393 void wwSectionManager::SetSegmentToPageDesc(const wwSection &rSection,
4394 bool bIgnoreCols)
4396 SwPageDesc &rPage = *rSection.mpPage;
4398 SetNumberingType(rSection, rPage);
4400 SwFrameFormat &rFormat = rPage.GetMaster();
4402 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4403 mrReader.GraphicCtor();
4405 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4407 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4408 SvxMSDffImportData aData(aRect);
4409 rtl::Reference<SdrObject> pObject;
4410 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4412 // Only handle shape if it is a background shape
4413 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4415 SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND,XATTR_START, XATTR_END>
4416 aSet(rFormat.GetDoc()->GetAttrPool());
4417 mrReader.MatchSdrItemsIntoFlySet(pObject.get(), aSet, mso_lineSimple,
4418 mso_lineSolid, mso_sptRectangle, aRect);
4419 if ( aSet.HasItem(RES_BACKGROUND) )
4420 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4421 else
4422 rFormat.SetFormatAttr(aSet);
4426 wwULSpaceData aULData;
4427 GetPageULData(rSection, aULData);
4428 SetPageULSpaceItems(rFormat, aULData, rSection);
4430 rPage.SetVerticalAdjustment( rSection.mnVerticalAdjustment );
4432 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4434 if (!(rSection.maSep.pgbApplyTo & 1))
4435 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4436 if (!(rSection.maSep.pgbApplyTo & 2))
4437 SwWW8ImplReader::SetPageBorder(rPage.GetFirstMaster(), rSection);
4439 mrReader.SetDocumentGrid(rFormat, rSection);
4442 void wwSectionManager::SetUseOn(wwSection &rSection)
4444 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4445 mrReader.m_xWDop->doptypography.m_f2on1;
4447 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4448 UseOnPage eUse = eUseBase;
4449 if (!mrReader.m_xWDop->fFacingPages)
4450 eUse |= UseOnPage::HeaderShare | UseOnPage::FooterShare;
4451 if (!rSection.HasTitlePage())
4452 eUse |= UseOnPage::FirstShare;
4454 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4455 if (rSection.mpPage)
4456 rSection.mpPage->WriteUseOn(eUse);
4460 * Set the page descriptor on this node, handle the different cases for a text
4461 * node or a table
4463 static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4464 SwDoc &rDoc)
4467 If it's a table here, apply the pagebreak to the table
4468 properties, otherwise we add it to the para at this
4469 position
4471 if (rIdx.GetNode().IsTableNode())
4473 SwTable& rTable =
4474 rIdx.GetNode().GetTableNode()->GetTable();
4475 SwFrameFormat* pApply = rTable.GetFrameFormat();
4476 OSL_ENSURE(pApply, "impossible");
4477 if (pApply)
4478 pApply->SetFormatAttr(rPgDesc);
4480 else
4482 SwPaM aPage(rIdx);
4483 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4488 * Map a word section to a writer page descriptor
4490 SwFormatPageDesc wwSectionManager::SetSwFormatPageDesc(mySegIter const &rIter,
4491 mySegIter const &rStart, bool bIgnoreCols)
4493 if (mrReader.m_bNewDoc && rIter == rStart)
4495 rIter->mpPage =
4496 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD);
4498 else
4500 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4501 SwViewShell::GetShellRes()->GetPageDescName(mnDesc, ShellResource::NORMAL_PAGE),
4502 nullptr, false);
4504 OSL_ENSURE(rIter->mpPage, "no page!");
4505 if (!rIter->mpPage)
4506 return SwFormatPageDesc();
4508 // Set page before hd/ft
4509 const wwSection *pPrevious = nullptr;
4510 if (rIter != rStart)
4511 pPrevious = &(*(rIter-1));
4512 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4513 SetUseOn(*rIter);
4515 // Set hd/ft after set page
4516 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4518 SwFormatPageDesc aRet(rIter->mpPage);
4520 rIter->mpPage->SetFollow(rIter->mpPage);
4522 if (rIter->PageRestartNo())
4523 aRet.SetNumOffset(rIter->PageStartAt());
4525 ++mnDesc;
4526 return aRet;
4529 void wwSectionManager::InsertSegments()
4531 mySegIter aEnd = maSegments.end();
4532 mySegIter aStart = maSegments.begin();
4533 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4535 // If the section is of type "New column" (0x01), then simply insert a column break.
4536 // But only if there actually are columns on the page, otherwise a column break
4537 // seems to be handled like a page break by MSO.
4538 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4540 SwPaM start( aIter->maStart );
4541 mrReader.m_rDoc.getIDocumentContentOperations().InsertPoolItem( start, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
4542 continue;
4545 mySegIter aNext = aIter+1;
4546 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4548 // If two following sections are different in following properties, Word will interpret a continuous
4549 // section break between them as if it was a section break next page.
4550 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4551 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4553 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4554 bool bInsertPageDesc = !bInsertSection;
4555 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4557 if (bInsertPageDesc)
4560 If a cont section follows this section then we won't be
4561 creating a page desc with 2+ cols as we cannot host a one
4562 col section in a 2+ col pagedesc and make it look like
4563 word. But if the current section actually has columns then
4564 we are forced to insert a section here as well as a page
4565 descriptor.
4568 bool bIgnoreCols = bInsertSection;
4569 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4570 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4571 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4572 (aIter->IsLandScape() == aNext->IsLandScape()));
4574 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4576 bIgnoreCols = true;
4577 if ((aIter->NoCols() > 1) || bProtected)
4578 bInsertSection = true;
4581 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4582 if (!aDesc.GetPageDesc())
4583 continue;
4585 // special case handling for odd/even section break
4586 // a) as before create a new page style for the section break
4587 // b) set Layout of generated page style to right/left ( according
4588 // to section break odd/even )
4589 // c) create a new style to follow the break page style
4590 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4592 // SetSwFormatPageDesc calls some methods that could
4593 // modify aIter (e.g. wwSection ).
4594 // Since we call SetSwFormatPageDesc below to generate the
4595 // 'Following' style of the Break style, it is safer
4596 // to take a copy of the contents of aIter.
4597 wwSection aTmpSection = *aIter;
4598 // create a new following page style
4599 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4600 // restore any contents of aIter trashed by SetSwFormatPageDesc
4601 *aIter = std::move(aTmpSection);
4603 // Handle the section break
4604 UseOnPage eUseOnPage = UseOnPage::Left;
4605 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4606 eUseOnPage = UseOnPage::Right;
4608 // Keep the share flags.
4609 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4610 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4613 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4614 // unless it is needed to specify a page number.
4615 if (aIter != aStart || aDesc.GetNumOffset())
4616 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4619 SwTextNode* pTextNd = nullptr;
4620 if (bInsertSection)
4622 // Start getting the bounds of this section
4623 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4624 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4625 if (aNext != aEnd)
4627 aAnchor = aNext->maStart;
4628 aSectPaM.GetPoint()->Assign(aAnchor);
4629 aSectPaM.Move(fnMoveBackward);
4632 const SwPosition* pPos = aSectPaM.GetPoint();
4633 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4634 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4635 if (pTableNd)
4637 pTextNd =
4638 mrReader.m_rDoc.GetNodes().MakeTextNode(aAnchor.GetNode(),
4639 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
4641 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4644 aSectPaM.SetMark();
4646 aSectPaM.GetPoint()->Assign(aIter->maStart);
4648 bool bHasOwnHdFt = false;
4650 In this nightmare scenario the continuous section has its own
4651 headers and footers so we will try and find a hard page break
4652 between here and the end of the section and put the headers and
4653 footers there.
4655 if (!bInsertPageDesc)
4657 bHasOwnHdFt =
4658 mrReader.HasOwnHeaderFooter(
4659 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4660 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4663 if (bHasOwnHdFt)
4665 // #i40766# Need to cache the page descriptor in case there is
4666 // no page break in the section
4667 SwPageDesc *pOrig = aIter->mpPage;
4668 bool bFailed = true;
4669 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4670 if (aDesc.GetPageDesc())
4672 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4673 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4674 for(; nStart <= nEnd; ++nStart)
4676 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4677 if (!pNode)
4678 continue;
4679 if (sw::util::HasPageBreak(*pNode))
4681 SwNodeIndex aIdx(*pNode);
4682 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4683 bFailed = false;
4684 break;
4688 if(bFailed)
4690 aIter->mpPage = pOrig;
4694 // End getting the bounds of this section, quite a job eh?
4695 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4696 // The last section if continuous is always unbalanced
4697 if (pRet)
4699 // Set the columns to be UnBalanced if that compatibility option is set
4700 if (mrReader.m_xWDop->fNoColumnBalance)
4701 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4702 else
4704 // Otherwise set to unbalanced if the following section is
4705 // not continuous, (which also means that the last section
4706 // is unbalanced)
4707 if (aNext == aEnd || !aNext->IsContinuous())
4708 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4713 if (pTextNd)
4715 SwPaM aTest(*pTextNd);
4716 mrReader.m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4717 pTextNd = nullptr;
4722 void wwExtraneousParas::delete_all_from_doc()
4724 auto aEnd = m_aTextNodes.rend();
4725 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4727 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4728 SwTextNode* pTextNode = rListener.GetTextNode();
4729 rListener.StopListening(pTextNode);
4731 SwPaM aTest(*pTextNode);
4732 m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4734 m_aTextNodes.clear();
4737 void wwExtraneousParas::insert(SwTextNode *pTextNode)
4739 m_aTextNodes.emplace(pTextNode, this);
4742 void wwExtraneousParas::remove_if_present(SwModify* pModify)
4744 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4745 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4746 if (it == m_aTextNodes.end())
4747 return;
4748 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4749 m_aTextNodes.erase(it);
4752 TextNodeListener::TextNodeListener(SwTextNode* pTextNode)
4753 : m_pTextNode(pTextNode)
4755 m_pTextNode->Add(*this);
4758 TextNodeListener::~TextNodeListener()
4760 if (!m_pTextNode)
4761 return;
4762 StopListening(m_pTextNode);
4765 void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4767 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4768 return;
4769 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4770 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4771 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4772 // indicates an underlying bug.
4773 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4774 removed(const_cast<SwModify*>(&rModify));
4777 void TextNodeListener::StopListening(SwModify* pTextNode)
4779 pTextNode->Remove(*this);
4780 m_pTextNode = nullptr;
4783 void TextNodeListener::removed(SwModify* pTextNode)
4785 StopListening(pTextNode);
4788 void wwExtraneousParas::ExtraTextNodeListener::removed(SwModify* pTextNode)
4790 m_pOwner->remove_if_present(pTextNode);
4793 void SwWW8ImplReader::StoreMacroCmds()
4795 if (!m_xWwFib->m_lcbCmds)
4796 return;
4798 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4799 if (!bValidPos)
4800 return;
4802 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4804 if (!xRoot.is())
4805 return;
4809 uno::Reference < io::XStream > xStream =
4810 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4811 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4813 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4814 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4815 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4816 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4818 catch (...)
4823 void SwWW8ImplReader::ReadDocVars()
4825 std::vector<OUString> aDocVarStrings;
4826 std::vector<ww::bytes> aDocVarStringIds;
4827 std::vector<OUString> aDocValueStrings;
4828 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4829 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4830 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4831 if (m_bVer67) return;
4833 uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4834 uno::Reference<css::lang::XMultiServiceFactory> xTextFactory(m_pDocShell->GetModel(), uno::UNO_QUERY);
4835 uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
4836 for(size_t i = 0; i < aDocVarStrings.size(); i++)
4838 const OUString &rName = aDocVarStrings[i];
4839 uno::Any aValue;
4840 if (aDocValueStrings.size() > i)
4842 OUString value = aDocValueStrings[i];
4843 value = value.replaceAll("\r\n", "\n");
4844 value = value.replaceAll("\r", "\n");
4845 // fdo48097-1.doc is an example of a case needing sanitizeStringSurrogates
4846 aValue <<= comphelper::string::sanitizeStringSurrogates(value);
4849 uno::Reference< beans::XPropertySet > xMaster;
4850 OUString sFieldMasterService("com.sun.star.text.FieldMaster.User." + rName);
4852 // Find or create Field Master
4853 if (xFieldMasterAccess->hasByName(sFieldMasterService))
4855 xMaster.set(xFieldMasterAccess->getByName(sFieldMasterService), uno::UNO_QUERY_THROW);
4857 else
4859 xMaster.set(xTextFactory->createInstance(u"com.sun.star.text.FieldMaster.User"_ustr), uno::UNO_QUERY_THROW);
4860 xMaster->setPropertyValue(u"Name"_ustr, uno::Any(rName));
4862 xMaster->setPropertyValue(u"Content"_ustr, aValue);
4867 * Document Info
4869 void SwWW8ImplReader::ReadDocInfo()
4871 if( !m_pStg )
4872 return;
4874 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4875 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4876 uno::Reference<document::XDocumentProperties> xDocProps(
4877 xDPS->getDocumentProperties());
4878 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4880 if (!xDocProps.is())
4881 return;
4883 if ( m_xWwFib->m_fDot )
4885 SfxMedium* pMedium = m_pDocShell->GetMedium();
4886 if ( pMedium )
4888 const OUString& aName = pMedium->GetName();
4889 INetURLObject aURL( aName );
4890 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4891 if ( !sTemplateURL.isEmpty() )
4892 xDocProps->setTemplateURL( sTemplateURL );
4895 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4897 auto nCur = m_pTableStream->Tell();
4898 Sttb aSttb;
4899 // point at tgc record
4900 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4901 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4902 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4903 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4904 OUString aURL;
4905 // attempt to convert to url (won't work for obvious reasons on linux)
4906 if ( !sPath.isEmpty() )
4907 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4908 if (aURL.isEmpty())
4909 xDocProps->setTemplateURL( aURL );
4910 else
4911 xDocProps->setTemplateURL( sPath );
4914 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4917 static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4919 if ( !xPrjNameCache.is() )
4920 return;
4922 INetURLObject aObj;
4923 aObj.SetURL( sTemplatePathOrURL );
4924 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4925 OUString aURL;
4926 if ( bIsURL )
4927 aURL = sTemplatePathOrURL;
4928 else
4930 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4931 aObj.SetURL( aURL );
4935 OUString templateNameWithExt = aObj.GetLastName();
4936 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4937 if ( nIndex != -1 )
4939 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4940 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4943 catch( const uno::Exception& )
4948 namespace {
4950 class WW8Customizations
4952 SvStream* mpTableStream;
4953 WW8Fib mWw8Fib;
4954 public:
4955 WW8Customizations( SvStream*, WW8Fib const & );
4956 void Import( SwDocShell* pShell );
4961 WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4965 void WW8Customizations::Import( SwDocShell* pShell )
4967 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4968 return;
4971 Tcg aTCG;
4972 sal_uInt64 nCur = mpTableStream->Tell();
4973 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4975 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4976 return;
4978 bool bReadResult = aTCG.Read( *mpTableStream );
4979 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4980 if ( !bReadResult )
4982 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4983 return;
4985 aTCG.ImportCustomToolBar( *pShell );
4987 catch(...)
4989 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4993 void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4995 if (m_bFuzzing)
4996 return;
4998 SvtPathOptions aPathOpt;
4999 const OUString& aAddinPath = aPathOpt.GetAddinPath();
5000 uno::Sequence< OUString > sGlobalTemplates;
5002 // first get the autoload addins in the directory STARTUP
5003 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
5005 if( xSFA->isFolder( aAddinPath ) )
5006 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
5008 for (const auto& rGlobalTemplate : sGlobalTemplates)
5010 INetURLObject aObj;
5011 aObj.SetURL( rGlobalTemplate );
5012 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
5013 OUString aURL;
5014 if ( bIsURL )
5015 aURL = rGlobalTemplate;
5016 else
5017 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
5018 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
5019 continue; // don't try and read the same document as ourselves
5021 rtl::Reference<SotStorage> rRoot = new SotStorage(aURL, StreamMode::STD_READWRITE);
5023 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5024 // Import vba via oox filter
5025 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5026 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
5027 // Read toolbars & menus
5028 rtl::Reference<SotStorageStream> refMainStream = rRoot->OpenSotStream(u"WordDocument"_ustr);
5029 refMainStream->SetEndian(SvStreamEndian::LITTLE);
5030 WW8Fib aWwFib( *refMainStream, 8 );
5031 rtl::Reference<SotStorageStream> xTableStream =
5032 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table, StreamMode::STD_READ);
5034 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5036 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5037 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5038 aGblCustomisations.Import( m_pDocShell );
5043 ErrCode SwWW8ImplReader::CoreLoad(WW8Glossary const *pGloss)
5045 m_rDoc.SetDocumentType( SwDoc::DOCTYPE_MSWORD );
5046 if (m_bNewDoc && m_pStg && !pGloss)
5048 // Initialize RDF metadata, to be able to add statements during the import.
5051 rtl::Reference<SwXTextDocument> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5052 const uno::Reference<uno::XComponentContext>& xComponentContext(comphelper::getProcessComponentContext());
5053 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5054 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, static_cast<SfxBaseModel*>(xModel.get()), m_sBaseURL));
5055 uno::Reference<task::XInteractionHandler> xHandler;
5056 xModel->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5058 catch (const uno::Exception&)
5060 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5062 ReadDocInfo();
5065 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5067 if (m_xWwFib->m_fReadOnlyRecommended)
5068 pFibData->setReadOnlyRecommended(true);
5069 else
5070 pFibData->setReadOnlyRecommended(false);
5072 if (m_xWwFib->m_fWriteReservation)
5073 pFibData->setWriteReservation(true);
5074 else
5075 pFibData->setWriteReservation(false);
5077 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5079 ::sw::tExternalDataPointer pSttbfAsoc
5080 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5082 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5084 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5086 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5087 if (pDocShell)
5088 pDocShell->SetReadOnlyUI();
5091 m_pPaM = mpCursor.get();
5093 m_xCtrlStck.reset(new SwWW8FltControlStack(m_rDoc, m_nFieldFlags, *this));
5095 m_xRedlineStack.reset(new sw::util::RedlineStack(m_rDoc));
5098 RefFieldStck: Keeps track of bookmarks which may be inserted as
5099 variables instead.
5101 m_xReffedStck.reset(new SwWW8ReferencedFltEndStack(m_rDoc, m_nFieldFlags));
5102 m_xReffingStck.reset(new SwWW8FltRefStack(m_rDoc, m_nFieldFlags));
5104 m_xAnchorStck.reset(new SwWW8FltAnchorStack(m_rDoc, m_nFieldFlags));
5106 size_t nPageDescOffset = m_rDoc.GetPageDescCnt();
5108 RedlineFlags eMode = RedlineFlags::ShowInsert | RedlineFlags::ShowDelete;
5110 m_oSprmParser.emplace(*m_xWwFib);
5112 // Set handy helper variables
5113 m_bVer6 = (6 == m_xWwFib->m_nVersion);
5114 m_bVer7 = (7 == m_xWwFib->m_nVersion);
5115 m_bVer67 = m_bVer6 || m_bVer7;
5116 m_bVer8 = (8 == m_xWwFib->m_nVersion);
5118 m_eTextCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chse, m_xWwFib->m_lid);
5119 m_eStructCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chseTables, m_xWwFib->m_lid);
5121 m_bWWBugNormal = m_xWwFib->m_nProduct == 0xc03d;
5123 m_xProgress.reset(new ImportProgress(m_pDocShell, 0, 100));
5125 // read Font Table
5126 m_xFonts.reset(new WW8Fonts(*m_pTableStream, *m_xWwFib));
5128 // Document Properties
5129 m_xWDop.reset(new WW8Dop(*m_pTableStream, m_xWwFib->m_nFib, m_xWwFib->m_fcDop,
5130 m_xWwFib->m_lcbDop));
5132 if (m_bNewDoc)
5133 ImportDop();
5136 Import revisioning data: author names
5138 if( m_xWwFib->m_lcbSttbfRMark )
5140 ReadRevMarkAuthorStrTabl(*m_pTableStream,
5141 m_xWwFib->m_fcSttbfRMark,
5142 m_xWwFib->m_lcbSttbfRMark, m_rDoc);
5145 // Initialize our String/ID map for Linked Sections
5146 std::vector<OUString> aLinkStrings;
5147 std::vector<ww::bytes> aStringIds;
5149 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcSttbFnm,
5150 m_xWwFib->m_lcbSttbFnm, m_bVer67 ? 2 : 0, m_eStructCharSet,
5151 aLinkStrings, &aStringIds);
5153 for (size_t i=0; i < aLinkStrings.size() && i < aStringIds.size(); ++i)
5155 const ww::bytes& stringId = aStringIds[i];
5156 if (stringId.size() < sizeof(WW8_STRINGID))
5158 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: WW8_STRINGID is too short");
5159 continue;
5161 const WW8_STRINGID *stringIdStruct = reinterpret_cast<const WW8_STRINGID*>(stringId.data());
5162 m_aLinkStringMap[SVBT16ToUInt16(stringIdStruct->nStringId)] = aLinkStrings[i];
5165 ReadDocVars(); // import document variables as meta information.
5167 m_xProgress->Update(m_nProgress); // Update
5169 m_xLstManager.reset(new WW8ListManager(*m_pTableStream, *this));
5172 first (1) import all styles (see WW8PAR2.CXX)
5173 BEFORE the import of the lists !!
5175 m_xProgress->Update(m_nProgress); // Update
5176 m_xStyles.reset(new WW8RStyle(*m_xWwFib, this)); // Styles
5177 m_xStyles->Import();
5180 In the end: (also see WW8PAR3.CXX)
5182 Go through all Styles and attach respective List Format
5183 AFTER we imported the Styles and AFTER we imported the Lists!
5185 m_xProgress->Update(m_nProgress); // Update
5186 m_xStyles->PostProcessStyles();
5188 if (!m_vColl.empty())
5189 SetOutlineStyles();
5191 m_xSBase.reset(new WW8ScannerBase(m_pStrm,m_pTableStream,m_pDataStream, m_xWwFib.get()));
5193 if (m_xSBase->AreThereFootnotes())
5195 static const SwFootnoteNum eNumA[4] =
5197 FTNNUM_DOC, FTNNUM_CHAPTER, FTNNUM_PAGE, FTNNUM_DOC
5200 SwFootnoteInfo aInfo = m_rDoc.GetFootnoteInfo(); // Copy-Ctor private
5202 aInfo.m_ePos = FTNPOS_PAGE;
5203 aInfo.m_eNum = eNumA[m_xWDop->rncFootnote];
5204 sal_uInt16 nfcFootnoteRef = m_xWDop->nfcFootnoteRef;
5205 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcFootnoteRef));
5206 if( m_xWDop->nFootnote )
5207 aInfo.m_nFootnoteOffset = m_xWDop->nFootnote - 1;
5208 m_rDoc.SetFootnoteInfo( aInfo );
5210 if (m_xSBase->AreThereEndnotes())
5212 SwEndNoteInfo aInfo = m_rDoc.GetEndNoteInfo(); // Same as for Footnote
5213 sal_uInt16 nfcEdnRef = m_xWDop->nfcEdnRef;
5214 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcEdnRef));
5215 if( m_xWDop->nEdn )
5216 aInfo.m_nFootnoteOffset = m_xWDop->nEdn - 1;
5217 m_rDoc.SetEndNoteInfo( aInfo );
5220 if (m_xWwFib->m_lcbPlcfhdd)
5221 m_xHdFt.reset(new WW8PLCF_HdFt(m_pTableStream, *m_xWwFib, *m_xWDop));
5223 if (!m_bNewDoc)
5225 // inserting into an existing document:
5226 // As only complete paragraphs are inserted, the current one
5227 // needs to be split - once or even twice.
5228 const SwPosition* pPos = m_pPaM->GetPoint();
5230 // split current paragraph to get new paragraph for the insertion
5231 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5233 // another split, if insertion position was not at the end of the current paragraph.
5234 SwTextNode const*const pTextNd = pPos->GetNode().GetTextNode();
5235 if ( pTextNd->GetText().getLength() )
5237 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5238 // move PaM back to the newly empty paragraph
5239 m_pPaM->Move( fnMoveBackward );
5242 // suppress insertion of tables inside footnotes.
5243 const SwNodeOffset nNd = pPos->GetNodeIndex();
5244 m_bReadNoTable = ( nNd < m_rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
5245 m_rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd );
5248 m_xProgress->Update(m_nProgress); // Update
5250 // loop for each glossary entry and add dummy section node
5251 if (pGloss)
5253 WW8PLCF aPlc(*m_pTableStream, m_xWwFib->m_fcPlcfglsy, m_xWwFib->m_lcbPlcfglsy, 0);
5255 WW8_CP nStart, nEnd;
5256 void* pDummy;
5258 for (int i = 0; i < pGloss->GetNoStrings(); ++i, aPlc.advance())
5260 SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfContent());
5261 SwTextFormatColl* pColl =
5262 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,
5263 false);
5264 SwStartNode *pNode =
5265 m_rDoc.GetNodes().MakeTextSection(aIdx.GetNode(),
5266 SwNormalStartNode,pColl);
5267 m_pPaM->GetPoint()->Assign( pNode->GetIndex()+1 );
5268 aPlc.Get( nStart, nEnd, pDummy );
5269 ReadText(nStart,nEnd-nStart-1,MAN_MAINTEXT);
5272 else // ordinary case
5274 if (m_bNewDoc && m_pStg) /*meaningless for a glossary */
5276 m_pDocShell->SetIsTemplate( m_xWwFib->m_fDot ); // point at tgc record
5277 uno::Reference<document::XDocumentPropertiesSupplier> const
5278 xDocPropSupp(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
5279 uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW );
5281 OUString sCreatedFrom = xDocProps->getTemplateURL();
5282 uno::Reference< container::XNameContainer > xPrjNameCache;
5283 uno::Reference< lang::XMultiServiceFactory> xSF(m_pDocShell->GetModel(), uno::UNO_QUERY);
5284 if ( xSF.is() )
5285 xPrjNameCache.set( xSF->createInstance( u"ooo.vba.VBAProjectNameProvider"_ustr ), uno::UNO_QUERY );
5287 // Read Global templates
5288 ReadGlobalTemplateSettings( sCreatedFrom, xPrjNameCache );
5290 // Create and insert Word vba Globals
5291 uno::Any aGlobs;
5292 uno::Sequence< uno::Any > aArgs{ uno::Any(m_pDocShell->GetModel()) };
5295 aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( u"ooo.vba.word.Globals"_ustr, aArgs );
5297 catch (const uno::Exception&)
5299 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: ooo.vba.word.Globals is not available");
5302 #if HAVE_FEATURE_SCRIPTING
5303 if (!m_bFuzzing)
5305 BasicManager *pBasicMan = m_pDocShell->GetBasicManager();
5306 if (pBasicMan)
5307 pBasicMan->SetGlobalUNOConstant( u"VBAGlobals"_ustr, aGlobs );
5309 #endif
5310 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5311 // Import vba via oox filter
5312 bool bRet = aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5314 lcl_createTemplateToProjectEntry( xPrjNameCache, sCreatedFrom, aBasicImporter.getProjectName() );
5315 WW8Customizations aCustomisations( m_pTableStream, *m_xWwFib );
5316 aCustomisations.Import( m_pDocShell );
5318 if( bRet )
5319 m_rDoc.SetContainsMSVBasic(true);
5321 StoreMacroCmds();
5323 ReadText(0, m_xWwFib->m_ccpText, MAN_MAINTEXT);
5326 m_xProgress->Update(m_nProgress); // Update
5328 if (m_pDrawPg && m_xMSDffManager && m_xMSDffManager->GetShapeOrders())
5330 // Helper array to chain the inserted frames (instead of SdrTextObj)
5331 SvxMSDffShapeTxBxSort aTxBxSort;
5333 // Ensure correct z-order of read Escher objects
5334 sal_uInt16 nShapeCount = m_xMSDffManager->GetShapeOrders()->size();
5336 for (sal_uInt16 nShapeNum=0; nShapeNum < nShapeCount; nShapeNum++)
5338 SvxMSDffShapeOrder *pOrder =
5339 (*m_xMSDffManager->GetShapeOrders())[nShapeNum].get();
5340 // Insert Pointer into new Sort array
5341 if (pOrder->nTxBxComp && pOrder->pFly)
5342 aTxBxSort.insert(pOrder);
5344 // Chain Frames
5345 if( !aTxBxSort.empty() )
5347 SwFormatChain aChain;
5348 for( SvxMSDffShapeTxBxSort::iterator it = aTxBxSort.begin(); it != aTxBxSort.end(); ++it )
5350 SvxMSDffShapeOrder *pOrder = *it;
5352 // Initialize FlyFrame Formats
5353 SwFlyFrameFormat* pFlyFormat = pOrder->pFly;
5354 SwFlyFrameFormat* pNextFlyFormat = nullptr;
5355 SwFlyFrameFormat* pPrevFlyFormat = nullptr;
5357 // Determine successor, if we can
5358 SvxMSDffShapeTxBxSort::iterator tmpIter1 = it;
5359 ++tmpIter1;
5360 if( tmpIter1 != aTxBxSort.end() )
5362 SvxMSDffShapeOrder *pNextOrder = *tmpIter1;
5363 if ((0xFFFF0000 & pOrder->nTxBxComp)
5364 == (0xFFFF0000 & pNextOrder->nTxBxComp))
5365 pNextFlyFormat = pNextOrder->pFly;
5367 // Determine predecessor, if we can
5368 if( it != aTxBxSort.begin() )
5370 SvxMSDffShapeTxBxSort::iterator tmpIter2 = it;
5371 --tmpIter2;
5372 SvxMSDffShapeOrder *pPrevOrder = *tmpIter2;
5373 if ((0xFFFF0000 & pOrder->nTxBxComp)
5374 == (0xFFFF0000 & pPrevOrder->nTxBxComp))
5375 pPrevFlyFormat = pPrevOrder->pFly;
5377 // If successor or predecessor present, insert the
5378 // chain at the FlyFrame Format
5379 if (pNextFlyFormat || pPrevFlyFormat)
5381 aChain.SetNext( pNextFlyFormat );
5382 aChain.SetPrev( pPrevFlyFormat );
5383 pFlyFormat->SetFormatAttr( aChain );
5389 bool isHideRedlines(false);
5391 if (m_bNewDoc)
5393 if( m_xWDop->fRevMarking )
5394 eMode |= RedlineFlags::On;
5395 isHideRedlines = !m_xWDop->fRMView;
5398 m_aInsertedTables.DelAndMakeTableFrames();
5399 m_aSectionManager.InsertSegments();
5401 m_vColl.clear();
5403 m_xStyles.reset();
5405 m_xFormImpl.reset();
5406 GraphicDtor();
5407 m_xMSDffManager.reset();
5408 m_xHdFt.reset();
5409 m_xSBase.reset();
5410 m_xWDop.reset();
5411 m_xFonts.reset();
5412 m_xAtnNames.reset();
5413 m_oSprmParser.reset();
5414 m_xProgress.reset();
5416 m_pDataStream = nullptr;
5417 m_pTableStream = nullptr;
5419 DeleteCtrlStack();
5420 DeleteAnchorStack();
5421 DeleteRefStacks();
5422 m_oLastAnchorPos.reset();//ensure this is deleted before UpdatePageDescs
5423 // ofz#10994 remove any trailing fly paras before processing redlines
5424 m_xWFlyPara.reset();
5425 // ofz#12660 remove any trailing fly paras before deleting extra paras
5426 m_xSFlyPara.reset();
5427 // remove extra paragraphs after attribute ctrl
5428 // stacks etc. are destroyed, and before fields
5429 // are updated
5430 m_aExtraneousParas.delete_all_from_doc();
5431 m_xRedlineStack->closeall(*m_pPaM->GetPoint());
5433 // For i120928,achieve the graphics from the special bookmark with is for graphic bullet
5435 std::vector<const SwGrfNode*> vecBulletGrf;
5436 std::vector<SwFrameFormat*> vecFrameFormat;
5438 IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
5439 if ( pMarkAccess )
5441 auto ppBkmk = pMarkAccess->findBookmark( u"_PictureBullets"_ustr );
5442 if ( ppBkmk != pMarkAccess->getBookmarksEnd() &&
5443 IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::BOOKMARK )
5445 SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().GetNode().GetTextNode();
5447 if ( pTextNode )
5449 const SwpHints* pHints = pTextNode->GetpSwpHints();
5450 for( size_t nHintPos = 0; pHints && nHintPos < pHints->Count(); ++nHintPos)
5452 const SwTextAttr *pHt = pHints->Get(nHintPos);
5453 if (pHt->Which() != RES_TXTATR_FLYCNT)
5454 continue;
5455 const sal_Int32 st = pHt->GetStart();
5456 if (st >= (*ppBkmk)->GetMarkStart().GetContentIndex())
5458 SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
5459 vecFrameFormat.push_back(pFrameFormat);
5460 const SwNodeIndex* pNdIdx = pFrameFormat->GetContent().GetContentIdx();
5461 const SwNodes* pNodesArray = (pNdIdx != nullptr)
5462 ? &(pNdIdx->GetNodes())
5463 : nullptr;
5464 const SwGrfNode *pGrf = (pNodesArray != nullptr)
5465 ? (*pNodesArray)[pNdIdx->GetIndex() + 1]->GetGrfNode()
5466 : nullptr;
5467 vecBulletGrf.push_back(pGrf);
5470 // update graphic bullet information
5471 size_t nCount = m_xLstManager->GetWW8LSTInfoNum();
5472 for (size_t i = 0; i < nCount; ++i)
5474 SwNumRule* pRule = m_xLstManager->GetNumRule(i);
5475 for (sal_uInt16 j = 0; j < MAXLEVEL; ++j)
5477 SwNumFormat aNumFormat(pRule->Get(j));
5478 const sal_Int16 nType = aNumFormat.GetNumberingType();
5479 const sal_uInt16 nGrfBulletCP = aNumFormat.GetGrfBulletCP();
5480 if ( nType == SVX_NUM_BITMAP
5481 && vecBulletGrf.size() > nGrfBulletCP
5482 && vecBulletGrf[nGrfBulletCP] != nullptr )
5484 Graphic aGraphic = vecBulletGrf[nGrfBulletCP]->GetGrf();
5485 SvxBrushItem aBrush(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
5486 const vcl::Font& aFont = numfunc::GetDefBulletFont();
5487 int nHeight = aFont.GetFontHeight() * 12;
5488 Size aPrefSize( aGraphic.GetPrefSize());
5489 if (aPrefSize.Height() * aPrefSize.Width() != 0 )
5491 int nWidth = (nHeight * aPrefSize.Width()) / aPrefSize.Height();
5492 Size aSize(nWidth, nHeight);
5493 aNumFormat.SetGraphicBrush(&aBrush, &aSize);
5495 else
5497 aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
5498 aNumFormat.SetBulletChar(0x2190);
5500 pRule->Set( j, aNumFormat );
5504 // Remove additional pictures
5505 for (SwFrameFormat* p : vecFrameFormat)
5507 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat(p);
5512 m_xLstManager.reset();
5515 m_oPosAfterTOC.reset();
5516 m_xRedlineStack.reset();
5517 mpCursor.reset();
5518 m_pPaM = nullptr;
5520 UpdateFields();
5522 // delete the pam before the call for hide all redlines (Bug 73683)
5523 if (m_bNewDoc)
5524 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(eMode);
5526 UpdatePageDescs(m_rDoc, nPageDescOffset);
5528 // can't set it on the layout or view shell because it doesn't exist yet
5529 m_rDoc.GetDocumentRedlineManager().SetHideRedlines(isHideRedlines);
5531 return ERRCODE_NONE;
5534 ErrCode SwWW8ImplReader::SetSubStreams(rtl::Reference<SotStorageStream>& rTableStream,
5535 rtl::Reference<SotStorageStream>& rDataStream)
5537 ErrCode nErrRet = ERRCODE_NONE;
5538 // 6 stands for "6 OR 7", 7 stands for "ONLY 7"
5539 switch (m_xWwFib->m_nVersion)
5541 case 6:
5542 case 7:
5543 m_pTableStream = m_pStrm;
5544 m_pDataStream = m_pStrm;
5545 break;
5546 case 8:
5547 if(!m_pStg)
5549 OSL_ENSURE( m_pStg, "Version 8 always needs to have a Storage!!" );
5550 nErrRet = ERR_SWG_READ_ERROR;
5551 break;
5554 rTableStream = m_pStg->OpenSotStream(
5555 m_xWwFib->m_fWhichTableStm ? SL::a1Table : SL::a0Table,
5556 StreamMode::STD_READ);
5558 m_pTableStream = rTableStream.get();
5559 m_pTableStream->SetEndian( SvStreamEndian::LITTLE );
5561 rDataStream = m_pStg->OpenSotStream(SL::aData, StreamMode::STD_READ);
5563 if (rDataStream.is() && ERRCODE_NONE == rDataStream->GetError())
5565 m_pDataStream = rDataStream.get();
5566 m_pDataStream->SetEndian(SvStreamEndian::LITTLE);
5568 else
5569 m_pDataStream = m_pStrm;
5570 break;
5571 default:
5572 // Program error!
5573 OSL_ENSURE( false, "We forgot to encode nVersion!" );
5574 nErrRet = ERR_SWG_READ_ERROR;
5575 break;
5577 return nErrRet;
5580 namespace
5582 SvStream* MakeTemp(std::optional<utl::TempFileFast>& roTempFile)
5584 roTempFile.emplace();
5585 return roTempFile->GetStream(StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
5588 #define WW_BLOCKSIZE 0x200
5590 void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut)
5592 const std::size_t nLen = rIn.TellEnd();
5593 rIn.Seek(0);
5595 sal_uInt8 in[WW_BLOCKSIZE];
5596 for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
5598 std::size_t nBS = std::min<size_t>(nLen - nI, WW_BLOCKSIZE);
5599 nBS = rIn.ReadBytes(in, nBS);
5600 rCtx.InitCipher(nBlock);
5601 rCtx.Decode(in, nBS, in, nBS);
5602 rOut.WriteBytes(in, nBS);
5606 void DecryptXOR(msfilter::MSCodec_XorWord95 &rCtx, SvStream &rIn, SvStream &rOut)
5608 std::size_t nSt = rIn.Tell();
5609 std::size_t nLen = rIn.TellEnd();
5611 rCtx.InitCipher();
5612 rCtx.Skip(nSt);
5614 sal_uInt8 in[0x4096];
5615 for (std::size_t nI = nSt; nI < nLen; nI += 0x4096)
5617 std::size_t nBS = std::min<size_t>(nLen - nI, 0x4096 );
5618 nBS = rIn.ReadBytes(in, nBS);
5619 rCtx.Decode(in, nBS);
5620 rOut.WriteBytes(in, nBS);
5624 // moan, copy and paste :-(
5625 OUString QueryPasswordForMedium(SfxMedium& rMedium)
5627 OUString aPassw;
5629 if (const SfxStringItem* pPasswordItem = rMedium.GetItemSet().GetItemIfSet(SID_PASSWORD))
5630 aPassw = pPasswordItem->GetValue();
5631 else
5635 uno::Reference< task::XInteractionHandler > xHandler( rMedium.GetInteractionHandler() );
5636 if( xHandler.is() )
5638 rtl::Reference<::comphelper::DocPasswordRequest> pRequest = new ::comphelper::DocPasswordRequest(
5639 ::comphelper::DocPasswordRequestType::MS, task::PasswordRequestMode_PASSWORD_ENTER,
5640 INetURLObject(rMedium.GetOrigURL())
5641 .GetLastName(INetURLObject::DecodeMechanism::WithCharset));
5643 xHandler->handle( pRequest );
5645 if( pRequest->isPassword() )
5646 aPassw = pRequest->getPassword();
5649 catch( const uno::Exception& )
5654 return aPassw;
5657 uno::Sequence< beans::NamedValue > InitXorWord95Codec( ::msfilter::MSCodec_XorWord95& rCodec, SfxMedium& rMedium, WW8Fib const * pWwFib )
5659 uno::Sequence< beans::NamedValue > aEncryptionData;
5660 const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
5661 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5662 aEncryptionData.realloc( 0 );
5664 if ( !aEncryptionData.hasElements() )
5666 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5668 OString sPassword(OUStringToOString(sUniPassword,
5669 WW8Fib::GetFIBCharset(pWwFib->m_chseTables, pWwFib->m_lid)));
5671 sal_Int32 nLen = sPassword.getLength();
5672 if( nLen <= 15 )
5674 sal_uInt8 pPassword[16];
5675 memcpy(pPassword, sPassword.getStr(), nLen);
5676 memset(pPassword+nLen, 0, sizeof(pPassword)-nLen);
5678 rCodec.InitKey( pPassword );
5679 aEncryptionData = rCodec.GetEncryptionData();
5681 // the export supports RC4 algorithm only, so we have to
5682 // generate the related EncryptionData as well, so that Save
5683 // can export the document without asking for a password;
5684 // as result there will be EncryptionData for both algorithms
5685 // in the MediaDescriptor
5686 ::msfilter::MSCodec_Std97 aCodec97;
5688 sal_uInt8 pDocId[ 16 ];
5689 if (rtl_random_getBytes(nullptr, pDocId, 16) != rtl_Random_E_None)
5691 throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
5694 sal_uInt16 pStd97Pass[16] = {};
5695 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5696 pStd97Pass[nChar] = sUniPassword[nChar];
5698 aCodec97.InitKey( pStd97Pass, pDocId );
5700 // merge the EncryptionData, there should be no conflicts
5701 ::comphelper::SequenceAsHashMap aEncryptionHash( aEncryptionData );
5702 aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
5703 aEncryptionHash >> aEncryptionData;
5707 return aEncryptionData;
5710 uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 const pDocId[16], SfxMedium& rMedium)
5712 uno::Sequence< beans::NamedValue > aEncryptionData;
5713 const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
5714 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5715 aEncryptionData.realloc( 0 );
5717 if ( !aEncryptionData.hasElements() )
5719 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5721 sal_Int32 nLen = sUniPassword.getLength();
5722 if ( nLen <= 15 )
5724 sal_uInt16 pPassword[16] = {};
5725 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5726 pPassword[nChar] = sUniPassword[nChar];
5728 rCodec.InitKey( pPassword, pDocId );
5729 aEncryptionData = rCodec.GetEncryptionData();
5733 return aEncryptionData;
5737 //TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different
5738 //stream thing
5739 static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream)
5741 //It is possible there are other variants in existence but these
5742 //are the defaults I get with Word 2013
5744 rStream.ReadUInt32(info.header.flags);
5745 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
5746 return false;
5748 sal_uInt32 nHeaderSize(0);
5749 rStream.ReadUInt32(nHeaderSize);
5750 sal_uInt32 actualHeaderSize = sizeof(info.header);
5752 if (nHeaderSize < actualHeaderSize)
5753 return false;
5755 rStream.ReadUInt32(info.header.flags);
5756 rStream.ReadUInt32(info.header.sizeExtra);
5757 rStream.ReadUInt32(info.header.algId);
5758 rStream.ReadUInt32(info.header.algIdHash);
5759 rStream.ReadUInt32(info.header.keyBits);
5760 rStream.ReadUInt32(info.header.providedType);
5761 rStream.ReadUInt32(info.header.reserved1);
5762 rStream.ReadUInt32(info.header.reserved2);
5764 rStream.SeekRel(nHeaderSize - actualHeaderSize);
5766 rStream.ReadUInt32(info.verifier.saltSize);
5767 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
5768 return false;
5769 rStream.ReadBytes(&info.verifier.salt, sizeof(info.verifier.salt));
5770 rStream.ReadBytes(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
5772 rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize);
5773 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
5774 return false;
5775 rStream.ReadBytes(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
5777 // check flags and algorithm IDs, required are AES128 and SHA-1
5778 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
5779 return false;
5781 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
5782 return false;
5784 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
5785 return false;
5787 // hash algorithm ID 0 defaults to SHA-1 too
5788 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
5789 return false;
5791 return true;
5794 ErrCode SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
5796 ErrCode nErrRet = ERRCODE_NONE;
5797 if (pGloss)
5798 m_xWwFib = pGloss->GetFib();
5799 else
5800 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5802 if (m_xWwFib->m_nFibError)
5803 nErrRet = ERR_SWG_READ_ERROR;
5805 rtl::Reference<SotStorageStream> xTableStream, xDataStream;
5807 if (!nErrRet)
5808 nErrRet = SetSubStreams(xTableStream, xDataStream);
5810 std::optional<utl::TempFileFast> oTempMain;
5811 std::optional<utl::TempFileFast> oTempTable;
5812 std::optional<utl::TempFileFast> oTempData;
5813 SvStream* pDecryptMain = nullptr;
5814 SvStream* pDecryptTable = nullptr;
5815 SvStream* pDecryptData = nullptr;
5817 bool bDecrypt = false;
5818 enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other;
5819 if (m_xWwFib->m_fEncrypted && !nErrRet)
5821 if (!pGloss)
5823 bDecrypt = true;
5824 if (8 != m_xWwFib->m_nVersion)
5825 eAlgo = XOR;
5826 else
5828 if (m_xWwFib->m_nKey != 0)
5829 eAlgo = XOR;
5830 else
5832 m_pTableStream->Seek(0);
5833 sal_uInt32 nEncType(0);
5834 m_pTableStream->ReadUInt32(nEncType);
5835 if (nEncType == msfilter::VERSION_INFO_1997_FORMAT)
5836 eAlgo = RC4;
5837 else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2)
5838 eAlgo = RC4CryptoAPI;
5844 if (bDecrypt)
5846 nErrRet = ERRCODE_SVX_WRONGPASS;
5847 SfxMedium* pMedium = m_pDocShell->GetMedium();
5849 if ( pMedium )
5851 switch (eAlgo)
5853 default:
5854 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5855 break;
5856 case XOR:
5858 msfilter::MSCodec_XorWord95 aCtx;
5859 uno::Sequence< beans::NamedValue > aEncryptionData = InitXorWord95Codec(aCtx, *pMedium, m_xWwFib.get());
5861 // if initialization has failed the EncryptionData should be empty
5862 if (aEncryptionData.hasElements() && aCtx.VerifyKey(m_xWwFib->m_nKey, m_xWwFib->m_nHash))
5864 nErrRet = ERRCODE_NONE;
5865 pDecryptMain = MakeTemp(oTempMain);
5867 m_pStrm->Seek(0);
5868 size_t nUnencryptedHdr =
5869 (8 == m_xWwFib->m_nVersion) ? 0x44 : 0x34;
5870 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5871 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5872 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5873 pIn.reset();
5875 DecryptXOR(aCtx, *m_pStrm, *pDecryptMain);
5877 if (!m_pTableStream || m_pTableStream == m_pStrm)
5878 m_pTableStream = pDecryptMain;
5879 else
5881 pDecryptTable = MakeTemp(oTempTable);
5882 DecryptXOR(aCtx, *m_pTableStream, *pDecryptTable);
5883 m_pTableStream = pDecryptTable;
5886 if (!m_pDataStream || m_pDataStream == m_pStrm)
5887 m_pDataStream = pDecryptMain;
5888 else
5890 pDecryptData = MakeTemp(oTempData);
5891 DecryptXOR(aCtx, *m_pDataStream, *pDecryptData);
5892 m_pDataStream = pDecryptData;
5895 pMedium->GetItemSet().ClearItem( SID_PASSWORD );
5896 pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5899 break;
5900 case RC4:
5901 case RC4CryptoAPI:
5903 std::unique_ptr<msfilter::MSCodec97> xCtx;
5904 msfilter::RC4EncryptionInfo info;
5905 bool bCouldReadHeaders;
5907 if (eAlgo == RC4)
5909 xCtx.reset(new msfilter::MSCodec_Std97);
5910 assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5);
5911 bCouldReadHeaders =
5912 checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) &&
5913 checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) &&
5914 checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5);
5916 else
5918 xCtx.reset(new msfilter::MSCodec_CryptoAPI);
5919 bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream);
5922 // if initialization has failed the EncryptionData should be empty
5923 uno::Sequence< beans::NamedValue > aEncryptionData;
5924 if (bCouldReadHeaders)
5925 aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium);
5926 else
5927 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5928 if (aEncryptionData.hasElements() && xCtx->VerifyKey(info.verifier.encryptedVerifier,
5929 info.verifier.encryptedVerifierHash))
5931 nErrRet = ERRCODE_NONE;
5933 pDecryptMain = MakeTemp(oTempMain);
5935 m_pStrm->Seek(0);
5936 std::size_t nUnencryptedHdr = 0x44;
5937 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5938 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5940 DecryptRC4(*xCtx, *m_pStrm, *pDecryptMain);
5942 pDecryptMain->Seek(0);
5943 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5944 pIn.reset();
5946 pDecryptTable = MakeTemp(oTempTable);
5947 DecryptRC4(*xCtx, *m_pTableStream, *pDecryptTable);
5948 m_pTableStream = pDecryptTable;
5950 if (!m_pDataStream || m_pDataStream == m_pStrm)
5951 m_pDataStream = pDecryptMain;
5952 else
5954 pDecryptData = MakeTemp(oTempData);
5955 DecryptRC4(*xCtx, *m_pDataStream, *pDecryptData);
5956 m_pDataStream = pDecryptData;
5959 pMedium->GetItemSet().ClearItem( SID_PASSWORD );
5960 pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5963 break;
5967 if (nErrRet == ERRCODE_NONE)
5969 m_pStrm = pDecryptMain;
5970 assert(m_pStrm);
5971 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5972 if (m_xWwFib->m_nFibError)
5973 nErrRet = ERR_SWG_READ_ERROR;
5977 if (!nErrRet)
5978 nErrRet = CoreLoad(pGloss);
5980 oTempMain.reset();
5981 oTempTable.reset();
5982 oTempData.reset();
5984 m_xWwFib.reset();
5985 return nErrRet;
5988 void SwWW8ImplReader::SetOutlineStyles()
5990 // If we are inserted into a document then don't clobber existing outline
5991 // levels.
5992 sal_uInt16 nOutlineStyleListLevelWithAssignment = 0;
5993 if (!m_bNewDoc)
5995 ww8::ParaStyles aOutLined(sw::util::GetParaStyles(m_rDoc));
5996 sw::util::SortByAssignedOutlineStyleListLevel(aOutLined);
5997 ww8::ParaStyles::reverse_iterator aEnd = aOutLined.rend();
5998 for ( ww8::ParaStyles::reverse_iterator aIter = aOutLined.rbegin(); aIter < aEnd; ++aIter)
6000 if ((*aIter)->IsAssignedToListLevelOfOutlineStyle())
6001 nOutlineStyleListLevelWithAssignment |= 1 << (*aIter)->GetAssignedOutlineStyleLevel();
6002 else
6003 break;
6007 // Check applied WW8 list styles at WW8 Built-In Heading Styles
6008 // - Choose the list style which occurs most often as the one which provides
6009 // the list level properties for the Outline Style.
6010 // - Populate temporary list of WW8 Built-In Heading Styles for further
6011 // iteration
6012 std::vector<SwWW8StyInf*> aWW8BuiltInHeadingStyles;
6014 sal_uInt16 nStyle = 0;
6015 std::map<const SwNumRule*, int> aWW8ListStyleCounts;
6016 std::map<const SwNumRule*, bool> aPreventUseAsChapterNumbering;
6017 for (SwWW8StyInf& rSI : m_vColl)
6019 // Copy inherited numbering info since LO drops inheritance after ChapterNumbering
6020 // and only applies listLevel via style with the selected ChapterNumbering LFO.
6021 bool bReRegister = false;
6022 if (rSI.m_nBase && rSI.m_nBase < m_vColl.size()
6023 && m_vColl[rSI.m_nBase].HasWW8OutlineLevel())
6025 if (rSI.m_nLFOIndex == USHRT_MAX)
6027 rSI.m_nLFOIndex = m_vColl[rSI.m_nBase].m_nLFOIndex;
6029 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6030 // A style never inherits numbering from Chapter Numbering.
6031 if (rSI.m_nLFOIndex != USHRT_MAX)
6033 const SwNumRule* pNumRule = m_vColl[rSI.m_nBase].m_pOutlineNumrule;
6034 if (pNumRule)
6035 aPreventUseAsChapterNumbering[pNumRule] = true;
6038 if (rSI.m_nListLevel == MAXLEVEL)
6039 rSI.m_nListLevel = m_vColl[rSI.m_nBase].m_nListLevel;
6040 if (rSI.mnWW8OutlineLevel == MAXLEVEL)
6041 rSI.mnWW8OutlineLevel = m_vColl[rSI.m_nBase].mnWW8OutlineLevel;
6042 bReRegister = true;
6045 // Undefined listLevel is treated as the first level with valid numbering rule.
6046 if (rSI.m_nLFOIndex < USHRT_MAX && rSI.m_nListLevel == MAXLEVEL)
6048 rSI.m_nListLevel = 0;
6049 bReRegister = true;
6052 if (bReRegister)
6053 RegisterNumFormatOnStyle(nStyle);
6055 ++nStyle; // increment before the first "continue";
6057 if (!rSI.m_bColl || !rSI.IsWW8BuiltInHeadingStyle() || !rSI.HasWW8OutlineLevel())
6059 continue;
6062 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6063 if (rSI.IsOutlineNumbered() && rSI.m_nListLevel != rSI.mnWW8OutlineLevel)
6065 aPreventUseAsChapterNumbering[rSI.m_pOutlineNumrule] = true;
6068 aWW8BuiltInHeadingStyles.push_back(&rSI);
6070 const SwNumRule* pWW8ListStyle = rSI.GetOutlineNumrule();
6071 if (pWW8ListStyle != nullptr)
6073 std::map<const SwNumRule*, int>::iterator aCountIter
6074 = aWW8ListStyleCounts.find(pWW8ListStyle);
6075 if (aCountIter == aWW8ListStyleCounts.end())
6077 aWW8ListStyleCounts[pWW8ListStyle] = 1;
6079 else
6081 ++(aCountIter->second);
6086 int nCurrentMaxCount = 0;
6087 for (const auto& rEntry : aWW8ListStyleCounts)
6089 if (aPreventUseAsChapterNumbering[rEntry.first])
6090 continue;
6092 if (rEntry.second > nCurrentMaxCount)
6094 nCurrentMaxCount = rEntry.second;
6095 m_pChosenWW8OutlineStyle = rEntry.first;
6100 // - set list level properties of Outline Style - ODF's list style applied
6101 // by default to headings
6102 // - assign corresponding Heading Paragraph Styles to the Outline Style
6103 // - If a heading Paragraph Styles is not applying the WW8 list style which
6104 // had been chosen as
6105 // the one which provides the list level properties for the Outline Style,
6106 // its assignment to
6107 // the Outline Style is removed. A potential applied WW8 list style is
6108 // assigned directly and
6109 // its default outline level is applied.
6110 SwNumRule aOutlineRule(*m_rDoc.GetOutlineNumRule());
6111 if (m_pChosenWW8OutlineStyle)
6113 for (int i = 0; i < WW8ListManager::nMaxLevel; ++i)
6115 // Don't clobber existing outline levels.
6116 const sal_uInt16 nLevel = 1 << i;
6117 if (!(nOutlineStyleListLevelWithAssignment & nLevel))
6118 aOutlineRule.Set(i, m_pChosenWW8OutlineStyle->Get(i));
6122 for (const SwWW8StyInf* pStyleInf : aWW8BuiltInHeadingStyles)
6124 const sal_uInt16 nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6125 = 1 << pStyleInf->mnWW8OutlineLevel;
6126 if (nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6127 & nOutlineStyleListLevelWithAssignment)
6129 continue;
6132 // in case that there are more styles on this level ignore them
6133 nOutlineStyleListLevelWithAssignment
6134 |= nOutlineStyleListLevelOfWW8BuiltInHeadingStyle;
6136 SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pStyleInf->m_pFormat);
6137 if (pStyleInf->GetOutlineNumrule() != m_pChosenWW8OutlineStyle
6138 || (pStyleInf->m_nListLevel < WW8ListManager::nMaxLevel
6139 && pStyleInf->mnWW8OutlineLevel != pStyleInf->m_nListLevel))
6141 // WW8 Built-In Heading Style does not apply the chosen one.
6142 // --> delete assignment to OutlineStyle, but keep its current
6143 // outline level
6144 pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
6145 // Apply existing WW8 list style a normal list style at the
6146 // Paragraph Style
6147 if (pStyleInf->GetOutlineNumrule() != nullptr)
6149 pTextFormatColl->SetFormatAttr(
6150 SwNumRuleItem(pStyleInf->GetOutlineNumrule()->GetName()));
6152 // apply default outline level of WW8 Built-in Heading Style
6153 const sal_uInt8 nOutlineLevel
6154 = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(
6155 pStyleInf->mnWW8OutlineLevel);
6156 pTextFormatColl->SetFormatAttr(
6157 SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel));
6159 else
6161 pTextFormatColl->AssignToListLevelOfOutlineStyle(
6162 pStyleInf->mnWW8OutlineLevel);
6166 if (m_pChosenWW8OutlineStyle)
6168 m_rDoc.SetOutlineNumRule(aOutlineRule);
6172 const OUString* SwWW8ImplReader::GetAnnotationAuthor(sal_uInt16 nIdx)
6174 if (!m_xAtnNames && m_xWwFib->m_lcbGrpStAtnOwners)
6176 // Determine authors: can be found in the TableStream
6177 m_xAtnNames.emplace();
6178 SvStream& rStrm = *m_pTableStream;
6180 auto nOldPos = rStrm.Tell();
6181 bool bValidPos = checkSeek(rStrm, m_xWwFib->m_fcGrpStAtnOwners);
6182 if (bValidPos)
6184 tools::Long nRead = 0, nCount = m_xWwFib->m_lcbGrpStAtnOwners;
6185 while (nRead < nCount && rStrm.good())
6187 if( m_bVer67 )
6189 m_xAtnNames->push_back(read_uInt8_PascalString(rStrm,
6190 RTL_TEXTENCODING_MS_1252));
6191 nRead += m_xAtnNames->rbegin()->getLength() + 1; // Length + sal_uInt8 count
6193 else
6195 m_xAtnNames->push_back(read_uInt16_PascalString(rStrm));
6196 // Unicode: double the length + sal_uInt16 count
6197 nRead += (m_xAtnNames->rbegin()->getLength() + 1)*2;
6201 rStrm.Seek( nOldPos );
6204 const OUString *pRet = nullptr;
6205 if (m_xAtnNames && nIdx < m_xAtnNames->size())
6206 pRet = &((*m_xAtnNames)[nIdx]);
6207 return pRet;
6210 void SwWW8ImplReader::GetSmartTagInfo(SwFltRDFMark& rMark)
6212 if (!m_pSmartTagData && m_xWwFib->m_lcbFactoidData)
6214 m_pSmartTagData.reset(new WW8SmartTagData);
6215 m_pSmartTagData->Read(*m_pTableStream, m_xWwFib->m_fcFactoidData, m_xWwFib->m_lcbFactoidData);
6218 if (!m_pSmartTagData)
6219 return;
6221 // Check if the handle is a valid smart tag bookmark index.
6222 size_t nIndex = rMark.GetHandle();
6223 if (nIndex >= m_pSmartTagData->m_aPropBags.size())
6224 return;
6226 // Check if the smart tag bookmark refers to a valid factoid type.
6227 const MSOPropertyBag& rPropertyBag = m_pSmartTagData->m_aPropBags[rMark.GetHandle()];
6228 auto& rFactoidTypes = m_pSmartTagData->m_aPropBagStore.m_aFactoidTypes;
6229 auto itPropertyBag = std::find_if(rFactoidTypes.begin(), rFactoidTypes.end(),
6230 [&rPropertyBag](const MSOFactoidType& rType) { return rType.m_nId == rPropertyBag.m_nId; });
6231 if (itPropertyBag == rFactoidTypes.end())
6232 return;
6234 // Check if the factoid is an RDF one.
6235 const MSOFactoidType& rFactoidType = *itPropertyBag;
6236 if (rFactoidType.m_aUri != "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
6237 return;
6239 // Finally put the relevant attributes to the mark.
6240 std::vector< std::pair<OUString, OUString> > aAttributes;
6241 for (const MSOProperty& rProperty : rPropertyBag.m_aProperties)
6243 OUString aKey;
6244 OUString aValue;
6245 if (rProperty.m_nKey < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6246 aKey = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nKey];
6247 if (rProperty.m_nValue < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6248 aValue = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nValue];
6249 if (!aKey.isEmpty() && !aValue.isEmpty())
6250 aAttributes.emplace_back(aKey, aValue);
6252 rMark.SetAttributes(std::move(aAttributes));
6255 ErrCode SwWW8ImplReader::LoadDoc(WW8Glossary *pGloss)
6257 ErrCode nErrRet = ERRCODE_NONE;
6260 static constexpr OUString aNames[ 13 ] = {
6261 u"WinWord/WW"_ustr, u"WinWord/WW8"_ustr, u"WinWord/WWFT"_ustr,
6262 u"WinWord/WWFLX"_ustr, u"WinWord/WWFLY"_ustr,
6263 u"WinWord/WWF"_ustr,
6264 u"WinWord/WWFA0"_ustr, u"WinWord/WWFA1"_ustr, u"WinWord/WWFA2"_ustr,
6265 u"WinWord/WWFB0"_ustr, u"WinWord/WWFB1"_ustr, u"WinWord/WWFB2"_ustr,
6266 u"WinWord/RegardHindiDigits"_ustr
6268 sal_uInt64 aVal[ 13 ];
6270 SwFilterOptions aOpt( 13, aNames, aVal );
6272 m_nIniFlags = aVal[ 0 ];
6273 m_nIniFlags1= aVal[ 1 ];
6274 // Moves Flys by x twips to the right or left
6275 m_nIniFlyDx = aVal[ 3 ];
6276 m_nIniFlyDy = aVal[ 4 ];
6278 m_nFieldFlags = aVal[ 5 ];
6279 m_nFieldTagAlways[0] = aVal[ 6 ];
6280 m_nFieldTagAlways[1] = aVal[ 7 ];
6281 m_nFieldTagAlways[2] = aVal[ 8 ];
6282 m_nFieldTagBad[0] = aVal[ 9 ];
6283 m_nFieldTagBad[1] = aVal[ 10 ];
6284 m_nFieldTagBad[2] = aVal[ 11 ];
6285 m_bRegardHindiDigits = aVal[ 12 ] > 0;
6288 sal_uInt16 nMagic(0);
6289 m_pStrm->ReadUInt16( nMagic );
6291 // Remember: 6 means "6 OR 7", 7 means "JUST 7"
6292 switch (m_nWantedVersion)
6294 case 6:
6295 case 7:
6296 if (
6297 0xa59b != nMagic && 0xa59c != nMagic &&
6298 0xa5dc != nMagic && 0xa5db != nMagic &&
6299 (nMagic < 0xa697 || nMagic > 0xa699)
6302 // Test for own 97 fake!
6303 if (m_pStg && 0xa5ec == nMagic)
6305 sal_uInt64 nCurPos = m_pStrm->Tell();
6306 if (checkSeek(*m_pStrm, nCurPos + 2))
6308 sal_uInt32 nfcMin(0);
6309 m_pStrm->ReadUInt32( nfcMin );
6310 if (0x300 != nfcMin)
6311 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6313 m_pStrm->Seek( nCurPos );
6315 else
6316 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6318 break;
6319 case 8:
6320 if (0xa5ec != nMagic)
6321 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6322 break;
6323 default:
6324 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6325 OSL_ENSURE( false, "We forgot to encode nVersion!" );
6326 break;
6329 if (!nErrRet)
6330 nErrRet = LoadThroughDecryption(pGloss);
6332 m_rDoc.PropagateOutlineRule();
6334 return nErrRet;
6337 extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOC()
6339 return new WW8Reader;
6342 namespace
6344 class FontCacheGuard
6346 public:
6347 ~FontCacheGuard()
6349 FlushFontCache();
6354 bool TestImportDOC(SvStream &rStream, const OUString &rFltName)
6356 FontCacheGuard aFontCacheGuard;
6357 std::unique_ptr<Reader> xReader(ImportDOC());
6359 rtl::Reference<SotStorage> xStorage;
6360 xReader->m_pStream = &rStream;
6361 if (rFltName != "WW6")
6365 xStorage.set(new SotStorage(rStream));
6366 if (xStorage->GetError())
6367 return false;
6369 catch (...)
6371 return false;
6373 xReader->m_pStorage = xStorage.get();
6375 xReader->SetFltName(rFltName);
6377 SwGlobals::ensure();
6379 SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
6380 xDocSh->DoInitNew();
6381 SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
6383 SwPaM aPaM(pD->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
6384 pD->SetInReading(true);
6385 bool bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE;
6386 pD->SetInReading(false);
6388 return bRet;
6391 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW8(SvStream &rStream)
6393 return TestImportDOC(rStream, u"CWW8"_ustr);
6396 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW6(SvStream &rStream)
6398 return TestImportDOC(rStream, u"CWW6"_ustr);
6401 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW2(SvStream &rStream)
6403 return TestImportDOC(rStream, u"WW6"_ustr);
6406 ErrCode WW8Reader::OpenMainStream(rtl::Reference<SotStorageStream>& rRef, sal_uInt16& rBuffSize)
6408 ErrCode nRet = ERR_SWG_READ_ERROR;
6409 OSL_ENSURE(m_pStorage, "Where is my Storage?");
6410 rRef = m_pStorage->OpenSotStream( u"WordDocument"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6412 if( rRef.is() )
6414 if( ERRCODE_NONE == rRef->GetError() )
6416 sal_uInt16 nOld = rRef->GetBufferSize();
6417 rRef->SetBufferSize( rBuffSize );
6418 rBuffSize = nOld;
6419 nRet = ERRCODE_NONE;
6421 else
6422 nRet = rRef->GetError();
6424 return nRet;
6427 static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, std::u16string_view sPrefix)
6429 SvStorageInfoList aElements;
6430 pStorage->FillInfoList(&aElements);
6431 for (const auto & aElement : aElements)
6433 OUString sStreamFullName = sPrefix.size() ? OUString::Concat(sPrefix) + "/" + aElement.GetName() : aElement.GetName();
6434 if (aElement.IsStorage())
6436 rtl::Reference<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
6437 lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
6439 else
6441 // Read stream
6442 rtl::Reference<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
6443 if (rStream.is())
6445 sal_Int32 nStreamSize = rStream->GetSize();
6446 css::uno::Sequence< sal_Int8 > oData;
6447 oData.realloc(nStreamSize);
6448 sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
6449 if (nStreamSize == nReadBytes)
6450 aStreamsData[sStreamFullName] <<= oData;
6456 ErrCode WW8Reader::DecryptDRMPackage()
6458 // We have DRM encrypted storage. We should try to decrypt it first, if we can
6459 uno::Sequence< uno::Any > aArguments;
6460 const uno::Reference<uno::XComponentContext>& xComponentContext(comphelper::getProcessComponentContext());
6461 uno::Reference< packages::XPackageEncryption > xPackageEncryption(
6462 xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
6463 u"com.sun.star.comp.oox.crypto.DRMDataSpace"_ustr, aArguments, xComponentContext), uno::UNO_QUERY);
6465 if (!xPackageEncryption.is())
6467 // We do not know how to decrypt this
6468 return ERRCODE_IO_ACCESSDENIED;
6471 comphelper::SequenceAsHashMap aStreamsData;
6472 lcl_getListOfStreams(m_pStorage.get(), aStreamsData, u"");
6474 try {
6475 uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
6476 if (!xPackageEncryption->readEncryptionInfo(aStreams))
6478 // We failed with decryption
6479 return ERRCODE_IO_ACCESSDENIED;
6482 rtl::Reference<SotStorageStream> rContentStream = m_pStorage->OpenSotStream(u"\011DRMContent"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6483 if (!rContentStream.is())
6485 return ERRCODE_IO_NOTEXISTS;
6488 mDecodedStream = std::make_shared<SvMemoryStream>();
6490 uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
6491 uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*mDecodedStream));
6493 if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
6495 // We failed with decryption
6496 return ERRCODE_IO_ACCESSDENIED;
6499 mDecodedStream->Seek(0);
6501 // Further reading is done from new document
6502 m_pStorage = new SotStorage(*mDecodedStream);
6504 // Set the media descriptor data
6505 uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData(u""_ustr);
6506 m_pMedium->GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
6508 catch (const std::exception&)
6510 return ERRCODE_IO_ACCESSDENIED;
6513 return ERRCODE_NONE;
6516 ErrCodeMsg WW8Reader::Read(SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & /* FileName */)
6518 sal_uInt16 nOldBuffSize = 32768;
6519 bool bNew = !m_bInsertMode; // New Doc (no inserting)
6521 rtl::Reference<SotStorageStream> refStrm; // So that no one else can steal the Stream
6522 SvStream* pIn = m_pStream;
6524 ErrCode nRet = ERRCODE_NONE;
6525 sal_uInt8 nVersion = 8;
6527 const OUString sFltName = GetFltName();
6528 if ( sFltName=="WW6" )
6530 if (m_pStream)
6531 nVersion = 6;
6532 else
6534 OSL_ENSURE(false, "WinWord 95 Reader-Read without Stream");
6535 nRet = ERR_SWG_READ_ERROR;
6538 else
6540 if ( sFltName=="CWW6" )
6541 nVersion = 6;
6542 else if ( sFltName=="CWW7" )
6543 nVersion = 7;
6545 if( m_pStorage.is() )
6547 // Check if we have special encrypted content
6548 rtl::Reference<SotStorageStream> rRef = m_pStorage->OpenSotStream(u"\006DataSpaces/DataSpaceInfo/\011DRMDataSpace"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
6549 if (rRef.is())
6551 nRet = DecryptDRMPackage();
6553 nRet = OpenMainStream(refStrm, nOldBuffSize);
6554 pIn = refStrm.get();
6556 else
6558 OSL_ENSURE(false, "WinWord 95/97 Reader-Read without Storage");
6559 nRet = ERR_SWG_READ_ERROR;
6563 if( !nRet )
6565 std::unique_ptr<SwWW8ImplReader> pRdr(new SwWW8ImplReader(nVersion, m_pStorage.get(), pIn, rDoc,
6566 rBaseURL, bNew, m_bSkipImages, *rPaM.GetPoint()));
6567 if (bNew)
6569 rPaM.GetBound().nContent.Assign(nullptr, 0);
6570 rPaM.GetBound(false).nContent.Assign(nullptr, 0);
6574 nRet = pRdr->LoadDoc();
6576 catch( const std::exception& )
6578 nRet = ERR_WW8_NO_WW8_FILE_ERR;
6581 if( refStrm.is() )
6583 refStrm->SetBufferSize( nOldBuffSize );
6584 refStrm.clear();
6586 else
6588 pIn->ResetError();
6592 return nRet;
6595 SwReaderType WW8Reader::GetReaderType()
6597 return SwReaderType::Storage | SwReaderType::Stream;
6600 bool WW8Reader::HasGlossaries() const
6602 return true;
6605 bool WW8Reader::ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const
6607 bool bRet=false;
6609 WW8Reader *pThis = const_cast<WW8Reader *>(this);
6611 sal_uInt16 nOldBuffSize = 32768;
6612 rtl::Reference<SotStorageStream> refStrm;
6613 if (!pThis->OpenMainStream(refStrm, nOldBuffSize))
6615 WW8Glossary aGloss( refStrm, 8, m_pStorage.get() );
6616 bRet = aGloss.Load( rBlocks, bSaveRelFiles );
6618 return bRet;
6621 bool SwMSDffManager::GetOLEStorageName(sal_uInt32 nOLEId, OUString& rStorageName,
6622 rtl::Reference<SotStorage>& rSrcStorage, uno::Reference < embed::XStorage >& rDestStorage) const
6624 bool bRet = false;
6626 sal_Int32 nPictureId = 0;
6627 if (m_rReader.m_pStg)
6629 // Via the TextBox-PLCF we get the right char Start-End positions
6630 // We should then find the EmbeddedField and the corresponding Sprms
6631 // in that Area.
6632 // We only need the Sprm for the Picture Id.
6633 sal_uInt64 nOldPos = m_rReader.m_pStrm->Tell();
6635 // #i32596# - consider return value of method
6636 // <rReader.GetTxbxTextSttEndCp(..)>. If it returns false, method
6637 // wasn't successful. Thus, continue in this case.
6638 // Note: Ask MM for initialization of <nStartCp> and <nEndCp>.
6639 // Note: Ask MM about assertions in method <rReader.GetTxbxTextSttEndCp(..)>.
6640 WW8_CP nStartCp, nEndCp;
6641 if ( m_rReader.m_bDrawCpOValid && m_rReader.GetTxbxTextSttEndCp(nStartCp, nEndCp,
6642 o3tl::narrowing<sal_uInt16>((nOLEId >> 16) & 0xFFFF),
6643 o3tl::narrowing<sal_uInt16>(nOLEId & 0xFFFF)) )
6645 WW8PLCFxSaveAll aSave;
6646 m_rReader.m_xPlcxMan->SaveAllPLCFx( aSave );
6648 nStartCp += m_rReader.m_nDrawCpO;
6649 nEndCp += m_rReader.m_nDrawCpO;
6650 WW8PLCFx_Cp_FKP* pChp = m_rReader.m_xPlcxMan->GetChpPLCF();
6651 wwSprmParser aSprmParser(*m_rReader.m_xWwFib);
6652 while (nStartCp <= nEndCp && !nPictureId)
6654 if (!pChp->SeekPos( nStartCp))
6655 break;
6656 WW8PLCFxDesc aDesc;
6657 pChp->GetSprms( &aDesc );
6659 if (aDesc.nSprmsLen && aDesc.pMemPos) // Attributes present
6661 auto nLen = aDesc.nSprmsLen;
6662 const sal_uInt8* pSprm = aDesc.pMemPos;
6664 while (nLen >= 2 && !nPictureId)
6666 sal_uInt16 nId = aSprmParser.GetSprmId(pSprm);
6667 sal_Int32 nSL = aSprmParser.GetSprmSize(nId, pSprm, nLen);
6669 if( nLen < nSL )
6670 break; // Not enough Bytes left
6672 if (0x6A03 == nId)
6674 nPictureId = SVBT32ToUInt32(pSprm +
6675 aSprmParser.DistanceToData(nId));
6676 bRet = true;
6678 pSprm += nSL;
6679 nLen -= nSL;
6682 nStartCp = aDesc.nEndPos;
6685 m_rReader.m_xPlcxMan->RestoreAllPLCFx( aSave );
6688 m_rReader.m_pStrm->Seek( nOldPos );
6691 if( bRet )
6693 rStorageName = "_";
6694 rStorageName += OUString::number(nPictureId);
6695 rSrcStorage = m_rReader.m_pStg->OpenSotStorage(SL::aObjectPool);
6696 if (!m_rReader.m_pDocShell)
6697 bRet=false;
6698 else
6699 rDestStorage = m_rReader.m_pDocShell->GetStorage();
6701 return bRet;
6705 * When reading a single Box (which possibly is part of a group), we do
6706 * not yet have enough information to decide whether we need it as a TextField
6707 * or not.
6708 * So convert all of them as a precaution.
6709 * FIXME: Actually implement this!
6711 bool SwMSDffManager::ShapeHasText(sal_uLong, sal_uLong) const
6713 return true;
6716 bool SwWW8ImplReader::InEqualOrHigherApo(int nLvl) const
6718 if (nLvl)
6719 --nLvl;
6720 // #i60827# - check size of <maApos> to assure that <maApos.begin() + nLvl> can be performed.
6721 if ( sal::static_int_cast< sal_Int32>(nLvl) >= sal::static_int_cast< sal_Int32>(m_aApos.size()) )
6723 return false;
6725 auto aIter = std::find(m_aApos.begin() + nLvl, m_aApos.end(), true);
6726 return aIter != m_aApos.end();
6729 bool SwWW8ImplReader::InEqualApo(int nLvl) const
6731 // If we are in a table, see if an apo was inserted at the level below the table.
6732 if (nLvl)
6733 --nLvl;
6734 if (nLvl < 0 || o3tl::make_unsigned(nLvl) >= m_aApos.size())
6735 return false;
6736 return m_aApos[nLvl];
6739 namespace sw::hack
6741 Position::Position(const SwPosition &rPos)
6742 : maPtNode(rPos.GetNode()), mnPtContent(rPos.GetContentIndex())
6746 Position::operator SwPosition() const
6748 return SwPosition(maPtNode, maPtNode.GetNode().GetContentNode(), mnPtContent);
6752 SwMacroInfo::SwMacroInfo()
6753 : SdrObjUserData( SdrInventor::ScOrSwDraw, SW_UD_IMAPDATA )
6754 , mnShapeId(-1)
6758 SwMacroInfo::~SwMacroInfo()
6762 std::unique_ptr<SdrObjUserData> SwMacroInfo::Clone( SdrObject* /*pObj*/ ) const
6764 return std::unique_ptr<SdrObjUserData>(new SwMacroInfo( *this ));
6767 std::unique_ptr<SfxItemSet> SwWW8ImplReader::SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet)
6769 std::unique_ptr<SfxItemSet> xRet(std::move(m_xCurrentItemSet));
6770 m_xCurrentItemSet = std::move(pItemSet);
6771 return xRet;
6774 void SwWW8ImplReader::NotifyMacroEventRead()
6776 if (m_bNotifyMacroEventRead)
6777 return;
6778 if (SwDocShell* pShell = m_rDoc.GetDocShell())
6780 uno::Reference<frame::XModel> const xModel(static_cast<SfxBaseModel*>(pShell->GetBaseModel().get()));
6781 comphelper::DocumentInfo::notifyMacroEventRead(xModel);
6782 m_bNotifyMacroEventRead = true;
6786 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */