android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / ww8par.cxx
blobabd7cf4c0345b65baab6ea43fce3cf54fe9f7456
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>
31 #include <i18nlangtag/languagetag.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <unotools/ucbstreamhelper.hxx>
35 #include <unotools/streamwrap.hxx>
36 #include <rtl/random.h>
37 #include <rtl/ustring.hxx>
38 #include <rtl/ustrbuf.hxx>
40 #include <sfx2/docinf.hxx>
41 #include <sfx2/frame.hxx>
42 #include <sfx2/zoomitem.hxx>
43 #include <tools/urlobj.hxx>
44 #include <unotools/tempfile.hxx>
46 #include <comphelper/docpasswordrequest.hxx>
47 #include <comphelper/documentinfo.hxx>
48 #include <comphelper/propertysequence.hxx>
50 #include <editeng/outlobj.hxx>
51 #include <editeng/brushitem.hxx>
52 #include <editeng/formatbreakitem.hxx>
53 #include <editeng/tstpitem.hxx>
54 #include <editeng/ulspitem.hxx>
55 #include <editeng/langitem.hxx>
56 #include <editeng/opaqitem.hxx>
57 #include <editeng/charhiddenitem.hxx>
58 #include <editeng/fontitem.hxx>
59 #include <editeng/editeng.hxx>
60 #include <svx/svdoole2.hxx>
61 #include <svx/svdoashp.hxx>
62 #include <svx/svxerr.hxx>
63 #include <filter/msfilter/mscodec.hxx>
64 #include <svx/svdmodel.hxx>
65 #include <svx/xflclit.hxx>
66 #include <svx/sdasitm.hxx>
67 #include <svx/sdtagitm.hxx>
68 #include <svx/sdtcfitm.hxx>
69 #include <svx/sdtditm.hxx>
70 #include <svx/sdtmfitm.hxx>
71 #include <unotools/fltrcfg.hxx>
72 #include <fmtfld.hxx>
73 #include <fmturl.hxx>
74 #include <fmtinfmt.hxx>
75 #include <reffld.hxx>
76 #include <fmthdft.hxx>
77 #include <fmtcntnt.hxx>
78 #include <fmtcnct.hxx>
79 #include <fmtanchr.hxx>
80 #include <fmtpdsc.hxx>
81 #include <ftninfo.hxx>
82 #include <fmtftn.hxx>
83 #include <txtftn.hxx>
84 #include <ndtxt.hxx>
85 #include <pagedesc.hxx>
86 #include <paratr.hxx>
87 #include <poolfmt.hxx>
88 #include <fmtclbl.hxx>
89 #include <section.hxx>
90 #include <docsh.hxx>
91 #include <IDocumentFieldsAccess.hxx>
92 #include <IDocumentLayoutAccess.hxx>
93 #include <IDocumentMarkAccess.hxx>
94 #include <IDocumentStylePoolAccess.hxx>
95 #include <IDocumentExternalData.hxx>
96 #include <../../core/inc/DocumentRedlineManager.hxx>
97 #include <docufld.hxx>
98 #include <swfltopt.hxx>
99 #include <utility>
100 #include <viewsh.hxx>
101 #include <shellres.hxx>
102 #include <swerror.h>
103 #include <swtable.hxx>
104 #include <fchrfmt.hxx>
105 #include <charfmt.hxx>
106 #include <fmtautofmt.hxx>
107 #include <IDocumentSettingAccess.hxx>
108 #include "sprmids.hxx"
110 #include "writerwordglue.hxx"
112 #include <ndgrf.hxx>
113 #include <editeng/editids.hrc>
114 #include <fmtflcnt.hxx>
115 #include <txatbase.hxx>
117 #include "ww8par2.hxx"
119 #include <com/sun/star/beans/PropertyAttribute.hpp>
120 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
121 #include <com/sun/star/document/XViewDataSupplier.hpp>
123 #include <svl/lngmisc.hxx>
124 #include <svl/itemiter.hxx>
125 #include <svl/whiter.hxx>
127 #include <comphelper/indexedpropertyvalues.hxx>
128 #include <comphelper/processfactory.hxx>
129 #include <basic/basmgr.hxx>
131 #include "ww8toolbar.hxx"
132 #include <o3tl/unit_conversion.hxx>
133 #include <o3tl/safeint.hxx>
134 #include <osl/file.hxx>
136 #include <breakit.hxx>
138 #include <sfx2/docfile.hxx>
139 #include <swdll.hxx>
140 #include "WW8Sttbf.hxx"
141 #include "WW8FibData.hxx"
142 #include <unordered_set>
143 #include <memory>
145 using namespace ::com::sun::star;
146 using namespace sw::util;
147 using namespace sw::types;
148 using namespace nsHdFtFlags;
150 #include <com/sun/star/i18n/XBreakIterator.hpp>
151 #include <com/sun/star/i18n/ScriptType.hpp>
152 #include <unotools/pathoptions.hxx>
153 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
155 #include <com/sun/star/script/vba/XVBACompatibility.hpp>
156 #include <comphelper/sequenceashashmap.hxx>
157 #include <oox/ole/vbaproject.hxx>
158 #include <oox/ole/olestorage.hxx>
159 #include <comphelper/storagehelper.hxx>
160 #include <sfx2/DocumentMetadataAccess.hxx>
161 #include <comphelper/diagnose_ex.hxx>
163 static SwMacroInfo* GetMacroInfo( SdrObject* pObj )
165 if ( pObj )
167 sal_uInt16 nCount = pObj->GetUserDataCount();
168 for( sal_uInt16 i = 0; i < nCount; i++ )
170 SdrObjUserData* pData = pObj->GetUserData( i );
171 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
172 && pData->GetId() == SW_UD_IMAPDATA)
174 return dynamic_cast<SwMacroInfo*>(pData);
177 SwMacroInfo* pData = new SwMacroInfo;
178 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
179 return pData;
182 return nullptr;
185 static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell)
187 OUStringBuffer aTmpStr;
188 while( nLevel )
190 aTmpStr.append("../");
191 --nLevel;
193 if (!aTmpStr.isEmpty())
194 aTmpStr.append(rPath);
195 else
196 aTmpStr = rPath;
198 if (!aTmpStr.isEmpty())
200 bool bWasAbs = false;
201 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
202 // full path as stored in SvxURLField must be encoded
206 namespace
208 void lclIgnoreUString32(SvStream& rStrm)
210 sal_uInt32 nChars(0);
211 rStrm.ReadUInt32(nChars);
212 nChars *= 2;
213 rStrm.SeekRel(nChars);
217 void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
219 // (0x01B8) HLINK
220 // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
221 constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL.
222 constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path.
223 constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description.
224 constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark.
225 constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame.
226 constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path.
228 //sal_uInt8 maGuidStdLink[ 16 ] ={
229 // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
231 sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
232 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
234 sal_uInt8 const aGuidFileMoniker[ 16 ] = {
235 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
237 sal_uInt8 aGuid[16];
238 sal_uInt32 nFlags(0);
240 rStrm.ReadBytes(aGuid, 16);
241 rStrm.SeekRel( 4 );
242 rStrm.ReadUInt32( nFlags );
244 std::unique_ptr< OUString > xLongName; // link / file name
245 std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
246 std::unique_ptr< OUString > xTextMark; // text mark
248 // description (ignore)
249 if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
250 lclIgnoreUString32( rStrm );
252 // target frame
253 if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
255 hlStr.tarFrame = read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm);
258 // UNC path
259 if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
261 // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the
262 // string field, including the null-terminating character.
263 sal_uInt32 nStrLen(0);
264 rStrm.ReadUInt32(nStrLen);
265 if (nStrLen)
267 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
268 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
269 lclGetAbsPath( *xLongName, 0 , pDocShell);
272 // file link or URL
273 else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
275 rStrm.ReadBytes(aGuid, 16);
277 if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
279 sal_uInt16 nLevel = 0; // counter for level to climb down in path
280 rStrm.ReadUInt16( nLevel );
281 // MS-OSHARED: An unsigned integer that specifies the number of
282 // ANSI characters in ansiPath, including the terminating NULL character
283 sal_uInt32 nUnits = 0;
284 rStrm.ReadUInt32(nUnits);
285 if (!nUnits)
286 xShortName.reset(new OUString);
287 else
289 OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1));
290 rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
291 xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage()));
293 rStrm.SeekRel( 24 );
295 sal_uInt32 nStrLen(0);
296 rStrm.ReadUInt32( nStrLen );
297 if( nStrLen )
299 nStrLen = 0;
300 rStrm.ReadUInt32( nStrLen );
301 nStrLen /= 2;
302 rStrm.SeekRel( 2 );
303 // MS-OSHARED: This array MUST not include a terminating NULL character.
304 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen)));
305 lclGetAbsPath( *xLongName, nLevel, pDocShell);
307 else
308 lclGetAbsPath( *xShortName, nLevel, pDocShell);
310 else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
312 // MS-OSHARED: An unsigned integer that specifies the size of this
313 // structure in bytes, excluding the size of the length field. The
314 // value of this field MUST be ... the byte size of the url
315 // field (including the terminating NULL character)
316 sal_uInt32 nStrLen(0);
317 rStrm.ReadUInt32( nStrLen );
318 nStrLen /= 2;
319 if (!nStrLen)
320 xLongName.reset(new OUString);
321 else
323 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
324 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
326 if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
327 lclGetAbsPath( *xLongName, 0 ,pDocShell);
329 else
331 SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
335 // text mark
336 if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
338 xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
341 if (!xLongName && xShortName)
342 xLongName.reset(new OUString(*xShortName));
343 else if (!xLongName && xTextMark)
344 xLongName.reset( new OUString );
346 if (xLongName)
348 if (xTextMark)
350 if (xLongName->isEmpty())
351 *xTextMark = xTextMark->replace('!', '.');
352 *xLongName += "#" + *xTextMark;
354 hlStr.hLinkAddr = *xLongName;
358 namespace {
360 class BasicProjImportHelper
362 SwDocShell& mrDocShell;
363 uno::Reference< uno::XComponentContext > mxCtx;
364 public:
365 explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
366 mxCtx(comphelper::getProcessComponentContext())
369 bool import( const uno::Reference< io::XInputStream >& rxIn );
370 OUString getProjectName() const;
375 bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
377 bool bRet = false;
380 oox::ole::OleStorage root( mxCtx, rxIn, false );
381 oox::StorageRef vbaStg = root.openSubStorage( "Macros" , false );
382 if ( vbaStg )
384 oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
385 bRet = aVbaPrj.importVbaProject( *vbaStg );
388 catch( const uno::Exception& )
390 bRet = false;
392 return bRet;
395 OUString BasicProjImportHelper::getProjectName() const
397 OUString sProjName( "Standard" );
398 uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
399 if ( xProps.is() )
403 uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW );
404 sProjName = xVBA->getProjectName();
407 catch( const uno::Exception& )
411 return sProjName;
414 namespace {
416 class Sttb : public TBBase
418 struct SBBItem
420 sal_uInt16 cchData;
421 OUString data;
422 SBBItem() : cchData(0){}
424 sal_uInt16 m_fExtend;
425 sal_uInt16 m_cData;
426 sal_uInt16 m_cbExtra;
428 std::vector< SBBItem > m_dataItems;
430 Sttb(Sttb const&) = delete;
431 Sttb& operator=(Sttb const&) = delete;
433 public:
434 Sttb();
436 bool Read(SvStream &rS) override;
437 OUString getStringAtIndex( sal_uInt32 );
442 Sttb::Sttb()
443 : m_fExtend(0)
444 , m_cData(0)
445 , m_cbExtra(0)
449 bool Sttb::Read( SvStream& rS )
451 SAL_INFO("sw.ww8", "stream pos " << rS.Tell());
452 nOffSet = rS.Tell();
453 rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
454 if ( m_cData )
456 //if they are all going to be empty strings, how many could there be
457 const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16);
458 if (m_cData > nMaxPossibleRecords)
459 return false;
460 for ( sal_Int32 index = 0; index < m_cData; ++index )
462 SBBItem aItem;
463 rS.ReadUInt16( aItem.cchData );
464 aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
465 m_dataItems.push_back( aItem );
468 return true;
471 OUString
472 Sttb::getStringAtIndex( sal_uInt32 index )
474 OUString aRet;
475 if ( index < m_dataItems.size() )
476 aRet = m_dataItems[ index ].data;
477 return aRet;
481 SwMSDffManager::SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages )
482 : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo,
483 rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages),
484 m_rReader(rRdr), m_pFallbackStream(nullptr)
486 SetSvxMSDffSettings( GetSvxMSDffSettings() );
487 nSvxMSDffOLEConvFlags = SwMSDffManager::GetFilterFlags();
490 sal_uInt32 SwMSDffManager::GetFilterFlags()
492 sal_uInt32 nFlags(0);
493 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
494 if (rOpt.IsMathType2Math())
495 nFlags |= OLE_MATHTYPE_2_STARMATH;
496 if (rOpt.IsExcel2Calc())
497 nFlags |= OLE_EXCEL_2_STARCALC;
498 if (rOpt.IsPowerPoint2Impress())
499 nFlags |= OLE_POWERPOINT_2_STARIMPRESS;
500 if (rOpt.IsWinWord2Writer())
501 nFlags |= OLE_WINWORD_2_STARWRITER;
502 return nFlags;
506 * I would like to override the default OLE importing to add a test
507 * and conversion of OCX controls from their native OLE type into our
508 * native nonOLE Form Control Objects.
510 // #i32596# - consider new parameter <_nCalledByGroup>
511 rtl::Reference<SdrObject> SwMSDffManager::ImportOLE( sal_uInt32 nOLEId,
512 const Graphic& rGrf,
513 const tools::Rectangle& rBoundRect,
514 const tools::Rectangle& rVisArea,
515 const int _nCalledByGroup ) const
517 // #i32596# - no import of OLE object, if it's inside a group.
518 // NOTE: This can be undone, if grouping of Writer fly frames is possible or
519 // if drawing OLE objects are allowed in Writer.
520 if ( _nCalledByGroup > 0 )
522 return nullptr;
525 rtl::Reference<SdrObject> pRet;
526 OUString sStorageName;
527 tools::SvRef<SotStorage> xSrcStg;
528 uno::Reference < embed::XStorage > xDstStg;
529 if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
531 tools::SvRef<SotStorage> xSrc = xSrcStg->OpenSotStorage( sStorageName );
532 OSL_ENSURE(m_rReader.m_xFormImpl, "No Form Implementation!");
533 css::uno::Reference< css::drawing::XShape > xShape;
534 if ( (!(m_rReader.m_bIsHeader || m_rReader.m_bIsFooter)) &&
535 m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
537 pRet = SdrObject::getSdrObjectFromXShape(xShape);
539 else
541 ErrCode nError = ERRCODE_NONE;
542 pRet = CreateSdrOLEFromStorage(
543 *pSdrModel,
544 sStorageName,
545 xSrcStg,
546 xDstStg,
547 rGrf,
548 rBoundRect,
549 rVisArea,
550 pStData,
551 nError,
552 nSvxMSDffOLEConvFlags,
553 css::embed::Aspects::MSOLE_CONTENT,
554 m_rReader.GetBaseURL());
557 return pRet;
560 void SwMSDffManager::DisableFallbackStream()
562 OSL_ENSURE(!m_pFallbackStream,
563 "if you're recursive, you're broken");
564 m_pFallbackStream = pStData2;
565 m_aOldEscherBlipCache = aEscherBlipCache;
566 aEscherBlipCache.clear();
567 pStData2 = nullptr;
570 void SwMSDffManager::EnableFallbackStream()
572 pStData2 = m_pFallbackStream;
573 aEscherBlipCache = m_aOldEscherBlipCache;
574 m_aOldEscherBlipCache.clear();
575 m_pFallbackStream = nullptr;
578 sal_uInt16 SwWW8ImplReader::GetToggleAttrFlags() const
580 return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
583 sal_uInt16 SwWW8ImplReader::GetToggleBiDiAttrFlags() const
585 return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
588 void SwWW8ImplReader::SetToggleAttrFlags(sal_uInt16 nFlags)
590 if (m_xCtrlStck)
591 m_xCtrlStck->SetToggleAttrFlags(nFlags);
594 void SwWW8ImplReader::SetToggleBiDiAttrFlags(sal_uInt16 nFlags)
596 if (m_xCtrlStck)
597 m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
600 rtl::Reference<SdrObject> SwMSDffManager::ProcessObj(SvStream& rSt,
601 DffObjData& rObjData,
602 SvxMSDffClientData& rData,
603 tools::Rectangle& rTextRect,
604 SdrObject* pObj1
607 rtl::Reference<SdrObject> pObj = pObj1;
608 if( !rTextRect.IsEmpty() )
610 SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
611 std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
613 // fill Import Record with data
614 pImpRec->nShapeId = rObjData.nShapeId;
615 pImpRec->eShapeType = rObjData.eShapeType;
617 rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt,
618 DFF_msofbtClientAnchor,
619 SEEK_FROM_CURRENT_AND_RESTART );
620 if( rObjData.bClientAnchor )
621 ProcessClientAnchor( rSt,
622 maShapeRecords.Current()->nRecLen,
623 pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
625 rObjData.bClientData = maShapeRecords.SeekToContent( rSt,
626 DFF_msofbtClientData,
627 SEEK_FROM_CURRENT_AND_RESTART );
628 if( rObjData.bClientData )
629 ProcessClientData( rSt,
630 maShapeRecords.Current()->nRecLen,
631 pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
633 pImpRec->nGroupShapeBooleanProperties = 0;
635 if( maShapeRecords.SeekToContent( rSt,
636 DFF_msofbtUDefProp,
637 SEEK_FROM_CURRENT_AND_RESTART )
638 && maShapeRecords.Current()->nRecLen )
640 sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
641 auto nAvailableBytes = rSt.remainingSize();
642 if (nBytesLeft > nAvailableBytes)
644 SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
645 nBytesLeft = nAvailableBytes;
647 while( 5 < nBytesLeft )
649 sal_uInt16 nPID(0);
650 rSt.ReadUInt16(nPID);
651 sal_uInt32 nUDData(0);
652 rSt.ReadUInt32(nUDData);
653 if (!rSt.good())
654 break;
655 switch (nPID)
657 case 0x038F: pImpRec->nXAlign = nUDData; break;
658 case 0x0390:
659 pImpRec->nXRelTo = nUDData;
660 break;
661 case 0x0391: pImpRec->nYAlign = nUDData; break;
662 case 0x0392:
663 pImpRec->nYRelTo = nUDData;
664 break;
665 case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
666 case 0x0393:
667 // This seems to correspond to o:hrpct from .docx (even including
668 // the difference that it's in 0.1% even though the .docx spec
669 // says it's in 1%).
670 pImpRec->relativeHorizontalWidth = nUDData;
671 break;
672 case 0x0394:
673 // And this is really just a guess, but a mere presence of this
674 // flag makes a horizontal rule be as wide as the page (unless
675 // overridden by something), so it probably matches o:hr from .docx.
676 pImpRec->isHorizontalRule = true;
677 break;
679 nBytesLeft -= 6;
683 // Text Frame also Title or Outline
684 sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
685 if( nTextId )
687 SfxItemSet aSet( pSdrModel->GetItemPool() );
689 // Originally anything that as a mso_sptTextBox was created as a
690 // textbox, this was changed to be created as a simple
691 // rect to keep impress happy. For the rest of us we'd like to turn
692 // it back into a textbox again.
693 bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox);
694 if (!bIsSimpleDrawingTextBox)
696 // Either
697 // a) it's a simple text object or
698 // b) it's a rectangle with text and square wrapping.
699 bIsSimpleDrawingTextBox =
701 (pImpRec->eShapeType == mso_sptTextSimple) ||
703 (pImpRec->eShapeType == mso_sptRectangle)
704 && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
709 // Distance of Textbox to its surrounding Autoshape
710 sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440);
711 sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 );
712 sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 );
713 sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 );
715 ScaleEmu( nTextLeft );
716 ScaleEmu( nTextRight );
717 ScaleEmu( nTextTop );
718 ScaleEmu( nTextBottom );
720 Degree100 nTextRotationAngle;
721 bool bVerticalText = false;
722 if ( IsProperty( DFF_Prop_txflTextFlow ) )
724 MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
725 DFF_Prop_txflTextFlow, 0) & 0xFFFF);
726 switch( eTextFlow )
728 case mso_txflBtoT:
729 nTextRotationAngle = 9000_deg100;
730 break;
731 case mso_txflVertN:
732 case mso_txflTtoBN:
733 nTextRotationAngle = 27000_deg100;
734 break;
735 case mso_txflTtoBA:
736 bVerticalText = true;
737 break;
738 case mso_txflHorzA:
739 bVerticalText = true;
740 nTextRotationAngle = 9000_deg100;
741 break;
742 case mso_txflHorzN:
743 default :
744 break;
748 if (nTextRotationAngle)
750 if (nTextRotationAngle == 9000_deg100)
752 tools::Long nWidth = rTextRect.GetWidth();
753 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
754 rTextRect.SetBottom( rTextRect.Top() + nWidth );
756 sal_Int32 nOldTextLeft = nTextLeft;
757 sal_Int32 nOldTextRight = nTextRight;
758 sal_Int32 nOldTextTop = nTextTop;
759 sal_Int32 nOldTextBottom = nTextBottom;
761 nTextLeft = nOldTextBottom;
762 nTextRight = nOldTextTop;
763 nTextTop = nOldTextLeft;
764 nTextBottom = nOldTextRight;
766 else if (nTextRotationAngle == 27000_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 = nOldTextTop;
778 nTextRight = nOldTextBottom;
779 nTextTop = nOldTextRight;
780 nTextBottom = nOldTextLeft;
784 if (bIsSimpleDrawingTextBox)
786 pObj = new SdrRectObj(
787 *pSdrModel,
788 SdrObjKind::Text,
789 rTextRect);
792 // The vertical paragraph justification are contained within the
793 // BoundRect so calculate it here
794 tools::Rectangle aNewRect(rTextRect);
795 aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
796 aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
798 // Only if it's a simple Textbox, Writer can replace the Object
799 // with a Frame, else
800 if( bIsSimpleDrawingTextBox )
802 std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
803 std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
805 SvxMSDffShapeInfos_ById::const_iterator const it =
806 GetShapeInfos()->find(xTmpRec);
807 if (it != GetShapeInfos()->end())
809 SvxMSDffShapeInfo& rInfo = **it;
810 pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
813 ApplyAttributes(rSt, aSet, rObjData);
816 if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2)
818 aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
819 aSet.Put( makeSdrTextMinFrameHeightItem(
820 aNewRect.Bottom() - aNewRect.Top() ) );
821 aSet.Put( makeSdrTextMinFrameWidthItem(
822 aNewRect.Right() - aNewRect.Left() ) );
824 else
826 aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
827 aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
830 switch ( static_cast<MSO_WrapMode>(GetPropertyValue( DFF_Prop_WrapText, mso_wrapSquare )) )
832 case mso_wrapNone :
833 aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
834 pImpRec->bAutoWidth = true;
835 break;
836 case mso_wrapByPoints :
837 aSet.Put( makeSdrTextContourFrameItem( true ) );
838 break;
839 default:
843 // Set distances on Textbox's margins
844 aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
845 aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
846 aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
847 aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
848 pImpRec->nDxTextLeft = nTextLeft;
849 pImpRec->nDyTextTop = nTextTop;
850 pImpRec->nDxTextRight = nTextRight;
851 pImpRec->nDyTextBottom = nTextBottom;
853 // Taking the correct default (which is mso_anchorTop)
854 sal_uInt32 eTextAnchor =
855 GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
857 SdrTextVertAdjust eTVA = bVerticalText
858 ? SDRTEXTVERTADJUST_BLOCK
859 : SDRTEXTVERTADJUST_CENTER;
860 SdrTextHorzAdjust eTHA = bVerticalText
861 ? SDRTEXTHORZADJUST_CENTER
862 : SDRTEXTHORZADJUST_BLOCK;
864 switch( eTextAnchor )
866 case mso_anchorTop:
868 if ( bVerticalText )
869 eTHA = SDRTEXTHORZADJUST_RIGHT;
870 else
871 eTVA = SDRTEXTVERTADJUST_TOP;
873 break;
874 case mso_anchorTopCentered:
876 if ( bVerticalText )
877 eTHA = SDRTEXTHORZADJUST_RIGHT;
878 else
879 eTVA = SDRTEXTVERTADJUST_TOP;
881 break;
882 case mso_anchorMiddle:
883 break;
884 case mso_anchorMiddleCentered:
885 break;
886 case mso_anchorBottom:
888 if ( bVerticalText )
889 eTHA = SDRTEXTHORZADJUST_LEFT;
890 else
891 eTVA = SDRTEXTVERTADJUST_BOTTOM;
893 break;
894 case mso_anchorBottomCentered:
896 if ( bVerticalText )
897 eTHA = SDRTEXTHORZADJUST_LEFT;
898 else
899 eTVA = SDRTEXTVERTADJUST_BOTTOM;
901 break;
902 default:
906 aSet.Put( SdrTextVertAdjustItem( eTVA ) );
907 aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
909 if (pObj != nullptr)
911 pObj->SetMergedItemSet(aSet);
913 if (bVerticalText)
915 SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
916 if (pTextObj)
917 pTextObj->SetVerticalWriting(true);
920 if ( bIsSimpleDrawingTextBox )
922 if ( nTextRotationAngle )
924 tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
925 rTextRect.GetWidth() : rTextRect.GetHeight();
926 nMinWH /= 2;
927 Point aPivot(rTextRect.TopLeft());
928 aPivot.AdjustX(nMinWH );
929 aPivot.AdjustY(nMinWH );
930 pObj->NbcRotate(aPivot, nTextRotationAngle);
934 if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
936 SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
937 if (pCustomShape)
939 double fExtraTextRotation = 0.0;
940 if ( mnFix16Angle && !( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 4 ) )
941 { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
942 fExtraTextRotation = -mnFix16Angle.get();
944 if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text
945 { // remains unchanged, so we have to take back the flipping here
946 fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped.
948 fExtraTextRotation += nTextRotationAngle.get();
949 if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
951 fExtraTextRotation /= 100.0;
952 SdrCustomShapeGeometryItem aGeometryItem( pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
953 css::beans::PropertyValue aPropVal;
954 aPropVal.Name = "TextRotateAngle";
955 aPropVal.Value <<= fExtraTextRotation;
956 aGeometryItem.SetPropertyValue( aPropVal );
957 pCustomShape->SetMergedItem( aGeometryItem );
961 else if ( mnFix16Angle )
963 // rotate text with shape ?
964 pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
968 else if( !pObj )
970 // simple rectangular objects are ignored by ImportObj() :-(
971 // this is OK for Draw but not for Calc and Writer
972 // cause here these objects have a default border
973 pObj = new SdrRectObj(
974 *pSdrModel,
975 rTextRect);
977 SfxItemSet aSet( pSdrModel->GetItemPool() );
978 ApplyAttributes( rSt, aSet, rObjData );
980 SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
981 if( SfxItemState::DEFAULT == eState )
982 aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
983 pObj->SetMergedItemSet(aSet);
986 // Means that fBehindDocument is set
987 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
988 pImpRec->bDrawHell = true;
989 else
990 pImpRec->bDrawHell = false;
991 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
992 pImpRec->bHidden = true;
993 pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
995 if ( nTextId )
997 pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
998 pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
1001 pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
1002 o3tl::Length::emu, o3tl::Length::twip);
1003 pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
1004 o3tl::Length::emu, o3tl::Length::twip);
1005 pImpRec->nDxWrapDistRight
1006 = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistRight, 114935), o3tl::Length::emu,
1007 o3tl::Length::twip);
1008 pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
1009 o3tl::Length::emu, o3tl::Length::twip);
1010 // 16.16 fraction times total image width or height, as appropriate.
1012 if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt))
1014 pImpRec->pWrapPolygon.reset();
1016 sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0);
1017 rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
1018 bool bOk = false;
1019 if (nNumElemVert && (nElemSizeVert == 8 || nElemSizeVert == 4))
1021 //check if there is enough data in the file to make the
1022 //record sane
1023 // coverity[tainted_data : FALSE] - nElemSizeVert is either 8 or 4 so it has been sanitized
1024 bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert;
1026 if (bOk)
1028 pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
1029 for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
1031 sal_Int32 nX(0), nY(0);
1032 if (nElemSizeVert == 8)
1033 rSt.ReadInt32( nX ).ReadInt32( nY );
1034 else
1036 sal_Int16 nSmallX(0), nSmallY(0);
1037 rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
1038 nX = nSmallX;
1039 nY = nSmallY;
1041 (*(pImpRec->pWrapPolygon))[i].setX( nX );
1042 (*(pImpRec->pWrapPolygon))[i].setY( nY );
1047 pImpRec->nCropFromTop = GetPropertyValue(
1048 DFF_Prop_cropFromTop, 0 );
1049 pImpRec->nCropFromBottom = GetPropertyValue(
1050 DFF_Prop_cropFromBottom, 0 );
1051 pImpRec->nCropFromLeft = GetPropertyValue(
1052 DFF_Prop_cropFromLeft, 0 );
1053 pImpRec->nCropFromRight = GetPropertyValue(
1054 DFF_Prop_cropFromRight, 0 );
1056 sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
1058 if ( !IsHardAttribute( DFF_Prop_fLine ) &&
1059 pImpRec->eShapeType == mso_sptPictureFrame )
1061 nLineFlags &= ~0x08;
1064 pImpRec->eLineStyle = (nLineFlags & 8)
1065 ? static_cast<MSO_LineStyle>(GetPropertyValue(
1066 DFF_Prop_lineStyle,
1067 mso_lineSimple ))
1068 : MSO_LineStyle(USHRT_MAX);
1069 pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
1070 DFF_Prop_lineDashing, mso_lineSolid ));
1072 pImpRec->nFlags = rObjData.nSpFlags;
1074 if( pImpRec->nShapeId )
1076 auto nShapeId = pImpRec->nShapeId;
1077 auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16)
1078 + pImpRec->aTextId.nSequence;
1079 // Complement Import Record List
1080 pImpRec->pObj = pObj;
1081 rImportData.insert(std::move(pImpRec));
1083 // Complement entry in Z Order List with a pointer to this Object
1084 // Only store objects which are not deep inside the tree
1085 if( ( rObjData.nCalledByGroup == 0 )
1087 ( (rObjData.nSpFlags & ShapeFlag::Group)
1088 && (rObjData.nCalledByGroup < 2) )
1091 StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
1094 else
1095 pImpRec.reset();
1098 sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
1099 if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
1101 SvMemoryStream aMemStream;
1102 struct HyperLinksTable hlStr;
1103 aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
1105 // copy from DFF stream to memory stream
1106 std::vector< sal_uInt8 > aBuffer( nBufferSize );
1107 if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
1109 aMemStream.WriteBytes(aBuffer.data(), nBufferSize);
1110 sal_uInt64 nStreamSize = aMemStream.TellEnd();
1111 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
1112 bool bRet = 4 <= nStreamSize;
1113 sal_uInt16 nRawRecId,nRawRecSize;
1114 if( bRet )
1115 aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize );
1116 SwDocShell* pDocShell = m_rReader.m_pDocShell;
1117 if (pDocShell)
1119 m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
1123 if (pObj && !hlStr.hLinkAddr.isEmpty())
1125 SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
1126 if( pInfo )
1128 pInfo->SetShapeId( rObjData.nShapeId );
1129 pInfo->SetHlink( hlStr.hLinkAddr );
1130 if (!hlStr.tarFrame.isEmpty())
1131 pInfo->SetTarFrame( hlStr.tarFrame );
1132 OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt );
1133 if (!aNameStr.isEmpty())
1134 pInfo->SetName( aNameStr );
1139 return pObj;
1143 * Special FastSave - Attributes
1145 void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
1147 if (nLen < 0)
1149 m_bCpxStyle = false;
1150 return;
1152 sal_uInt16 nColl = 0;
1153 if (m_xWwFib->GetFIBVersion() <= ww::eWW2)
1154 nColl = *pData;
1155 else
1156 nColl = SVBT16ToUInt16(pData);
1157 if (nColl < m_vColl.size())
1159 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[nColl] );
1160 m_bCpxStyle = true;
1165 * Read_Majority is for Majority (103) and Majority50 (108)
1167 void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
1172 * Stack
1174 void SwWW8FltControlStack::NewAttr(const SwPosition& rPos,
1175 const SfxPoolItem& rAttr)
1177 OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put"
1178 "fields into the control stack");
1179 OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put"
1180 "input fields into the control stack");
1181 OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put"
1182 "annotations into the control stack");
1183 OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put"
1184 "redlines into the control stack");
1185 SwFltControlStack::NewAttr(rPos, rAttr);
1188 SwFltStackEntry* SwWW8FltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId,
1189 bool bTstEnd, tools::Long nHand, bool )
1191 SwFltStackEntry *pRet = nullptr;
1192 // Doing a textbox, and using the control stack only as a temporary
1193 // collection point for properties which will are not to be set into
1194 // the real document
1195 if (m_rReader.m_xPlcxMan && m_rReader.m_xPlcxMan->GetDoingDrawTextBox())
1197 size_t nCnt = size();
1198 for (size_t i=0; i < nCnt; ++i)
1200 SwFltStackEntry& rEntry = (*this)[i];
1201 if (nAttrId == rEntry.m_pAttr->Which())
1203 DeleteAndDestroy(i--);
1204 --nCnt;
1208 else // Normal case, set the attribute into the document
1209 pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
1210 return pRet;
1213 tools::Long GetListFirstLineIndent(const SwNumFormat &rFormat)
1215 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1216 "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1218 SvxAdjust eAdj = rFormat.GetNumAdjust();
1219 tools::Long nReverseListIndented;
1220 if (eAdj == SvxAdjust::Right)
1221 nReverseListIndented = -rFormat.GetCharTextDistance();
1222 else if (eAdj == SvxAdjust::Center)
1223 nReverseListIndented = rFormat.GetFirstLineOffset()/2;
1224 else
1225 nReverseListIndented = rFormat.GetFirstLineOffset();
1226 return nReverseListIndented;
1229 static tools::Long lcl_GetTrueMargin(SvxFirstLineIndentItem const& rFirstLine,
1230 SvxTextLeftMarginItem const& rLeftMargin, const SwNumFormat &rFormat,
1231 tools::Long &rFirstLinePos)
1233 OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
1234 "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1236 const tools::Long nBodyIndent = rLeftMargin.GetTextLeft();
1237 const tools::Long nFirstLineDiff = rFirstLine.GetTextFirstLineOffset();
1238 rFirstLinePos = nBodyIndent + nFirstLineDiff;
1240 const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
1241 const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
1242 tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
1244 return std::max<tools::Long>(nExtraListIndent, 0);
1247 // #i103711#
1248 // #i105414#
1249 void SyncIndentWithList( SvxFirstLineIndentItem & rFirstLine,
1250 SvxTextLeftMarginItem & rLeftMargin,
1251 const SwNumFormat &rFormat,
1252 const bool bFirstLineOfstSet,
1253 const bool bLeftIndentSet )
1255 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
1257 tools::Long nWantedFirstLinePos;
1258 tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, rLeftMargin, rFormat, nWantedFirstLinePos);
1259 rLeftMargin.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
1260 rFirstLine.SetTextFirstLineOffset(0);
1262 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
1264 if ( !bFirstLineOfstSet && bLeftIndentSet &&
1265 rFormat.GetFirstLineIndent() != 0 )
1267 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1269 else if ( bFirstLineOfstSet && !bLeftIndentSet &&
1270 rFormat.GetIndentAt() != 0 )
1272 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1274 else if (!bFirstLineOfstSet && !bLeftIndentSet )
1276 if ( rFormat.GetFirstLineIndent() != 0 )
1278 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1280 if ( rFormat.GetIndentAt() != 0 )
1282 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1288 const SwNumFormat* SwWW8FltControlStack::GetNumFormatFromStack(const SwPosition &rPos,
1289 const SwTextNode &rTextNode)
1291 const SwNumFormat *pRet = nullptr;
1292 const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
1293 if (pItem && rTextNode.GetNumRule())
1295 if (rTextNode.IsCountedInList())
1297 OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue());
1298 const SwNumRule *pRule = m_rDoc.FindNumRulePtr(sName);
1299 if (pRule)
1300 pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel());
1303 return pRet;
1306 void SwWW8ReferencedFltEndStack::SetAttrInDoc( const SwPosition& rTmpPos,
1307 SwFltStackEntry& rEntry )
1309 switch( rEntry.m_pAttr->Which() )
1311 case RES_FLTR_BOOKMARK:
1313 // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content
1314 // and which is not referenced.
1315 bool bInsertBookmarkIntoDoc = true;
1317 SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
1318 if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
1320 const OUString& rName = pFltBookmark->GetName();
1321 std::set< OUString, SwWW8::ltstr >::const_iterator aResult = m_aReferencedTOCBookmarks.find(rName);
1322 if ( aResult == m_aReferencedTOCBookmarks.end() )
1324 bInsertBookmarkIntoDoc = false;
1327 if ( bInsertBookmarkIntoDoc )
1329 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1331 break;
1333 default:
1334 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1335 break;
1340 void SwWW8FltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
1341 SwFltStackEntry& rEntry)
1343 switch (rEntry.m_pAttr->Which())
1345 case RES_LR_SPACE:
1346 assert(false);
1347 break;
1348 case RES_MARGIN_FIRSTLINE:
1349 case RES_MARGIN_TEXTLEFT:
1352 Loop over the affected nodes and
1353 a) convert the word style absolute indent to indent relative
1354 to any numbering indent active on the nodes
1355 b) adjust the writer style tabstops relative to the old
1356 paragraph indent to be relative to the new paragraph indent
1358 SwPaM aRegion(rTmpPos);
1359 if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
1361 SvxFirstLineIndentItem firstLineNew(RES_MARGIN_FIRSTLINE);
1362 SvxTextLeftMarginItem leftMarginNew(RES_MARGIN_TEXTLEFT);
1363 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1365 SvxFirstLineIndentItem const firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
1366 firstLineNew.SetTextFirstLineOffset(firstLineEntry.GetTextFirstLineOffset(), firstLineEntry.GetPropTextFirstLineOffset());
1367 firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
1369 else
1371 SvxTextLeftMarginItem const leftMarginEntry(*static_cast<SvxTextLeftMarginItem*>(rEntry.m_pAttr.get()));
1372 leftMarginNew.SetTextLeft(leftMarginEntry.GetTextLeft(), leftMarginEntry.GetPropLeft());
1374 SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
1375 SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
1376 for(; nStart <= nEnd; ++nStart)
1378 SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
1379 if (!pNode || !pNode->IsTextNode())
1380 continue;
1382 SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
1383 SvxFirstLineIndentItem firstLineOld(pNd->GetAttr(RES_MARGIN_FIRSTLINE));
1384 SvxTextLeftMarginItem leftMarginOld(pNd->GetAttr(RES_MARGIN_TEXTLEFT));
1385 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1387 leftMarginNew.SetTextLeft(leftMarginOld.GetTextLeft(), leftMarginOld.GetPropLeft());
1389 else
1391 firstLineNew.SetTextFirstLineOffset(firstLineOld.GetTextFirstLineOffset(), firstLineOld.GetPropTextFirstLineOffset());
1392 firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
1395 SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
1397 const SwNumFormat* pNum
1398 = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
1399 if (!pNum)
1401 pNum = GetNumFormatFromTextNode(*pTextNode);
1404 if ( pNum )
1406 // #i103711#
1407 const bool bFirstLineIndentSet =
1408 ( m_rReader.m_aTextNodesHavingFirstLineOfstSet.end() !=
1409 m_rReader.m_aTextNodesHavingFirstLineOfstSet.find( pNode ) );
1410 // #i105414#
1411 const bool bLeftIndentSet =
1412 ( m_rReader.m_aTextNodesHavingLeftIndentSet.end() !=
1413 m_rReader.m_aTextNodesHavingLeftIndentSet.find( pNode ) );
1414 SyncIndentWithList(firstLineNew, leftMarginNew, *pNum,
1415 bFirstLineIndentSet,
1416 bLeftIndentSet );
1419 if (firstLineNew != firstLineOld)
1421 pNd->SetAttr(firstLineNew);
1423 if (leftMarginNew != leftMarginOld)
1425 pNd->SetAttr(leftMarginNew);
1430 break;
1432 case RES_TXTATR_FIELD:
1433 OSL_ENSURE(false, "What is a field doing in the control stack,"
1434 "probably should have been in the endstack");
1435 break;
1437 case RES_TXTATR_ANNOTATION:
1438 OSL_ENSURE(false, "What is an annotation doing in the control stack,"
1439 "probably should have been in the endstack");
1440 break;
1442 case RES_TXTATR_INPUTFIELD:
1443 OSL_ENSURE(false, "What is an input field doing in the control stack,"
1444 "probably should have been in the endstack");
1445 break;
1447 case RES_TXTATR_INETFMT:
1449 SwPaM aRegion(rTmpPos);
1450 if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
1452 SwFrameFormat *pFrame;
1453 // If we have just one single inline graphic then
1454 // don't insert a field for the single frame, set
1455 // the frames hyperlink field attribute directly.
1456 pFrame = SwWW8ImplReader::ContainsSingleInlineGraphic(aRegion);
1457 if (nullptr != pFrame)
1459 const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
1460 rEntry.m_pAttr.get());
1461 SwFormatURL aURL;
1462 aURL.SetURL(pAttr->GetValue(), false);
1463 aURL.SetTargetFrameName(pAttr->GetTargetFrame());
1464 pFrame->SetFormatAttr(aURL);
1466 else
1468 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
1472 break;
1473 default:
1474 SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
1475 break;
1479 const SfxPoolItem* SwWW8FltControlStack::GetFormatAttr(const SwPosition& rPos,
1480 sal_uInt16 nWhich)
1482 const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
1483 if (!pItem)
1485 SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
1486 if (!pNd)
1487 pItem = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
1488 else
1491 If we're hunting for the indent on a paragraph and need to use the
1492 parent style indent, then return the indent in msword format, and
1493 not writer format, because that's the style that the filter works
1494 in (naturally)
1496 if (nWhich == RES_MARGIN_FIRSTLINE
1497 || nWhich == RES_MARGIN_TEXTLEFT
1498 || nWhich == RES_MARGIN_RIGHT)
1500 SfxItemState eState = SfxItemState::DEFAULT;
1501 if (const SfxItemSet *pSet = pNd->GetpSwAttrSet())
1502 eState = pSet->GetItemState(nWhich, false);
1503 if (eState != SfxItemState::SET && m_rReader.m_nCurrentColl < m_rReader.m_vColl.size())
1505 switch (nWhich)
1507 case RES_MARGIN_FIRSTLINE:
1508 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordFirstLine.get();
1509 break;
1510 case RES_MARGIN_TEXTLEFT:
1511 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordLeftMargin.get();
1512 break;
1513 case RES_MARGIN_RIGHT:
1514 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordRightMargin.get();
1515 break;
1521 If we're hunting for a character property, try and exact position
1522 within the text node for lookup
1524 if (pNd->IsTextNode())
1526 const sal_Int32 nPos = rPos.GetContentIndex();
1527 m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
1528 if (pNd->GetTextNode()->GetParaAttr(*m_xScratchSet, nPos, nPos))
1529 pItem = m_xScratchSet->GetItem(nWhich);
1532 if (!pItem)
1533 pItem = &pNd->GetAttr(nWhich);
1536 return pItem;
1539 const SfxPoolItem* SwWW8FltControlStack::GetStackAttr(const SwPosition& rPos,
1540 sal_uInt16 nWhich)
1542 SwFltPosition aFltPos(rPos);
1544 size_t nSize = size();
1545 while (nSize)
1547 const SwFltStackEntry& rEntry = (*this)[ --nSize ];
1548 if (rEntry.m_pAttr->Which() == nWhich)
1550 if ( (rEntry.m_bOpen) ||
1552 (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) &&
1553 (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) &&
1554 (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) &&
1555 (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent)
1559 * e.g. half-open range [0-3) so asking for properties at 3
1560 * means props that end at 3 are not included
1563 return rEntry.m_pAttr.get();
1567 return nullptr;
1570 bool SwWW8FltRefStack::IsFootnoteEdnBkmField(
1571 const SwFormatField& rFormatField,
1572 sal_uInt16& rBkmNo)
1574 const SwField* pField = rFormatField.GetField();
1575 sal_uInt16 nSubType;
1576 if(pField && (SwFieldIds::GetRef == pField->Which())
1577 && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType))
1578 && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty())
1580 const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
1581 IDocumentMarkAccess::const_iterator_t ppBkmk =
1582 pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
1583 if(ppBkmk != pMarkAccess->getAllMarksEnd())
1585 // find Sequence No of corresponding Foot-/Endnote
1586 rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
1587 return true;
1590 return false;
1593 void SwWW8FltRefStack::SetAttrInDoc(const SwPosition& rTmpPos,
1594 SwFltStackEntry& rEntry)
1596 switch (rEntry.m_pAttr->Which())
1599 Look up these in our lists of bookmarks that were changed to
1600 variables, and replace the ref field with a var field, otherwise
1601 do normal (?) strange stuff
1603 case RES_TXTATR_FIELD:
1604 case RES_TXTATR_ANNOTATION:
1605 case RES_TXTATR_INPUTFIELD:
1607 SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
1609 SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
1610 SwField* pField = rFormatField.GetField();
1612 if (!RefToVar(pField, rEntry))
1614 sal_uInt16 nBkmNo;
1615 if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
1617 ::sw::mark::IMark const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
1619 const SwPosition& rBkMrkPos = pMark->GetMarkPos();
1621 SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
1622 if( pText && rBkMrkPos.GetContentIndex() )
1624 SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
1625 rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
1626 if( pFootnote )
1628 sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
1630 static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
1632 if( pFootnote->GetFootnote().IsEndNote() )
1633 static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
1639 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aPaM, *rEntry.m_pAttr);
1640 MoveAttrs(*aPaM.GetPoint());
1642 break;
1643 case RES_FLTR_TOX:
1644 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1645 break;
1646 default:
1647 case RES_FLTR_BOOKMARK:
1648 OSL_ENSURE(false, "EndStck used with non field, not what we want");
1649 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1650 break;
1655 For styles we will do our tabstop arithmetic in word style and adjust them to
1656 writer style after all the styles have been finished and the dust settles as
1657 to what affects what.
1659 For explicit attributes we turn the adjusted writer tabstops back into 0 based
1660 word indexes and we'll turn them back into writer indexes when setting them
1661 into the document. If explicit left indent exist which affects them, then this
1662 is handled when the explicit left indent is set into the document
1664 void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
1666 if (nLen < 0)
1668 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_TABSTOP);
1669 return;
1672 sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
1673 const sal_uInt8* pDel = pData + 1; // Del - Array
1675 sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
1676 const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
1678 short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
1679 if (nRequiredLength > nLen)
1681 // would require more data than available to describe!
1682 // discard invalid record
1683 nIns = 0;
1684 nDel = 0;
1687 WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
1689 std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
1691 const SwFormat * pSty = nullptr;
1692 sal_uInt16 nTabBase;
1693 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
1695 nTabBase = m_vColl[m_nCurrentColl].m_nBase;
1696 if (nTabBase < m_vColl.size()) // Based On
1697 pSty = m_vColl[nTabBase].m_pFormat;
1699 else
1700 { // Text
1701 nTabBase = m_nCurrentColl;
1702 if (m_nCurrentColl < m_vColl.size())
1703 pSty = m_vColl[m_nCurrentColl].m_pFormat;
1704 //TODO: figure out else here
1707 bool bFound = false;
1708 std::unordered_set<size_t> aLoopWatch;
1709 while (pSty && !bFound)
1711 const SvxTabStopItem* pTabs;
1712 bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
1713 &pTabs) == SfxItemState::SET;
1714 if( bFound )
1716 aAttr.reset(pTabs->Clone());
1718 else
1720 sal_uInt16 nOldTabBase = nTabBase;
1721 // If based on another
1722 if (nTabBase < m_vColl.size())
1723 nTabBase = m_vColl[nTabBase].m_nBase;
1725 if (
1726 nTabBase < m_vColl.size() &&
1727 nOldTabBase != nTabBase &&
1728 nTabBase != ww::stiNil
1731 // #i61789: Stop searching when next style is the same as the
1732 // current one (prevent loop)
1733 aLoopWatch.insert(reinterpret_cast<size_t>(pSty));
1734 if (nTabBase < m_vColl.size())
1735 pSty = m_vColl[nTabBase].m_pFormat;
1736 //TODO figure out the else branch
1738 if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
1739 aLoopWatch.end())
1740 pSty = nullptr;
1742 else
1743 pSty = nullptr; // Give up on the search
1747 SvxTabStop aTabStop;
1748 for (short i=0; i < nDel; ++i)
1750 sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
1751 if( nPos != SVX_TAB_NOTFOUND )
1752 aAttr->Remove( nPos );
1755 for (short i=0; i < nIns; ++i)
1757 short nPos = SVBT16ToUInt16(pIns + i*2);
1758 aTabStop.GetTabPos() = nPos;
1759 switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
1761 case 0:
1762 aTabStop.GetAdjustment() = SvxTabAdjust::Left;
1763 break;
1764 case 1:
1765 aTabStop.GetAdjustment() = SvxTabAdjust::Center;
1766 break;
1767 case 2:
1768 aTabStop.GetAdjustment() = SvxTabAdjust::Right;
1769 break;
1770 case 3:
1771 aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
1772 break;
1773 case 4:
1774 continue; // Ignore Bar
1777 switch( pTyp[i].aBits1 >> 3 & 0x7 )
1779 case 0:
1780 aTabStop.GetFill() = ' ';
1781 break;
1782 case 1:
1783 aTabStop.GetFill() = '.';
1784 break;
1785 case 2:
1786 aTabStop.GetFill() = '-';
1787 break;
1788 case 3:
1789 case 4:
1790 aTabStop.GetFill() = '_';
1791 break;
1794 sal_uInt16 nPos2 = aAttr->GetPos( nPos );
1795 if (nPos2 != SVX_TAB_NOTFOUND)
1796 aAttr->Remove(nPos2); // Or else Insert() refuses
1797 aAttr->Insert(aTabStop);
1800 if (nIns || nDel)
1801 NewAttr(*aAttr);
1802 else
1804 // Here we have a tab definition which inserts no extra tabs, or deletes
1805 // no existing tabs. An older version of writer is probably the creator
1806 // of the document :-( . So if we are importing a style we can just
1807 // ignore it. But if we are importing into text we cannot as during
1808 // text SwWW8ImplReader::Read_Tab is called at the begin and end of
1809 // the range the attrib affects, and ignoring it would upset the
1810 // balance
1811 if (!m_pCurrentColl) // not importing into a style
1813 SvxTabStopItem aOrig = pSty ?
1814 pSty->GetFormatAttr(RES_PARATR_TABSTOP) :
1815 m_rDoc.GetAttrPool().GetDefaultItem(RES_PARATR_TABSTOP);
1816 NewAttr(aOrig);
1822 * DOP
1824 void SwWW8ImplReader::ImportDop()
1826 // correct the LastPrinted date in DocumentProperties
1827 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1828 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1829 uno::Reference<document::XDocumentProperties> xDocuProps(
1830 xDPS->getDocumentProperties());
1831 OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null");
1832 if (xDocuProps.is())
1834 DateTime aLastPrinted(
1835 msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
1836 ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
1837 xDocuProps->setPrintDate(uDT);
1840 // COMPATIBILITY FLAGS START
1842 // #i78951# - remember the unknown compatibility options
1843 // so as to export them out
1844 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions1(m_xWDop->GetCompatibilityOptions());
1845 m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions2(m_xWDop->GetCompatibilityOptions2());
1847 // The distance between two paragraphs is the sum of the bottom distance of
1848 // the first paragraph and the top distance of the second one
1849 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, m_xWDop->fDontUseHTMLAutoSpacing);
1850 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, true );
1851 // move tabs on alignment
1852 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, true);
1853 // #i24363# tab stops relative to indent
1854 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, false);
1855 // tdf#117923
1856 m_rDoc.getIDocumentSettingAccess().set(
1857 DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true);
1858 m_rDoc.getIDocumentSettingAccess().set(
1859 DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true);
1860 // tdf#128195
1861 m_rDoc.getIDocumentSettingAccess().set(
1862 DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true);
1863 m_rDoc.getIDocumentSettingAccess().set(
1864 DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA, true);
1865 m_rDoc.getIDocumentSettingAccess().set(
1866 DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND, true);
1868 // Import Default Tabs
1869 tools::Long nDefTabSiz = m_xWDop->dxaTab;
1870 if( nDefTabSiz < 56 )
1871 nDefTabSiz = 709;
1873 // We want exactly one DefaultTab
1874 SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP );
1875 const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default;
1877 m_rDoc.GetAttrPool().SetPoolDefaultItem( aNewTab );
1879 // Import zoom factor
1880 if (m_xWDop->wScaleSaved)
1882 //Import zoom type
1883 sal_Int16 nZoomType;
1884 switch (m_xWDop->zkSaved) {
1885 case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break;
1886 case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break;
1887 case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break;
1888 default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break;
1890 uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({
1891 { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) },
1892 { "VisibleBottom", uno::Any(sal_Int32(0)) },
1893 { "ZoomType", uno::Any(nZoomType) }
1894 }));
1896 rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
1897 xBox->insertByIndex(sal_Int32(0), uno::Any(aViewProps));
1898 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
1899 xViewDataSupplier->setViewData(xBox);
1902 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_VIRTUAL_DEVICE, !m_xWDop->fUsePrinterMetrics);
1903 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, true);
1904 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, true );
1906 // No vertical offsets would lead to e.g. overlap of table and fly frames.
1907 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS, true );
1909 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, !m_xWDop->fNoLeading);
1910 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, false);
1911 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, false); // #i47448#
1912 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, !m_xWDop->fExpShRtn); // #i49277#, #i56856#
1913 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, false); // #i53199#
1914 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, false);
1916 // #i25901# - set new compatibility option
1917 // 'Add paragraph and table spacing at bottom of table cells'
1918 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, true);
1919 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, true);
1921 // #i11860# - set new compatibility option
1922 // 'Use former object positioning' to <false>
1923 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, false);
1925 // #i27767# - set new compatibility option
1926 // 'Consider Wrapping mode when positioning object' to <true>
1927 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, true);
1929 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, false); // #i13832#, #i24135#
1931 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
1933 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, true); // #i3952#
1935 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, true);
1936 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, true);
1937 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, true);
1938 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, true);
1939 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
1940 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true);
1941 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true);
1942 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
1943 // rely on default for HYPHENATE_URLS=false
1945 IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
1946 if (m_xWDop->fDontBreakWrappedTables)
1948 rIDSA.set(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES, true);
1951 // COMPATIBILITY FLAGS END
1953 // Import magic doptypography information, if it's there
1954 if (m_xWwFib->m_nFib > 105)
1955 ImportDopTypography(m_xWDop->doptypography);
1957 // disable form design mode to be able to use imported controls directly
1958 // #i31239# always disable form design mode, not only in protected docs
1959 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1960 if (xDocProps.is())
1962 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1963 if (xInfo.is())
1965 if (xInfo->hasPropertyByName("ApplyFormDesignMode"))
1966 xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::Any(false));
1969 // for the benefit of DOCX - if this is ever saved in that format.
1970 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1971 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1972 { "name", uno::Any(OUString("compatibilityMode")) },
1973 { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
1974 { "val", uno::Any(OUString("11")) } //11: Use features specified in MS-DOC.
1975 }));
1977 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
1978 { "compatSetting", uno::Any(aCompatSetting) }
1979 }));
1981 aGrabBag["CompatSettings"] <<= aValue;
1982 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1985 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
1986 // Treat comments-only like read-only since Writer has no support for that.
1987 // Still allow editing of form fields, without requiring the password.
1988 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
1989 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
1990 m_pDocShell->SetModifyPasswordHash(m_xWDop->lKeyProtDoc);
1991 else if ( xDocProps.is() )
1993 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1994 aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc;
1995 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1998 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
1999 if (rOpt.IsUseEnhancedFields())
2000 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, m_xWDop->fProtEnabled );
2002 if (m_xWDop->iGutterPos)
2004 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::GUTTER_AT_TOP, true);
2008 void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo)
2010 switch (rTypo.m_iLevelOfKinsoku)
2012 case 2: // custom
2014 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
2015 OUString(+rTypo.m_rgxchLPunct));
2016 // unary + makes sure not to accidentally call the
2017 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
2018 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
2019 // prefix leading up to the first NUL
2020 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(rTypo.GetConvertedLang(),
2021 aForbidden);
2022 // Obviously cannot set the standard level 1 for japanese, so
2023 // bail out now while we can.
2024 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
2025 return;
2027 break;
2028 default:
2029 break;
2033 This MS hack means that level 2 of japanese is not in operation, so we put
2034 in what we know are the MS defaults, there is a complementary reverse
2035 hack in the writer. Its our default as well, but we can set it anyway
2036 as a flag for later.
2038 if (!rTypo.m_reserved2)
2040 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
2041 WW8DopTypography::JapanNotEndLevel1);
2042 m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(LANGUAGE_JAPANESE,aForbidden);
2045 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bool(rTypo.m_fKerningPunct));
2046 m_rDoc.getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(rTypo.m_iJustification));
2050 * Footnotes and Endnotes
2052 WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) :
2053 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
2054 mxOldStck(std::move(pRdr->m_xCtrlStck)),
2055 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2056 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2057 mxOldPlcxMan(pRdr->m_xPlcxMan),
2058 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2059 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2060 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2061 mpPrevNumRule(pRdr->m_pPrevNumRule),
2062 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2063 mnInTable(pRdr->m_nInTable),
2064 mnCurrentColl(pRdr->m_nCurrentColl),
2065 mcSymbol(pRdr->m_cSymbol),
2066 mbIgnoreText(pRdr->m_bIgnoreText),
2067 mbSymbol(pRdr->m_bSymbol),
2068 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2069 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2070 mbAnl(pRdr->m_bAnl),
2071 mbInHyperlink(pRdr->m_bInHyperlink),
2072 mbPgSecBreak(pRdr->m_bPgSecBreak),
2073 mbWasParaEnd(pRdr->m_bWasParaEnd),
2074 mbHasBorder(pRdr->m_bHasBorder),
2075 mbFirstPara(pRdr->m_bFirstPara)
2077 pRdr->m_bSymbol = false;
2078 pRdr->m_bHdFtFootnoteEdn = true;
2079 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2080 = pRdr->m_bHasBorder = false;
2081 pRdr->m_bFirstPara = true;
2082 pRdr->m_nInTable = 0;
2083 pRdr->m_pPreviousNumPaM = nullptr;
2084 pRdr->m_pPrevNumRule = nullptr;
2085 pRdr->m_nCurrentColl = 0;
2087 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2088 *pRdr));
2090 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2092 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2094 // Save the attribute manager: we need this as the newly created PLCFx Manager
2095 // access the same FKPs as the old one and their Start-End position changes.
2096 if (pRdr->m_xPlcxMan)
2097 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2099 if (nStartCp != -1)
2101 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2102 mxOldPlcxMan->GetManType(), nStartCp);
2105 maOldApos.push_back(false);
2106 maOldApos.swap(pRdr->m_aApos);
2107 maOldFieldStack.swap(pRdr->m_aFieldStack);
2110 void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr )
2112 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2113 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2114 pRdr->m_pPreviousNumPaM = mpPreviousNumPaM;
2115 pRdr->m_pPrevNumRule = mpPrevNumRule;
2116 pRdr->m_xTableDesc = std::move(mxTableDesc);
2117 pRdr->m_cSymbol = mcSymbol;
2118 pRdr->m_bSymbol = mbSymbol;
2119 pRdr->m_bIgnoreText = mbIgnoreText;
2120 pRdr->m_bHdFtFootnoteEdn = mbHdFtFootnoteEdn;
2121 pRdr->m_bTxbxFlySection = mbTxbxFlySection;
2122 pRdr->m_nInTable = mnInTable;
2123 pRdr->m_bAnl = mbAnl;
2124 pRdr->m_bInHyperlink = mbInHyperlink;
2125 pRdr->m_bWasParaEnd = mbWasParaEnd;
2126 pRdr->m_bPgSecBreak = mbPgSecBreak;
2127 pRdr->m_nCurrentColl = mnCurrentColl;
2128 pRdr->m_bHasBorder = mbHasBorder;
2129 pRdr->m_bFirstPara = mbFirstPara;
2131 // Close all attributes as attributes could be created that extend the Fly
2132 pRdr->DeleteCtrlStack();
2133 pRdr->m_xCtrlStck = std::move(mxOldStck);
2135 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2137 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2138 // place, or somewhere close if that place got destroyed
2139 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2140 pRdr->m_oLastAnchorPos.reset();
2142 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2144 if (xLastAnchorCursor)
2145 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2147 pRdr->DeleteAnchorStack();
2148 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2150 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2152 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2153 pRdr->m_xPlcxMan = mxOldPlcxMan;
2154 if (pRdr->m_xPlcxMan)
2155 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2156 pRdr->m_aApos.swap(maOldApos);
2157 pRdr->m_aFieldStack.swap(maOldFieldStack);
2160 void SwWW8ImplReader::Read_HdFtFootnoteText( const SwNodeIndex* pSttIdx,
2161 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2163 if (nStartCp < 0 || nLen < 0)
2164 return;
2166 // Saves Flags (amongst other things) and resets them
2167 WW8ReaderSave aSave( this );
2169 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2171 // Read Text for Header, Footer or Footnote
2172 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2173 aSave.Restore( this );
2177 * Use authornames, if not available fall back to initials.
2179 tools::Long SwWW8ImplReader::Read_And(WW8PLCFManResult* pRes)
2181 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2182 if (!pSD)
2183 return 0;
2185 const void* pData = pSD->GetData();
2186 if (!pData)
2187 return 0;
2189 OUString sAuthor;
2190 OUString sInitials;
2191 if( m_bVer67 )
2193 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2194 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2195 if (pA)
2196 sAuthor = *pA;
2197 else
2199 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2200 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2201 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2204 else
2206 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2208 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2209 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2210 OUStringBuffer aBuf;
2211 aBuf.setLength(nLen);
2212 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2213 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2214 sInitials = aBuf.makeStringAndClear();
2217 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2218 sAuthor = *pA;
2219 else
2220 sAuthor = sInitials;
2223 sal_uInt32 nDateTime = 0;
2225 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2227 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2228 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2229 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2232 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2234 OUString sText;
2235 std::optional<OutlinerParaObject> pOutliner = ImportAsOutliner( sText, pRes->nCp2OrIdx,
2236 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2238 m_xFormatOfJustInsertedApo.reset();
2239 SwPostItField aPostIt(
2240 static_cast<SwPostItFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), sAuthor,
2241 sText, sInitials, OUString(), aDate );
2242 aPostIt.SetTextObject(std::move(pOutliner));
2244 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2245 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2246 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aEnd, SwFormatField(aPostIt));
2247 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2248 // If this is a range, make sure that it ends after the just inserted character, not before it.
2249 m_xReffedStck->MoveAttrs(*aEnd.GetPoint(), SwFltControlStack::MoveAttrsMode::POSTIT_INSERTED);
2251 return 0;
2254 void SwWW8ImplReader::Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen,
2255 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2257 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2258 OSL_ENSURE(pSttIdx, "impossible");
2259 if (!pSttIdx)
2260 return;
2262 SwPosition aTmpPos(*m_pPaM->GetPoint());
2264 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2266 // tdf#122425: Explicitly remove borders and spacing
2267 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aFlySet(m_rDoc.GetAttrPool());
2268 Reader::ResetFrameFormatAttrs(aFlySet);
2270 SwFlyFrameFormat* pFrame
2271 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2273 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2274 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2275 pFrame->SetFormatAttr( aAnch );
2276 SwFormatFrameSize aSz(SwFrameSize::Minimum, nPageWidth, MINLAY);
2277 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2278 if( eFrameSize != aSz.GetWidthSizeType() )
2279 aSz.SetWidthSizeType( eFrameSize );
2280 pFrame->SetFormatAttr(aSz);
2281 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2282 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2284 // #i43427# - send frame for header/footer into background.
2285 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2286 SdrObject* pFrameObj = CreateContactObject( pFrame );
2287 OSL_ENSURE( pFrameObj,
2288 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2289 if ( pFrameObj )
2291 pFrameObj->SetOrdNum( 0 );
2293 MoveInsideFly(pFrame);
2295 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2297 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2299 MoveOutsideFly(pFrame, aTmpPos);
2302 void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2304 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2305 if (!pSttIdx)
2306 return;
2308 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2310 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2312 *m_pPaM->GetPoint() = aTmpPos;
2315 bool SwWW8ImplReader::isValid_HdFt_CP(WW8_CP nHeaderCP) const
2317 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2318 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2321 bool SwWW8ImplReader::HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt,
2322 int nSect)
2324 if (m_xHdFt)
2326 WW8_CP nStart, nLen;
2327 sal_uInt8 nNumber = 5;
2329 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2331 if (nI & nWhichItems)
2333 bool bOk = true;
2334 if( m_bVer67 )
2335 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2336 else
2338 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2339 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2342 if (bOk)
2343 return true;
2347 return false;
2350 void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2351 const wwSection &rSection)
2353 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2354 SwPageDesc *pPD = rSection.mpPage;
2356 if( !m_xHdFt )
2357 return;
2359 WW8_CP nStart, nLen;
2360 sal_uInt8 nNumber = 5;
2362 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2363 // corresponding to bit fields in grpfIhdt indicating which
2364 // header/footer(s) are present in this section
2365 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2367 if (nI & grpfIhdt)
2369 bool bOk = true;
2370 if( m_bVer67 )
2371 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2372 else
2374 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2375 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2378 bool bUseLeft
2379 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2380 bool bUseFirst
2381 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2383 // If we are loading a first-page header/footer which is not
2384 // actually enabled in this section (it still needs to be
2385 // loaded as it may be inherited by a later section)
2386 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2388 bool bFooter
2389 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2391 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2392 : bUseFirst ? pPD->GetFirstMaster()
2393 : pPD->GetMaster();
2395 SwFrameFormat* pHdFtFormat;
2396 // If we have empty first page header and footer.
2397 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2398 if (bFooter)
2400 m_bIsFooter = true;
2401 //#i17196# Cannot have left without right
2402 if (!bDisabledFirst
2403 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2404 pPD->GetMaster().SetFormatAttr(SwFormatFooter(true));
2405 if (bUseLeft)
2406 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2407 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2408 pPD->GetFirstMaster().SetFormatAttr(SwFormatFooter(true));
2409 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2411 else
2413 m_bIsHeader = true;
2414 //#i17196# Cannot have left without right
2415 if (!bDisabledFirst
2416 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2417 pPD->GetMaster().SetFormatAttr(SwFormatHeader(true));
2418 if (bUseLeft)
2419 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2420 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2421 pPD->GetFirstMaster().SetFormatAttr(SwFormatHeader(true));
2422 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2425 if (bOk)
2427 bool bHackRequired = false;
2428 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2429 bHackRequired = true;
2430 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2431 bHackRequired = true;
2433 if (bHackRequired)
2435 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2436 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2438 else
2439 Read_HdFtText(nStart, nLen, pHdFtFormat);
2441 else if (pPrev)
2442 CopyPageDescHdFt(pPrev, pPD, nI);
2444 m_bIsHeader = m_bIsFooter = false;
2449 bool wwSectionManager::SectionIsProtected(const wwSection &rSection) const
2451 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2454 void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2455 const wwSection *pPrevious)
2457 // Header/Footer not present
2458 if (!rSection.maSep.grpfIhdt)
2459 return;
2461 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2462 if (rSection.mpPage)
2464 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2465 rSection);
2468 // Header/Footer - Update Index
2469 // So that the index is still valid later on
2470 if (mrReader.m_xHdFt)
2471 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2475 void SwWW8ImplReader::AppendTextNode(SwPosition& rPos)
2477 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
2479 const SwNumRule* pRule = nullptr;
2481 if (pText != nullptr)
2482 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2484 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2485 // set it as a separate paragraph property, just like we do for DOCX.
2486 // This is only being used for numbering currently, so limiting to that context.
2487 if (pRule)
2489 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1, RES_TXTATR_CHARFMT,
2490 RES_TXTATR_CHARFMT, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2491 items(m_pPaM->GetDoc().GetAttrPool());
2493 SfxWhichIter aIter(items);
2494 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2496 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2497 if (pItem)
2498 items.Put(*pItem);
2500 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
2501 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2502 pText->SetAttr(item);
2505 if (
2506 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2507 (m_bParaAutoBefore || m_bParaAutoAfter)
2510 // If after spacing is set to auto, set the after space to 0
2511 if (m_bParaAutoAfter)
2512 SetLowerSpacing(*m_pPaM, 0);
2514 // If the previous textnode had numbering and
2515 // and before spacing is set to auto, set before space to 0
2516 if(m_pPrevNumRule && m_bParaAutoBefore)
2517 SetUpperSpacing(*m_pPaM, 0);
2519 // If the previous numbering rule was different we need
2520 // to insert a space after the previous paragraph
2521 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2522 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2524 // cache current paragraph
2525 if(m_pPreviousNumPaM)
2527 delete m_pPreviousNumPaM;
2528 m_pPreviousNumPaM = nullptr;
2531 m_pPreviousNumPaM = new SwPaM(*m_pPaM, m_pPaM);
2532 m_pPrevNumRule = pRule;
2534 else if(!pRule && m_pPreviousNumPaM)
2536 // If the previous paragraph has numbering but the current one does not
2537 // we need to add a space after the previous paragraph
2538 SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
2539 delete m_pPreviousNumPaM;
2540 m_pPreviousNumPaM = nullptr;
2541 m_pPrevNumRule = nullptr;
2543 else
2545 // clear paragraph cache
2546 if(m_pPreviousNumPaM)
2548 delete m_pPreviousNumPaM;
2549 m_pPreviousNumPaM = nullptr;
2551 m_pPrevNumRule = pRule;
2554 // If this is the first paragraph in the document and
2555 // Auto-spacing before paragraph is set,
2556 // set the upper spacing value to 0
2557 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2558 SetUpperSpacing(*m_pPaM, 0);
2560 m_bFirstPara = false;
2562 m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos);
2564 // We can flush all anchored graphics at the end of a paragraph.
2565 m_xAnchorStck->Flush();
2568 bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2570 bool bRet = false;
2571 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2573 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2575 if(pULSpaceItem != nullptr)
2577 SvxULSpaceItem aUL(*pULSpaceItem);
2579 if(bIsUpper)
2580 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2581 else
2582 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2584 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2585 rMyPam.GetPoint()->SetContent(0);
2586 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2587 rMyPam.GetPoint()->SetContent(nEnd);
2588 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2589 bRet = true;
2591 return bRet;
2594 bool SwWW8ImplReader::SetLowerSpacing(SwPaM &rMyPam, int nSpace)
2596 return SetSpacing(rMyPam, nSpace, false);
2599 bool SwWW8ImplReader::SetUpperSpacing(SwPaM &rMyPam, int nSpace)
2601 return SetSpacing(rMyPam, nSpace, true);
2604 sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2606 if (m_bVer67)
2607 return NS_sprm::v6::sprmPTtp;
2608 return nLevel ? NS_sprm::PFInnerTtp::val : NS_sprm::PFTtp::val;
2611 void SwWW8ImplReader::EndSpecial()
2613 // Frame/Table/Anl
2614 if (m_bAnl)
2615 StopAllAnl(); // -> bAnl = false
2617 while(m_aApos.size() > 1)
2619 StopTable();
2620 m_aApos.pop_back();
2621 --m_nInTable;
2622 if (m_aApos[m_nInTable])
2623 StopApo();
2626 if (m_aApos[0])
2627 StopApo();
2629 OSL_ENSURE(!m_nInTable, "unclosed table!");
2632 bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2634 // Frame/Table/Anl
2635 if (m_bInHyperlink)
2636 return false;
2638 rbReSync = false;
2640 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2642 // TabRowEnd
2643 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2645 // Unfortunately, for every paragraph we need to check first whether
2646 // they contain a sprm 29 (0x261B), which starts an APO.
2647 // All other sprms then refer to that APO and not to the normal text
2648 // surrounding it.
2649 // The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2651 // WW: Table in APO is possible (Both Start-Ends occur at the same time)
2652 // WW: APO in Table not possible
2654 // This mean that of a Table is the content of an APO, the APO start needs
2655 // to be edited first, so that the Table remains in the APO and not the
2656 // other way around.
2657 // At the End, however, we need to edit the Table End first as the APO
2658 // must end after that Table (or else we never find the APO End).
2660 // The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2662 // If the Table is within an APO the TabRowEnd Area misses the
2663 // APO settings.
2664 // To not end the APO there, we do not call ProcessApo
2666 // KHZ: When there is a table inside the Apo the Apo-flags are also
2667 // missing for the 2nd, 3rd... paragraphs of each cell.
2669 // 1st look for in-table flag, for 2000+ there is a subtable flag to
2670 // be considered, the sprm 6649 gives the level of the table
2671 sal_uInt8 nCellLevel = 0;
2673 if (m_bVer67)
2674 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2675 else
2677 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2678 if (!nCellLevel)
2679 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2683 WW8_TablePos *pTabPos=nullptr;
2684 WW8_TablePos aTabPos;
2685 if(nCellLevel && !m_bVer67)
2687 WW8PLCFxSave1 aSave;
2688 m_xPlcxMan->GetPap()->Save( aSave );
2689 rbReSync = true;
2690 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2691 WW8_CP nMyStartCp=nStartCp;
2693 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2694 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2695 nCellLevel = *aLevel.pSprm;
2697 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2699 // Bad Table, remain unchanged in level, e.g. #i19667#
2700 if (!bHasRowEnd)
2701 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2703 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2704 pTabPos = &aTabPos;
2706 m_xPlcxMan->GetPap()->Restore( aSave );
2709 // Then look if we are in an Apo
2711 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2713 // Look to see if we are in a Table, but Table in foot/end note not allowed
2714 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2716 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2718 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2719 // WW8TabDesc::TableCellEnd() from making nonsense
2721 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2722 bStopTab = bStartTab = true; // Required to stop and start table
2724 // Test for Anl (Numbering) and process all events in the right order
2725 if( m_bAnl && !bTableRowEnd )
2727 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2728 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2729 if (pSprm13 && aSprm13.nRemainingData >= 1)
2730 { // Still Anl left?
2731 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2732 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2733 || aApo.HasStartStop() // Forced Anl end
2734 || bStopTab || bStartTab )
2736 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2738 else
2740 NextAnlLine( pSprm13 ); // Next Anl Line
2743 else
2744 { // Regular Anl end
2745 StopAllAnl(); // Actual end
2748 if (bStopTab)
2750 StopTable();
2751 m_aApos.pop_back();
2752 --m_nInTable;
2754 if (aApo.mbStopApo)
2756 StopApo();
2757 m_aApos[m_nInTable] = false;
2760 if (aApo.mbStartApo)
2762 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2763 // We need an ReSync after StartApo
2764 // (actually only if the Apo extends past a FKP border)
2765 rbReSync = true;
2767 if (bStartTab)
2769 WW8PLCFxSave1 aSave;
2770 m_xPlcxMan->GetPap()->Save( aSave );
2772 // Numbering for cell borders causes a crash -> no Anls in Tables
2773 if (m_bAnl)
2774 StopAllAnl();
2776 if(m_nInTable < nCellLevel)
2778 if (StartTable(nStartCp))
2779 ++m_nInTable;
2780 else
2781 break;
2782 m_aApos.push_back(false);
2785 if(m_nInTable >= nCellLevel)
2787 // We need an ReSync after StartTable
2788 // (actually only if the Apo extends past a FKP border)
2789 rbReSync = true;
2790 m_xPlcxMan->GetPap()->Restore( aSave );
2793 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2794 return bTableRowEnd;
2797 rtl_TextEncoding SwWW8ImplReader::GetCharSetFromLanguage()
2800 #i22206#/#i52786#
2801 The (default) character set used for a run of text is the default
2802 character set for the version of Word that last saved the document.
2804 This is a bit tentative, more might be required if the concept is correct.
2805 When later version of word write older 6/95 documents the charset is
2806 correctly set in the character runs involved, so it's hard to reproduce
2807 documents that require this to be sure of the process involved.
2809 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
2810 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2811 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2812 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2815 rtl_TextEncoding SwWW8ImplReader::GetCJKCharSetFromLanguage()
2818 #i22206#/#i52786#
2819 The (default) character set used for a run of text is the default
2820 character set for the version of Word that last saved the document.
2822 This is a bit tentative, more might be required if the concept is correct.
2823 When later version of word write older 6/95 documents the charset is
2824 correctly set in the character runs involved, so it's hard to reproduce
2825 documents that require this to be sure of the process involved.
2827 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_CJK_LANGUAGE));
2828 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2829 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2830 return msfilter::util::getBestTextEncodingFromLocale(aLocale);
2833 rtl_TextEncoding SwWW8ImplReader::GetCurrentCharSet()
2836 #i2015
2837 If the hard charset is set use it, if not see if there is an open
2838 character run that has set the charset, if not then fallback to the
2839 current underlying paragraph style.
2841 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2842 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2844 if (!m_bVer67)
2845 eSrcCharSet = GetCharSetFromLanguage();
2846 else if (!m_aFontSrcCharSets.empty())
2847 eSrcCharSet = m_aFontSrcCharSets.top();
2848 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2849 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2850 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2851 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2852 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2853 eSrcCharSet = GetCharSetFromLanguage();
2855 return eSrcCharSet;
2858 //Takashi Ono for CJK
2859 rtl_TextEncoding SwWW8ImplReader::GetCurrentCJKCharSet()
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_aFontSrcCJKCharSets.empty())
2871 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2872 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2873 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2874 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2875 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2876 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2877 eSrcCharSet = GetCJKCharSetFromLanguage();
2879 return eSrcCharSet;
2882 void SwWW8ImplReader::PostProcessAttrs()
2884 if (m_pPostProcessAttrsInfo == nullptr)
2885 return;
2887 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2889 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2891 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2892 *pItem);
2893 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2894 pItem->Which());
2897 m_pPostProcessAttrsInfo.reset();
2901 #i9240#
2902 It appears that some documents that are in a baltic 8 bit encoding which has
2903 some undefined characters can have use made of those characters, in which
2904 case they default to CP1252. If not then it's perhaps that the font encoding
2905 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2906 is always 1252.
2908 So an encoding converter that on an undefined character attempts to
2909 convert from 1252 on the undefined character
2911 static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2912 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2914 const sal_uInt32 nFlags =
2915 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2916 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2917 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2918 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2920 const sal_uInt32 nFlags2 =
2921 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2922 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2923 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2924 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2926 std::size_t nDestChars=0;
2927 std::size_t nConverted=0;
2931 sal_uInt32 nInfo = 0;
2932 sal_Size nThisConverted=0;
2934 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2935 pIn+nConverted, nInLen-nConverted,
2936 pOut+nDestChars, nOutLen-nDestChars,
2937 nFlags, &nInfo, &nThisConverted);
2939 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2941 nConverted += nThisConverted;
2943 if (
2944 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2945 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2948 sal_Size nOtherConverted;
2949 rtl_TextToUnicodeConverter hCP1252Converter =
2950 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2951 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2952 pIn+nConverted, 1,
2953 pOut+nDestChars, nOutLen-nDestChars,
2954 nFlags2, &nInfo, &nOtherConverted);
2955 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2956 nConverted+=1;
2958 } while (nConverted < nInLen);
2960 return nDestChars;
2963 bool SwWW8ImplReader::LangUsesHindiNumbers(LanguageType nLang)
2965 bool bResult = false;
2967 switch (static_cast<sal_uInt16>(nLang))
2969 case 0x1401: // Arabic(Algeria)
2970 case 0x3c01: // Arabic(Bahrain)
2971 case 0xc01: // Arabic(Egypt)
2972 case 0x801: // Arabic(Iraq)
2973 case 0x2c01: // Arabic (Jordan)
2974 case 0x3401: // Arabic(Kuwait)
2975 case 0x3001: // Arabic(Lebanon)
2976 case 0x1001: // Arabic(Libya)
2977 case 0x1801: // Arabic(Morocco)
2978 case 0x2001: // Arabic(Oman)
2979 case 0x4001: // Arabic(Qatar)
2980 case 0x401: // Arabic(Saudi Arabia)
2981 case 0x2801: // Arabic(Syria)
2982 case 0x1c01: // Arabic(Tunisia)
2983 case 0x3801: // Arabic(U.A.E)
2984 case 0x2401: // Arabic(Yemen)
2985 bResult = true;
2986 break;
2987 default:
2988 break;
2991 return bResult;
2994 sal_Unicode SwWW8ImplReader::TranslateToHindiNumbers(sal_Unicode nChar)
2996 if (nChar >= 0x0030 && nChar <= 0x0039)
2997 return nChar + 0x0630;
2999 return nChar;
3002 namespace
3004 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3006 //if read len was in or around that of allocated len, just reuse pStr
3007 if (nAllocLen < pStr->length + 256)
3008 return OUString(pStr, SAL_NO_ACQUIRE);
3009 //otherwise copy the shorter used section to release extra mem
3010 OUString sRet(pStr->buffer, pStr->length);
3011 rtl_uString_release(pStr);
3012 return sRet;
3017 * Return value: true for non special chars
3019 bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3021 sal_Int32 nRequestedStrLen = nEnd - rPos;
3023 OSL_ENSURE(nRequestedStrLen, "String is 0");
3024 if (nRequestedStrLen <= 0)
3025 return true;
3027 WW8_CP nCp;
3028 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3029 if (bFail)
3031 rPos+=nRequestedStrLen;
3032 return true;
3035 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3036 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3037 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3038 if (!bValidPos)
3040 // Swallow missing range, e.g. #i95550#
3041 rPos+=nRequestedStrLen;
3042 return true;
3045 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3046 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3047 if (!nAvailableStrLen)
3049 // Swallow missing range, e.g. #i95550#
3050 rPos+=nRequestedStrLen;
3051 return true;
3054 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3056 // Reset Unicode flag and correct FilePos if needed.
3057 // Note: Seek is not expensive, as we're checking inline whether or not
3058 // the correct FilePos has already been reached.
3059 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3061 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3062 RTL_TEXTENCODING_MS_1252;
3063 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3066 fdo#82904
3068 Older documents exported as word 95 that use unicode aware fonts will
3069 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3070 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3071 pain.
3073 We will try and use a fallback encoding if the conversion from
3074 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3075 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3076 valid RTL_TEXTENCODING_MS_932 by chance :-(
3078 We're not the only ones that struggle with this: Here's the help from
3079 MSOffice 2003 on the topic:
3082 Earlier versions of Microsoft Word were sometimes used in conjunction with
3083 third-party language-processing add-in programs designed to support Chinese or
3084 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3085 results in incorrect text display in more recent versions of Word.
3087 However, you can set options to convert these documents so that text is
3088 displayed correctly. On the Tools menu, click Options, and then click the
3089 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3090 text (to have Word interpret the text as Asian code page data, regardless of
3091 its font) or Automatically detect Asian text (to have Word attempt to determine
3092 which parts of the text are meant to be Asian).
3095 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3096 the language is not Japanese
3099 const SfxPoolItem * pItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
3100 if (pItem != nullptr && LANGUAGE_JAPANESE != static_cast<const SvxLanguageItem *>(pItem)->GetLanguage())
3102 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3103 eSrcCharSet = GetCharSetFromLanguage();
3106 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3107 RTL_TEXTENCODING_MS_1252;
3109 // allocate unicode string data
3110 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3111 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3112 sal_Unicode* pBuffer = xStr->buffer;
3113 sal_Unicode* pWork = pBuffer;
3115 std::unique_ptr<char[]> p8Bits;
3117 rtl_TextToUnicodeConverter hConverter = nullptr;
3118 if (!m_bIsUnicode || m_bVer67)
3119 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3121 if (!m_bIsUnicode)
3122 p8Bits.reset( new char[nStrLen] );
3124 // read the stream data
3125 sal_uInt8 nBCode = 0;
3126 sal_uInt16 nUCode;
3128 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3129 const SfxPoolItem * pItem = GetFormatAttr(RES_CHRATR_CTL_LANGUAGE);
3130 if (pItem != nullptr)
3131 nCTLLang = static_cast<const SvxLanguageItem *>(pItem)->GetLanguage();
3133 sal_Int32 nL2;
3134 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3136 if (m_bIsUnicode)
3137 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3138 else
3140 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3141 nUCode = nBCode;
3144 if (!m_pStrm->good())
3146 rPos = WW8_CP_MAX-10; // -> eof or other error
3147 return true;
3150 if ((32 > nUCode) || (0xa0 == nUCode))
3152 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3153 break; // Special character < 32, == 0xa0 found
3156 if (m_bIsUnicode)
3158 if (!m_bVer67)
3159 *pWork++ = nUCode;
3160 else
3162 if (nUCode >= 0x3000) //0x8000 ?
3164 char aTest[2];
3165 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3166 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3167 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3168 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3169 *pWork++ = aTemp[0];
3171 else
3173 char cTest = static_cast< char >(nUCode & 0x00FF);
3174 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3178 else
3179 p8Bits[nL2] = nBCode;
3182 if (nL2)
3184 const sal_Int32 nEndUsed = !m_bIsUnicode
3185 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3186 : pWork - pBuffer;
3188 if (m_bRegardHindiDigits && m_bBidi && LangUsesHindiNumbers(nCTLLang))
3190 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3191 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3194 xStr->buffer[nEndUsed] = 0;
3195 xStr->length = nEndUsed;
3197 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3198 rPos += nL2;
3199 if (!m_aApos.back()) // a para end in apo doesn't count
3200 m_bWasParaEnd = false; // No CR
3203 if (hConverter)
3204 rtl_destroyTextToUnicodeConverter(hConverter);
3205 return nL2 >= nStrLen;
3208 #define MSASCII SAL_MAX_INT16
3210 namespace
3212 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
3213 sal_Int16 lcl_getScriptType(
3214 const uno::Reference<i18n::XBreakIterator>& rBI,
3215 const OUString &rString, sal_Int32 nPos)
3217 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3218 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3219 nScript = MSASCII;
3220 return nScript;
3223 // We want to know about WEAK segments, so endOfScript isn't
3224 // useful, and see lcl_getScriptType anyway
3225 sal_Int32 lcl_endOfScript(
3226 const uno::Reference<i18n::XBreakIterator>& rBI,
3227 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3229 while (nPos < rString.getLength())
3231 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3232 if (nScript != nNewScript)
3233 break;
3234 ++nPos;
3236 return nPos;
3239 sal_Int32 lcl_getWriterScriptType(
3240 const uno::Reference<i18n::XBreakIterator>& rBI,
3241 const OUString &rString, sal_Int32 nPos)
3243 sal_Int16 nScript = i18n::ScriptType::WEAK;
3245 if (rString.isEmpty())
3246 return nScript;
3248 while (nPos >= 0)
3250 nScript = rBI->getScriptType(rString, nPos);
3251 if (nScript != i18n::ScriptType::WEAK)
3252 break;
3253 --nPos;
3256 return nScript;
3259 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3261 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3264 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3266 // Ignoring CharSet, and ignoring unknown pitch
3267 return rA.GetFamilyName() == rB.GetFamilyName() &&
3268 rA.GetStyleName() == rB.GetStyleName() &&
3269 rA.GetFamily() == rB.GetFamily() &&
3270 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3274 // In writer we categorize text into CJK, CTL and "Western" for everything else.
3275 // Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3276 // NonEastAsian/HighAnsi, with some shared characters and some properties to
3277 // hint as to which way to bias those shared characters.
3279 // That's four categories, we however have three categories. Given that problem
3280 // here we would ideally find out "what would word do" to see what font/language
3281 // word would assign to characters based on the unicode range they fall into and
3282 // hack the word one onto the range we use. However it's unclear what word's
3283 // categorization is. So we don't do that here yet.
3285 // Additional to the categorization, when word encounters weak text for ambiguous
3286 // chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3287 // feature in writer.
3289 // So what we currently do here then is to split our text into non-weak/weak
3290 // sections and uses word's idcthint to determine what font it would use and
3291 // force that on for the segment. Following what we *do* know about word's
3292 // categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3293 // word, something we map to LATIN, so we consider all weak chars in that range
3294 // to auto-bias to LATIN.
3296 // See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
3297 void SwWW8ImplReader::emulateMSWordAddTextToParagraph(const OUString& rAddString)
3299 if (rAddString.isEmpty())
3300 return;
3302 if (m_bFuzzing)
3304 simpleAddTextToParagraph(rAddString);
3305 return;
3308 uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
3309 assert(xBI.is());
3311 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3312 sal_Int32 nLen = rAddString.getLength();
3314 OUString sParagraphText;
3315 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3316 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3317 if (pNd)
3318 sParagraphText = pNd->GetText();
3319 sal_Int32 nParaOffset = sParagraphText.getLength();
3320 sParagraphText = sParagraphText + rAddString;
3322 sal_Int32 nPos = 0;
3323 while (nPos < nLen)
3325 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3326 if (nEnd < 0)
3327 break;
3329 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3330 const sal_uInt16 aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3331 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3332 bool aForced[] = {false, false, false};
3334 int nLclIdctHint = 0xFF;
3335 if (nScript == i18n::ScriptType::WEAK)
3337 const SfxInt16Item *pIdctHint = static_cast<const SfxInt16Item*>(GetFormatAttr(RES_CHRATR_IDCTHINT));
3338 nLclIdctHint = pIdctHint->GetValue();
3340 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3341 nLclIdctHint = 0;
3343 sal_uInt16 nForceFromFontId = 0;
3344 if (nLclIdctHint != 0xFF)
3346 switch (nLclIdctHint)
3348 case 0:
3349 nForceFromFontId = RES_CHRATR_FONT;
3350 break;
3351 case 1:
3352 nForceFromFontId = RES_CHRATR_CJK_FONT;
3353 break;
3354 case 2:
3355 nForceFromFontId = RES_CHRATR_CTL_FONT;
3356 break;
3357 default:
3358 break;
3362 if (nForceFromFontId != 0)
3364 // Now we know that word would use the nForceFromFontId font for this range
3365 // Try and determine what script writer would assign this range to
3367 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3368 nPos + nParaOffset);
3370 bool bWriterWillUseSameFontAsWordAutomatically = false;
3372 if (nWriterScript != i18n::ScriptType::WEAK)
3374 if (
3375 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3376 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3377 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3380 bWriterWillUseSameFontAsWordAutomatically = true;
3382 else
3384 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3385 sal_uInt16 nDestId = aIds[nWriterScript-1];
3386 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(nDestId));
3387 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3391 // Writer won't use the same font as word, so force the issue
3392 if (!bWriterWillUseSameFontAsWordAutomatically)
3394 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3396 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3398 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(aIds[i]));
3399 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3400 if (aForced[i])
3402 pOverriddenItems[i] =
3403 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3405 SvxFontItem aForceFont(*pSourceFont);
3406 aForceFont.SetWhich(aIds[i]);
3407 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3413 simpleAddTextToParagraph(sChunk);
3415 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3417 if (aForced[i])
3419 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3420 if (pOverriddenItems[i])
3421 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3425 nPos = nEnd;
3426 if (nPos < nLen)
3427 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3431 namespace sw {
3433 auto FilterControlChars(std::u16string_view aString) -> OUString
3435 OUStringBuffer buf(aString.size());
3436 for (size_t i = 0; i < aString.size(); ++i)
3438 sal_Unicode const ch(aString[i]);
3439 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3441 buf.append(ch);
3443 else
3445 SAL_INFO("sw.ww8", "filtering control character");
3448 return buf.makeStringAndClear();
3451 } // namespace sw
3453 void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
3455 OUString const addString(sw::FilterControlChars(aAddString));
3457 if (addString.isEmpty())
3458 return;
3460 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3461 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3463 OSL_ENSURE(pNd, "What the hell, where's my text node");
3465 if (!pNd)
3466 return;
3468 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3469 if (nCharsLeft > 0)
3471 if (addString.getLength() <= nCharsLeft)
3473 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3475 else
3477 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3478 AppendTextNode(*m_pPaM->GetPoint());
3479 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3482 else
3484 AppendTextNode(*m_pPaM->GetPoint());
3485 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
3488 m_bReadTable = false;
3492 * Return value: true for para end
3494 bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3495 tools::Long nCpOfs)
3497 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3499 if (m_bSymbol || m_bIgnoreText)
3501 WW8_CP nRequested = nEnd - rPos;
3502 if (m_bSymbol) // Insert special chars
3504 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3505 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3507 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3508 nRequested = nMaxPossible;
3511 if (!linguistic::IsControlChar(m_cSymbol)
3512 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3514 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3516 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(m_cSymbol));
3518 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
3519 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CJK_FONT);
3520 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CTL_FONT);
3523 m_pStrm->SeekRel(nRequested);
3524 rPos = nEnd; // Ignore until attribute end
3525 return false;
3528 while (true)
3530 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3531 return false; // Done
3533 bool bStartLine = ReadChar(rPos, nCpOfs);
3534 rPos++;
3535 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3537 return bStartLine;
3542 bool SwWW8ImplReader::HandlePageBreakChar()
3544 bool bParaEndAdded = false;
3545 // #i1909# section/page breaks should not occur in tables, word
3546 // itself ignores them in this case.
3547 if (!m_nInTable)
3549 bool IsTemp=true;
3550 SwTextNode* pTemp = m_pPaM->GetPointNode().GetTextNode();
3551 if (pTemp && pTemp->GetText().isEmpty()
3552 && (m_bFirstPara || m_bFirstParaOfPage))
3554 IsTemp = false;
3555 AppendTextNode(*m_pPaM->GetPoint());
3556 pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
3559 m_bPgSecBreak = true;
3560 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3562 If it's a 0x0c without a paragraph end before it, act like a
3563 paragraph end, but nevertheless, numbering (and perhaps other
3564 similar constructs) do not exist on the para.
3566 if (!m_bWasParaEnd && IsTemp)
3568 bParaEndAdded = true;
3569 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3571 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3573 pTextNode->SetAttr(
3574 *GetDfltAttr(RES_PARATR_NUMRULE));
3579 return bParaEndAdded;
3582 bool SwWW8ImplReader::ReadChar(tools::Long nPosCp, tools::Long nCpOfs)
3584 bool bNewParaEnd = false;
3585 // Reset Unicode flag and correct FilePos if needed.
3586 // Note: Seek is not expensive, as we're checking inline whether or not
3587 // the correct FilePos has already been reached.
3588 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3589 if (!checkSeek(*m_pStrm, nRequestedPos))
3590 return false;
3592 sal_uInt16 nWCharVal(0);
3593 if( m_bIsUnicode )
3594 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3595 else
3597 sal_uInt8 nBCode(0);
3598 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3599 nWCharVal = nBCode;
3602 sal_Unicode cInsert = '\x0';
3603 bool bParaMark = false;
3605 if ( 0xc != nWCharVal )
3606 m_bFirstParaOfPage = false;
3608 switch (nWCharVal)
3610 case 0:
3611 if (!m_bFuzzing)
3613 // Page number
3614 SwPageNumberField aField(
3615 static_cast<SwPageNumberFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(
3616 SwFieldIds::PageNumber )), PG_RANDOM, SVX_NUM_ARABIC);
3617 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
3619 else
3621 // extremely slow, so skip for fuzzing, and insert a space instead
3622 cInsert = ' ';
3624 break;
3625 case 0xe:
3626 // if there is only one column word treats a column break like a pagebreak.
3627 if (m_aSectionManager.CurrentSectionColCount() < 2)
3628 bParaMark = HandlePageBreakChar();
3629 else if (!m_nInTable)
3631 // Always insert a txtnode for a column break, e.g. ##
3632 SwContentNode *pCntNd=m_pPaM->GetPointContentNode();
3633 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3634 AppendTextNode(*m_pPaM->GetPoint());
3635 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
3637 break;
3638 case 0x7:
3640 bNewParaEnd = true;
3641 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3642 //The last paragraph of each cell is terminated by a special
3643 //paragraph mark called a cell mark. Following the cell mark
3644 //that ends the last cell of a table row, the table row is
3645 //terminated by a special paragraph mark called a row mark
3647 //So the 0x7 should be right at the end of the previous
3648 //range to be a real cell-end.
3649 if (pPap->nOrigStartPos == nPosCp+1 ||
3650 pPap->nOrigStartPos == WW8_CP_MAX)
3652 TabCellEnd(); // Table cell/row end
3654 else
3655 bParaMark = true;
3657 break;
3658 case 0xf:
3659 if( !m_bSpec ) // "Satellite"
3660 cInsert = u'\x00a4';
3661 break;
3662 case 0x14:
3663 if( !m_bSpec ) // "Para End" char
3664 cInsert = u'\x00b5';
3665 //TODO: should this be U+00B6 PILCROW SIGN rather than
3666 // U+00B5 MICRO SIGN?
3667 break;
3668 case 0x15:
3669 if( !m_bSpec ) // Juristenparagraph
3671 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3672 if (aItr == m_aTOXEndCps.end())
3673 cInsert = u'\x00a7';
3674 else
3675 m_aTOXEndCps.erase(aItr);
3677 break;
3678 case 0x9:
3679 cInsert = '\x9'; // Tab
3680 break;
3681 case 0xb:
3682 cInsert = '\xa'; // Hard NewLine
3683 break;
3684 case 0xc:
3685 bParaMark = HandlePageBreakChar();
3686 break;
3687 case 0x1e: // Non-breaking hyphen
3688 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDHYPHEN) );
3689 break;
3690 case 0x1f: // Non-required hyphens
3691 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_SOFTHYPHEN) );
3692 break;
3693 case 0xa0: // Non-breaking spaces
3694 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDBLANK) );
3695 break;
3696 case 0x1:
3698 Current thinking is that if bObj is set then we have a
3699 straightforward "traditional" ole object, otherwise we have a
3700 graphic preview of an associated ole2 object (or a simple
3701 graphic of course)
3703 normally in the canvas field, the code is 0x8 0x1.
3704 in a special case, the code is 0x1 0x1, which yields a simple picture
3707 bool bReadObj = IsInlineEscherHack();
3708 if( bReadObj )
3710 sal_uInt64 nCurPos = m_pStrm->Tell();
3711 sal_uInt16 nWordCode(0);
3713 if( m_bIsUnicode )
3714 m_pStrm->ReadUInt16( nWordCode );
3715 else
3717 sal_uInt8 nByteCode(0);
3718 m_pStrm->ReadUChar( nByteCode );
3719 nWordCode = nByteCode;
3721 if( nWordCode == 0x1 )
3722 bReadObj = false;
3723 m_pStrm->Seek( nCurPos );
3725 if( !bReadObj )
3727 SwFrameFormat *pResult = nullptr;
3728 if (m_bObj)
3729 pResult = ImportOle();
3730 else if (m_bSpec)
3732 SwFrameFormat* pAsCharFlyFormat =
3733 m_rDoc.MakeFrameFormat(OUString(), m_rDoc.GetDfltFrameFormat());
3734 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3735 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3736 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3737 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3741 // If we have a bad 0x1 insert a space instead.
3742 if (!pResult)
3744 cInsert = ' ';
3745 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3746 "WW8: Please report this document, it may have a "
3747 "missing graphic");
3749 else
3751 // reset the flags.
3752 m_bObj = m_bEmbeddObj = false;
3753 m_nObjLocFc = 0;
3757 break;
3758 case 0x8:
3759 if( !m_bObj )
3760 Read_GrafLayer( nPosCp );
3761 break;
3762 case 0xd:
3763 bNewParaEnd = bParaMark = true;
3764 if (m_nInTable > 1)
3767 #i9666#/#i23161#
3768 Yes complex, if there is an entry in the undocumented PLCF
3769 which I believe to be a record of cell and row boundaries
3770 see if the magic bit which I believe to mean cell end is
3771 set. I also think btw that the third byte of the 4 byte
3772 value is the level of the cell
3774 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3775 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3776 pTest->Where() == nPosCp+1+nCpOfs)
3778 WW8_FC nPos;
3779 void *pData;
3780 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3781 : 0;
3782 if (nData & 0x2) // Might be how it works
3784 TabCellEnd();
3785 bParaMark = false;
3788 // tdf#106799: We expect TTP marks to be also cell marks,
3789 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3790 else if (m_bWasTabCellEnd || m_bWasTabRowEnd)
3792 TabCellEnd();
3793 bParaMark = false;
3797 m_bWasTabCellEnd = false;
3799 break; // line end
3800 case 0x5: // Annotation reference
3801 case 0x13:
3802 break;
3803 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3804 if (!m_aFootnoteStack.empty())
3805 cInsert = '?';
3806 break;
3807 default:
3808 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3809 break;
3812 if( '\x0' != cInsert )
3814 OUString sInsert(cInsert);
3815 emulateMSWordAddTextToParagraph(sInsert);
3817 if (!m_aApos.back()) // a para end in apo doesn't count
3818 m_bWasParaEnd = bNewParaEnd;
3819 return bParaMark;
3822 void SwWW8ImplReader::ProcessCurrentCollChange(WW8PLCFManResult& rRes,
3823 bool* pStartAttr, bool bCallProcessSpecial)
3825 sal_uInt16 nOldColl = m_nCurrentColl;
3826 m_nCurrentColl = m_xPlcxMan->GetColl();
3828 // Invalid Style-Id
3829 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3831 m_nCurrentColl = 0;
3832 m_bParaAutoBefore = false;
3833 m_bParaAutoAfter = false;
3835 else
3837 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3838 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3841 if (nOldColl >= m_vColl.size())
3842 nOldColl = 0; // guess! TODO make sure this is what we want
3844 bool bTabRowEnd = false;
3845 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3847 bool bReSync;
3848 // Frame/Table/Autonumbering List Level
3849 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3850 if( bReSync )
3851 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3854 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3856 SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[ m_nCurrentColl ]);
3857 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3858 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3859 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3863 tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3865 tools::Long nSkipChars = 0;
3866 WW8PLCFManResult aRes;
3868 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3869 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3870 aRes.nCurrentCp = rTextPos; // Current Cp position
3872 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3873 if ( bNewSection ) // New Section
3875 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3876 // Create PageDesc and fill it
3877 m_aSectionManager.CreateSep(rTextPos);
3878 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3879 // Create PageDesc and fill it
3880 m_bPgSecBreak = false;
3881 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3884 // New paragraph over Plcx.Fkp.papx
3885 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3887 ProcessCurrentCollChange( aRes, &bStartAttr,
3888 MAN_MASK_NEW_PAP == (aRes.nFlags & MAN_MASK_NEW_PAP) &&
3889 !m_bIgnoreText );
3890 rbStartLine = false;
3893 // position of last CP that's to be ignored
3894 tools::Long nSkipPos = -1;
3896 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3898 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3900 if( bStartAttr ) // WW attributes
3902 if( aRes.nMemLen >= 0 )
3903 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3905 else
3906 EndSprm( aRes.nSprmId ); // Switch off Attr
3908 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3910 if (bStartAttr)
3912 nSkipChars = ImportExtSprm(&aRes);
3913 if (
3914 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3915 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3918 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3919 // Skip Field/Footnote-/End-Note here
3920 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3921 nSkipPos = rTextPos-1;
3924 else
3925 EndExtSprm( aRes.nSprmId );
3929 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3930 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3931 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3933 // Find next Attr position (and Skip attributes of field contents if needed)
3934 if (nSkipChars && !m_bIgnoreText)
3935 m_xCtrlStck->MarkAllAttrsOld();
3936 bool bOldIgnoreText = m_bIgnoreText;
3937 m_bIgnoreText = true;
3938 sal_uInt16 nOldColl = m_nCurrentColl;
3939 bool bDoPlcxManPlusPLus = true;
3940 tools::Long nNext;
3943 if( bDoPlcxManPlusPLus )
3944 m_xPlcxMan->advance();
3945 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3947 if (m_pPostProcessAttrsInfo &&
3948 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3950 m_pPostProcessAttrsInfo->mbCopy = true;
3953 if( (0 <= nNext) && (nSkipPos >= nNext) )
3955 if (nDepthGuard >= 1024)
3957 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3958 nNext = nTextEnd;
3960 else
3961 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3962 bDoPlcxManPlusPLus = false;
3963 m_bIgnoreText = true;
3966 if (m_pPostProcessAttrsInfo &&
3967 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3969 m_pPostProcessAttrsInfo->mbCopy = false;
3972 while( nSkipPos >= nNext );
3973 m_bIgnoreText = bOldIgnoreText;
3974 if( nSkipChars )
3976 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
3977 if( nOldColl != m_xPlcxMan->GetColl() )
3978 ProcessCurrentCollChange(aRes, nullptr, false);
3981 return nNext;
3984 void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
3986 // Do we have attributes?
3987 if( rTextPos >= rNext )
3991 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
3992 if (rTextPos == rNext && rTextPos >= nTextEnd)
3993 break;
3995 while( rTextPos >= rNext );
3998 else if ( rbStartLine )
4000 /* No attributes, but still a new line.
4001 * If a line ends with a line break and paragraph attributes or paragraph templates
4002 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
4003 * is false.
4004 * Due to this we need to set the template here as a kind of special treatment.
4006 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4007 SetTextFormatCollAndListLevel(*m_pPaM, m_vColl[m_nCurrentColl]);
4008 rbStartLine = false;
4013 * CloseAttrEnds to only read the attribute ends at the end of a text or a
4014 * text area (Header, Footnote, ...).
4015 * We ignore attribute starts and fields.
4017 void SwWW8ImplReader::CloseAttrEnds()
4019 // If there are any unclosed sprms then copy them to
4020 // another stack and close the ones that must be closed
4021 std::stack<sal_uInt16> aStack;
4022 m_xPlcxMan->TransferOpenSprms(aStack);
4024 while (!aStack.empty())
4026 sal_uInt16 nSprmId = aStack.top();
4027 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4028 EndSprm(nSprmId);
4029 aStack.pop();
4032 EndSpecial();
4035 bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4037 bool bJoined=false;
4039 bool bStartLine = true;
4040 short nCrCount = 0;
4041 short nDistance = 0;
4043 m_bWasParaEnd = false;
4044 m_nCurrentColl = 0;
4045 m_xCurrentItemSet.reset();
4046 m_nCharFormat = -1;
4047 m_bSpec = false;
4048 m_bPgSecBreak = false;
4050 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4051 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4053 WW8_CP nNext = m_xPlcxMan->Where();
4054 m_xPreviousNode.reset();
4055 sal_uInt8 nDropLines = 0;
4056 SwCharFormat* pNewSwCharFormat = nullptr;
4057 const SwCharFormat* pFormat = nullptr;
4059 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4060 if (!bValidPos)
4061 return false;
4063 WW8_CP l = nStartCp;
4064 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4065 if (nTextLen > nMaxPossible)
4067 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4068 nTextLen = nMaxPossible;
4070 WW8_CP nTextEnd = nStartCp+nTextLen;
4071 while (l < nTextEnd)
4073 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4074 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4076 if (m_pPostProcessAttrsInfo != nullptr)
4077 PostProcessAttrs();
4079 if (l >= nTextEnd)
4080 break;
4082 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4084 // If the previous paragraph was a dropcap then do not
4085 // create a new txtnode and join the two paragraphs together
4086 if (bStartLine && !m_xPreviousNode) // Line end
4088 bool bSplit = true;
4089 if (m_bCareFirstParaEndInToc)
4091 m_bCareFirstParaEndInToc = false;
4092 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4093 bSplit = false;
4095 if (m_bCareLastParaEndInToc)
4097 m_bCareLastParaEndInToc = false;
4098 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4099 bSplit = false;
4101 if (bSplit)
4103 AppendTextNode(*m_pPaM->GetPoint());
4107 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4109 SwTextNode* pEndNd = m_pPaM->GetPointNode().GetTextNode();
4110 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4111 if (pEndNd)
4113 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4115 // Need to reset the font size and text position for the dropcap
4117 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4118 m_xCtrlStck->Delete(aTmp);
4121 // Get the default document dropcap which we can use as our template
4122 const SwFormatDrop* defaultDrop =
4123 static_cast<const SwFormatDrop*>( GetFormatAttr(RES_PARATR_DROP));
4124 SwFormatDrop aDrop(*defaultDrop);
4126 aDrop.GetLines() = nDropLines;
4127 aDrop.GetDistance() = nDistance;
4128 aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
4129 // Word has no concept of a "whole word dropcap"
4130 aDrop.GetWholeWord() = false;
4132 if (pFormat)
4133 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4134 else if(pNewSwCharFormat)
4135 aDrop.SetCharFormat(pNewSwCharFormat);
4137 SwPosition aStart(*pEndNd);
4138 m_xCtrlStck->NewAttr(aStart, aDrop);
4139 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_DROP);
4141 m_xPreviousNode.reset();
4143 else if (m_bDropCap)
4145 // If we have found a dropcap store the textnode
4146 m_xPreviousNode.reset(new TextNodeListener(m_pPaM->GetPointNode().GetTextNode()));
4148 SprmResult aDCS;
4149 if (m_bVer67)
4150 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4151 else
4152 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4154 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4155 nDropLines = (*aDCS.pSprm) >> 3;
4156 else // There is no Drop Cap Specifier hence no dropcap
4157 m_xPreviousNode.reset();
4159 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4160 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4161 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4162 else
4163 nDistance = 0;
4165 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4167 if (m_xCurrentItemSet)
4168 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4170 if (pSwFormatCharFormat)
4171 pFormat = pSwFormatCharFormat->GetCharFormat();
4173 if (m_xCurrentItemSet && !pFormat)
4175 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4176 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4177 m_xCurrentItemSet->ClearItem(RES_CHRATR_ESCAPEMENT);
4178 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4181 m_xCurrentItemSet.reset();
4182 m_bDropCap=false;
4185 if (bStartLine || m_bWasTabRowEnd)
4187 // Call all 64 CRs; not for Header and the like
4188 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4190 if (nTextLen < WW8_CP_MAX/100)
4191 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4192 else
4193 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4194 m_xProgress->Update(m_nProgress); // Update
4198 // If we have encountered a 0x0c which indicates either section of
4199 // pagebreak then look it up to see if it is a section break, and
4200 // if it is not then insert a page break. If it is a section break
4201 // it will be handled as such in the ReadAttrs of the next loop
4202 if (m_bPgSecBreak)
4204 // We need only to see if a section is ending at this cp,
4205 // the plcf will already be sitting on the correct location
4206 // if it is there.
4207 WW8PLCFxDesc aTemp;
4208 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4209 if (m_xPlcxMan->GetSepPLCF())
4210 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4211 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4213 // #i39251# - insert text node for page break, if no one inserted.
4214 // #i43118# - refine condition: the anchor
4215 // control stack has to have entries, otherwise it's not needed
4216 // to insert a text node.
4217 if (!bStartLine && !m_xAnchorStck->empty())
4219 AppendTextNode(*m_pPaM->GetPoint());
4221 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM,
4222 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4223 m_bFirstParaOfPage = true;
4224 m_bPgSecBreak = false;
4229 m_xPreviousNode.reset();
4231 if (m_pPaM->GetPoint()->GetContentIndex())
4232 AppendTextNode(*m_pPaM->GetPoint());
4234 if (!m_bInHyperlink)
4235 bJoined = JoinNode(*m_pPaM);
4237 CloseAttrEnds();
4239 m_xPlcxMan.reset();
4240 return bJoined;
4243 SwWW8ImplReader::SwWW8ImplReader(sal_uInt8 nVersionPara, SotStorage* pStorage,
4244 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4245 : m_pDocShell(rD.GetDocShell())
4246 , m_pStg(pStorage)
4247 , m_pStrm(pSt)
4248 , m_pTableStream(nullptr)
4249 , m_pDataStream(nullptr)
4250 , m_rDoc(rD)
4251 , m_pPaM(nullptr)
4252 , m_aSectionManager(*this)
4253 , m_aExtraneousParas(rD)
4254 , m_aInsertedTables(rD)
4255 , m_aSectionNameGenerator(rD, "WW")
4256 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4257 , m_aParaStyleMapper(rD)
4258 , m_aCharStyleMapper(rD)
4259 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4260 , m_pPreviousNumPaM(nullptr)
4261 , m_pPrevNumRule(nullptr)
4262 , m_pCurrentColl(nullptr)
4263 , m_pDfltTextFormatColl(nullptr)
4264 , m_pStandardFormatColl(nullptr)
4265 , m_pDrawModel(nullptr)
4266 , m_pDrawPg(nullptr)
4267 , m_pNumFieldType(nullptr)
4268 , m_sBaseURL(std::move(aBaseURL))
4269 , m_nIniFlags(0)
4270 , m_nIniFlags1(0)
4271 , m_nFieldFlags(0)
4272 , m_bRegardHindiDigits( false )
4273 , m_bDrawCpOValid( false )
4274 , m_nDrawCpO(0)
4275 , m_nPicLocFc(0)
4276 , m_nObjLocFc(0)
4277 , m_nIniFlyDx(0)
4278 , m_nIniFlyDy(0)
4279 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4280 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4281 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4282 , m_nProgress(0)
4283 , m_nCurrentColl(0)
4284 , m_nFieldNum(0)
4285 , m_nLFOPosition(USHRT_MAX)
4286 , m_nCharFormat(0)
4287 , m_nDrawXOfs(0)
4288 , m_nDrawYOfs(0)
4289 , m_nDrawXOfs2(0)
4290 , m_nDrawYOfs2(0)
4291 , m_cSymbol(0)
4292 , m_nWantedVersion(nVersionPara)
4293 , m_nSwNumLevel(0xff)
4294 , m_nWwNumType(0xff)
4295 , m_pChosenWW8OutlineStyle(nullptr)
4296 , m_nListLevel(MAXLEVEL)
4297 , m_bNewDoc(bNewDoc)
4298 , m_bSkipImages(bSkipImages)
4299 , m_bReadNoTable(false)
4300 , m_bPgSecBreak(false)
4301 , m_bSpec(false)
4302 , m_bObj(false)
4303 , m_bTxbxFlySection(false)
4304 , m_bHasBorder(false)
4305 , m_bSymbol(false)
4306 , m_bIgnoreText(false)
4307 , m_nInTable(0)
4308 , m_bWasTabRowEnd(false)
4309 , m_bWasTabCellEnd(false)
4310 , m_bAnl(false)
4311 , m_bHdFtFootnoteEdn(false)
4312 , m_bFootnoteEdn(false)
4313 , m_bIsHeader(false)
4314 , m_bIsFooter(false)
4315 , m_bIsUnicode(false)
4316 , m_bCpxStyle(false)
4317 , m_bStyNormal(false)
4318 , m_bWWBugNormal(false)
4319 , m_bNoAttrImport(false)
4320 , m_bInHyperlink(false)
4321 , m_bWasParaEnd(false)
4322 , m_bVer67(false)
4323 , m_bVer6(false)
4324 , m_bVer7(false)
4325 , m_bVer8(false)
4326 , m_bEmbeddObj(false)
4327 , m_bCurrentAND_fNumberAcross(false)
4328 , m_bNoLnNumYet(true)
4329 , m_bFirstPara(true)
4330 , m_bFirstParaOfPage(false)
4331 , m_bParaAutoBefore(false)
4332 , m_bParaAutoAfter(false)
4333 , m_bDropCap(false)
4334 , m_nDropCap(0)
4335 , m_bBidi(false)
4336 , m_bReadTable(false)
4337 , m_bLoadingTOXCache(false)
4338 , m_nEmbeddedTOXLevel(0)
4339 , m_bLoadingTOXHyperlink(false)
4340 , m_bCareFirstParaEndInToc(false)
4341 , m_bCareLastParaEndInToc(false)
4342 , m_bNotifyMacroEventRead(false)
4343 , m_bFuzzing(utl::ConfigManager::IsFuzzing())
4345 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4346 m_aApos.push_back(false);
4348 mpCursor = m_rDoc.CreateUnoCursor(rPos);
4351 SwWW8ImplReader::~SwWW8ImplReader()
4355 void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4357 if( pStck )
4359 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4360 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4362 else
4364 OSL_ENSURE( false, "WW stack already deleted" );
4368 void wwSectionManager::SetSegmentToPageDesc(const wwSection &rSection,
4369 bool bIgnoreCols)
4371 SwPageDesc &rPage = *rSection.mpPage;
4373 SetNumberingType(rSection, rPage);
4375 SwFrameFormat &rFormat = rPage.GetMaster();
4377 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4378 mrReader.GraphicCtor();
4380 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4382 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4383 SvxMSDffImportData aData(aRect);
4384 rtl::Reference<SdrObject> pObject;
4385 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4387 // Only handle shape if it is a background shape
4388 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4390 SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND,XATTR_START, XATTR_END>
4391 aSet(rFormat.GetDoc()->GetAttrPool());
4392 mrReader.MatchSdrItemsIntoFlySet(pObject.get(), aSet, mso_lineSimple,
4393 mso_lineSolid, mso_sptRectangle, aRect);
4394 if ( aSet.HasItem(RES_BACKGROUND) )
4395 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4396 else
4397 rFormat.SetFormatAttr(aSet);
4401 wwULSpaceData aULData;
4402 GetPageULData(rSection, aULData);
4403 SetPageULSpaceItems(rFormat, aULData, rSection);
4405 rPage.SetVerticalAdjustment( rSection.mnVerticalAdjustment );
4407 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4409 if (!(rSection.maSep.pgbApplyTo & 1))
4410 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4411 if (!(rSection.maSep.pgbApplyTo & 2))
4412 SwWW8ImplReader::SetPageBorder(rPage.GetFirstMaster(), rSection);
4414 mrReader.SetDocumentGrid(rFormat, rSection);
4417 void wwSectionManager::SetUseOn(wwSection &rSection)
4419 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4420 mrReader.m_xWDop->doptypography.m_f2on1;
4422 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4423 UseOnPage eUse = eUseBase;
4424 if (!mrReader.m_xWDop->fFacingPages)
4425 eUse |= UseOnPage::HeaderShare | UseOnPage::FooterShare;
4426 if (!rSection.HasTitlePage())
4427 eUse |= UseOnPage::FirstShare;
4429 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4430 if (rSection.mpPage)
4431 rSection.mpPage->WriteUseOn(eUse);
4435 * Set the page descriptor on this node, handle the different cases for a text
4436 * node or a table
4438 static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4439 SwDoc &rDoc)
4442 If it's a table here, apply the pagebreak to the table
4443 properties, otherwise we add it to the para at this
4444 position
4446 if (rIdx.GetNode().IsTableNode())
4448 SwTable& rTable =
4449 rIdx.GetNode().GetTableNode()->GetTable();
4450 SwFrameFormat* pApply = rTable.GetFrameFormat();
4451 OSL_ENSURE(pApply, "impossible");
4452 if (pApply)
4453 pApply->SetFormatAttr(rPgDesc);
4455 else
4457 SwPaM aPage(rIdx);
4458 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4463 * Map a word section to a writer page descriptor
4465 SwFormatPageDesc wwSectionManager::SetSwFormatPageDesc(mySegIter const &rIter,
4466 mySegIter const &rStart, bool bIgnoreCols)
4468 if (mrReader.m_bNewDoc && rIter == rStart)
4470 rIter->mpPage =
4471 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD);
4473 else
4475 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4476 SwViewShell::GetShellRes()->GetPageDescName(mnDesc, ShellResource::NORMAL_PAGE),
4477 nullptr, false);
4479 OSL_ENSURE(rIter->mpPage, "no page!");
4480 if (!rIter->mpPage)
4481 return SwFormatPageDesc();
4483 // Set page before hd/ft
4484 const wwSection *pPrevious = nullptr;
4485 if (rIter != rStart)
4486 pPrevious = &(*(rIter-1));
4487 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4488 SetUseOn(*rIter);
4490 // Set hd/ft after set page
4491 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4493 SwFormatPageDesc aRet(rIter->mpPage);
4495 rIter->mpPage->SetFollow(rIter->mpPage);
4497 if (rIter->PageRestartNo())
4498 aRet.SetNumOffset(rIter->PageStartAt());
4500 ++mnDesc;
4501 return aRet;
4504 void wwSectionManager::InsertSegments()
4506 mySegIter aEnd = maSegments.end();
4507 mySegIter aStart = maSegments.begin();
4508 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4510 // If the section is of type "New column" (0x01), then simply insert a column break.
4511 // But only if there actually are columns on the page, otherwise a column break
4512 // seems to be handled like a page break by MSO.
4513 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4515 SwPaM start( aIter->maStart );
4516 mrReader.m_rDoc.getIDocumentContentOperations().InsertPoolItem( start, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
4517 continue;
4520 mySegIter aNext = aIter+1;
4521 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4523 // If two following sections are different in following properties, Word will interpret a continuous
4524 // section break between them as if it was a section break next page.
4525 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4526 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4528 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4529 bool bInsertPageDesc = !bInsertSection;
4530 // HACK Force new pagedesc if left/right margins change, otherwise e.g. floating tables may be anchored improperly.
4531 if( aIter->maSep.dxaLeft != aPrev->maSep.dxaLeft || aIter->maSep.dxaRight != aPrev->maSep.dxaRight )
4532 bInsertPageDesc = true;
4533 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4535 if (bInsertPageDesc)
4538 If a cont section follows this section then we won't be
4539 creating a page desc with 2+ cols as we cannot host a one
4540 col section in a 2+ col pagedesc and make it look like
4541 word. But if the current section actually has columns then
4542 we are forced to insert a section here as well as a page
4543 descriptor.
4546 bool bIgnoreCols = bInsertSection;
4547 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4548 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4549 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4550 (aIter->IsLandScape() == aNext->IsLandScape()));
4552 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4554 bIgnoreCols = true;
4555 if ((aIter->NoCols() > 1) || bProtected)
4556 bInsertSection = true;
4559 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4560 if (!aDesc.GetPageDesc())
4561 continue;
4563 // special case handling for odd/even section break
4564 // a) as before create a new page style for the section break
4565 // b) set Layout of generated page style to right/left ( according
4566 // to section break odd/even )
4567 // c) create a new style to follow the break page style
4568 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4570 // SetSwFormatPageDesc calls some methods that could
4571 // modify aIter (e.g. wwSection ).
4572 // Since we call SetSwFormatPageDesc below to generate the
4573 // 'Following' style of the Break style, it is safer
4574 // to take a copy of the contents of aIter.
4575 wwSection aTmpSection = *aIter;
4576 // create a new following page style
4577 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4578 // restore any contents of aIter trashed by SetSwFormatPageDesc
4579 *aIter = aTmpSection;
4581 // Handle the section break
4582 UseOnPage eUseOnPage = UseOnPage::Left;
4583 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4584 eUseOnPage = UseOnPage::Right;
4586 // Keep the share flags.
4587 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4588 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4591 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4592 // unless it is needed to specify a page number.
4593 if (aIter != aStart || aDesc.GetNumOffset())
4594 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4597 SwTextNode* pTextNd = nullptr;
4598 if (bInsertSection)
4600 // Start getting the bounds of this section
4601 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4602 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4603 if (aNext != aEnd)
4605 aAnchor = aNext->maStart;
4606 aSectPaM.GetPoint()->Assign(aAnchor);
4607 aSectPaM.Move(fnMoveBackward);
4610 const SwPosition* pPos = aSectPaM.GetPoint();
4611 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4612 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4613 if (pTableNd)
4615 pTextNd =
4616 mrReader.m_rDoc.GetNodes().MakeTextNode(aAnchor.GetNode(),
4617 mrReader.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
4619 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4622 aSectPaM.SetMark();
4624 aSectPaM.GetPoint()->Assign(aIter->maStart);
4626 bool bHasOwnHdFt = false;
4628 In this nightmare scenario the continuous section has its own
4629 headers and footers so we will try and find a hard page break
4630 between here and the end of the section and put the headers and
4631 footers there.
4633 if (!bInsertPageDesc)
4635 bHasOwnHdFt =
4636 mrReader.HasOwnHeaderFooter(
4637 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4638 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4641 if (bHasOwnHdFt)
4643 // #i40766# Need to cache the page descriptor in case there is
4644 // no page break in the section
4645 SwPageDesc *pOrig = aIter->mpPage;
4646 bool bFailed = true;
4647 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4648 if (aDesc.GetPageDesc())
4650 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4651 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4652 for(; nStart <= nEnd; ++nStart)
4654 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4655 if (!pNode)
4656 continue;
4657 if (sw::util::HasPageBreak(*pNode))
4659 SwNodeIndex aIdx(*pNode);
4660 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4661 bFailed = false;
4662 break;
4666 if(bFailed)
4668 aIter->mpPage = pOrig;
4672 // End getting the bounds of this section, quite a job eh?
4673 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4674 // The last section if continuous is always unbalanced
4675 if (pRet)
4677 // Set the columns to be UnBalanced if that compatibility option is set
4678 if (mrReader.m_xWDop->fNoColumnBalance)
4679 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4680 else
4682 // Otherwise set to unbalanced if the following section is
4683 // not continuous, (which also means that the last section
4684 // is unbalanced)
4685 if (aNext == aEnd || !aNext->IsContinuous())
4686 pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
4691 if (pTextNd)
4693 SwPaM aTest(*pTextNd);
4694 mrReader.m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4695 pTextNd = nullptr;
4700 void wwExtraneousParas::delete_all_from_doc()
4702 auto aEnd = m_aTextNodes.rend();
4703 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4705 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4706 SwTextNode* pTextNode = rListener.GetTextNode();
4707 rListener.StopListening(pTextNode);
4709 SwPaM aTest(*pTextNode);
4710 m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
4712 m_aTextNodes.clear();
4715 void wwExtraneousParas::insert(SwTextNode *pTextNode)
4717 m_aTextNodes.emplace(pTextNode, this);
4720 void wwExtraneousParas::remove_if_present(SwModify* pModify)
4722 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4723 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4724 if (it == m_aTextNodes.end())
4725 return;
4726 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4727 m_aTextNodes.erase(it);
4730 TextNodeListener::TextNodeListener(SwTextNode* pTextNode)
4731 : m_pTextNode(pTextNode)
4733 m_pTextNode->Add(this);
4736 TextNodeListener::~TextNodeListener()
4738 if (!m_pTextNode)
4739 return;
4740 StopListening(m_pTextNode);
4743 void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4745 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4746 return;
4747 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4748 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4749 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4750 // indicates an underlying bug.
4751 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4752 removed(const_cast<SwModify*>(&rModify));
4755 void TextNodeListener::StopListening(SwModify* pTextNode)
4757 pTextNode->Remove(this);
4758 m_pTextNode = nullptr;
4761 void TextNodeListener::removed(SwModify* pTextNode)
4763 StopListening(pTextNode);
4766 void wwExtraneousParas::ExtraTextNodeListener::removed(SwModify* pTextNode)
4768 m_pOwner->remove_if_present(pTextNode);
4771 void SwWW8ImplReader::StoreMacroCmds()
4773 if (!m_xWwFib->m_lcbCmds)
4774 return;
4776 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4777 if (!bValidPos)
4778 return;
4780 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4782 if (!xRoot.is())
4783 return;
4787 uno::Reference < io::XStream > xStream =
4788 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4789 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4791 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4792 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4793 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4794 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4796 catch (...)
4801 void SwWW8ImplReader::ReadDocVars()
4803 std::vector<OUString> aDocVarStrings;
4804 std::vector<ww::bytes> aDocVarStringIds;
4805 std::vector<OUString> aDocValueStrings;
4806 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4807 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4808 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4809 if (m_bVer67) return;
4811 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4812 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4813 uno::Reference<document::XDocumentProperties> xDocProps(
4814 xDPS->getDocumentProperties());
4815 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4816 uno::Reference<beans::XPropertyContainer> xUserDefinedProps =
4817 xDocProps->getUserDefinedProperties();
4818 OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null");
4820 for(size_t i=0; i<aDocVarStrings.size(); i++)
4822 const OUString &rName = aDocVarStrings[i];
4823 uno::Any aValue;
4824 aValue <<= rName;
4825 try {
4826 xUserDefinedProps->addProperty( rName,
4827 beans::PropertyAttribute::REMOVABLE,
4828 aValue );
4829 } catch (const uno::Exception &) {
4830 // ignore
4836 * Document Info
4838 void SwWW8ImplReader::ReadDocInfo()
4840 if( !m_pStg )
4841 return;
4843 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4844 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4845 uno::Reference<document::XDocumentProperties> xDocProps(
4846 xDPS->getDocumentProperties());
4847 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4849 if (!xDocProps.is())
4850 return;
4852 if ( m_xWwFib->m_fDot )
4854 SfxMedium* pMedium = m_pDocShell->GetMedium();
4855 if ( pMedium )
4857 const OUString& aName = pMedium->GetName();
4858 INetURLObject aURL( aName );
4859 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4860 if ( !sTemplateURL.isEmpty() )
4861 xDocProps->setTemplateURL( sTemplateURL );
4864 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4866 auto nCur = m_pTableStream->Tell();
4867 Sttb aSttb;
4868 // point at tgc record
4869 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4870 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4871 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4872 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4873 OUString aURL;
4874 // attempt to convert to url (won't work for obvious reasons on linux)
4875 if ( !sPath.isEmpty() )
4876 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4877 if (aURL.isEmpty())
4878 xDocProps->setTemplateURL( aURL );
4879 else
4880 xDocProps->setTemplateURL( sPath );
4883 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4886 static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4888 if ( !xPrjNameCache.is() )
4889 return;
4891 INetURLObject aObj;
4892 aObj.SetURL( sTemplatePathOrURL );
4893 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4894 OUString aURL;
4895 if ( bIsURL )
4896 aURL = sTemplatePathOrURL;
4897 else
4899 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4900 aObj.SetURL( aURL );
4904 OUString templateNameWithExt = aObj.GetLastName();
4905 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4906 if ( nIndex != -1 )
4908 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4909 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4912 catch( const uno::Exception& )
4917 namespace {
4919 class WW8Customizations
4921 SvStream* mpTableStream;
4922 WW8Fib mWw8Fib;
4923 public:
4924 WW8Customizations( SvStream*, WW8Fib const & );
4925 void Import( SwDocShell* pShell );
4930 WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4934 void WW8Customizations::Import( SwDocShell* pShell )
4936 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4937 return;
4940 Tcg aTCG;
4941 sal_uInt64 nCur = mpTableStream->Tell();
4942 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4944 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4945 return;
4947 bool bReadResult = aTCG.Read( *mpTableStream );
4948 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4949 if ( !bReadResult )
4951 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4952 return;
4954 aTCG.ImportCustomToolBar( *pShell );
4956 catch(...)
4958 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4962 void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4964 if (m_bFuzzing)
4965 return;
4967 SvtPathOptions aPathOpt;
4968 const OUString& aAddinPath = aPathOpt.GetAddinPath();
4969 uno::Sequence< OUString > sGlobalTemplates;
4971 // first get the autoload addins in the directory STARTUP
4972 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
4974 if( xSFA->isFolder( aAddinPath ) )
4975 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
4977 for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) )
4979 INetURLObject aObj;
4980 aObj.SetURL( rGlobalTemplate );
4981 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4982 OUString aURL;
4983 if ( bIsURL )
4984 aURL = rGlobalTemplate;
4985 else
4986 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
4987 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
4988 continue; // don't try and read the same document as ourselves
4990 tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE );
4992 BasicProjImportHelper aBasicImporter( *m_pDocShell );
4993 // Import vba via oox filter
4994 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
4995 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
4996 // Read toolbars & menus
4997 tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument");
4998 refMainStream->SetEndian(SvStreamEndian::LITTLE);
4999 WW8Fib aWwFib( *refMainStream, 8 );
5000 tools::SvRef<SotStorageStream> xTableStream =
5001 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? OUString(SL::a1Table) : OUString(SL::a0Table), StreamMode::STD_READ);
5003 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5005 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5006 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5007 aGblCustomisations.Import( m_pDocShell );
5012 ErrCode SwWW8ImplReader::CoreLoad(WW8Glossary const *pGloss)
5014 m_rDoc.SetDocumentType( SwDoc::DOCTYPE_MSWORD );
5015 if (m_bNewDoc && m_pStg && !pGloss)
5017 // Initialize RDF metadata, to be able to add statements during the import.
5020 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5021 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
5022 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
5023 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5024 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
5025 uno::Reference<task::XInteractionHandler> xHandler;
5026 xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5028 catch (const uno::Exception&)
5030 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5032 ReadDocInfo();
5035 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5037 if (m_xWwFib->m_fReadOnlyRecommended)
5038 pFibData->setReadOnlyRecommended(true);
5039 else
5040 pFibData->setReadOnlyRecommended(false);
5042 if (m_xWwFib->m_fWriteReservation)
5043 pFibData->setWriteReservation(true);
5044 else
5045 pFibData->setWriteReservation(false);
5047 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5049 ::sw::tExternalDataPointer pSttbfAsoc
5050 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5052 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5054 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5056 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5057 if (pDocShell)
5058 pDocShell->SetReadOnlyUI();
5061 m_pPaM = mpCursor.get();
5063 m_xCtrlStck.reset(new SwWW8FltControlStack(m_rDoc, m_nFieldFlags, *this));
5065 m_xRedlineStack.reset(new sw::util::RedlineStack(m_rDoc));
5068 RefFieldStck: Keeps track of bookmarks which may be inserted as
5069 variables instead.
5071 m_xReffedStck.reset(new SwWW8ReferencedFltEndStack(m_rDoc, m_nFieldFlags));
5072 m_xReffingStck.reset(new SwWW8FltRefStack(m_rDoc, m_nFieldFlags));
5074 m_xAnchorStck.reset(new SwWW8FltAnchorStack(m_rDoc, m_nFieldFlags));
5076 size_t nPageDescOffset = m_rDoc.GetPageDescCnt();
5078 RedlineFlags eMode = RedlineFlags::ShowInsert | RedlineFlags::ShowDelete;
5080 m_oSprmParser.emplace(*m_xWwFib);
5082 // Set handy helper variables
5083 m_bVer6 = (6 == m_xWwFib->m_nVersion);
5084 m_bVer7 = (7 == m_xWwFib->m_nVersion);
5085 m_bVer67 = m_bVer6 || m_bVer7;
5086 m_bVer8 = (8 == m_xWwFib->m_nVersion);
5088 m_eTextCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chse, m_xWwFib->m_lid);
5089 m_eStructCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chseTables, m_xWwFib->m_lid);
5091 m_bWWBugNormal = m_xWwFib->m_nProduct == 0xc03d;
5093 m_xProgress.reset(new ImportProgress(m_pDocShell, 0, 100));
5095 // read Font Table
5096 m_xFonts.reset(new WW8Fonts(*m_pTableStream, *m_xWwFib));
5098 // Document Properties
5099 m_xWDop.reset(new WW8Dop(*m_pTableStream, m_xWwFib->m_nFib, m_xWwFib->m_fcDop,
5100 m_xWwFib->m_lcbDop));
5102 if (m_bNewDoc)
5103 ImportDop();
5106 Import revisioning data: author names
5108 if( m_xWwFib->m_lcbSttbfRMark )
5110 ReadRevMarkAuthorStrTabl(*m_pTableStream,
5111 m_xWwFib->m_fcSttbfRMark,
5112 m_xWwFib->m_lcbSttbfRMark, m_rDoc);
5115 // Initialize our String/ID map for Linked Sections
5116 std::vector<OUString> aLinkStrings;
5117 std::vector<ww::bytes> aStringIds;
5119 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcSttbFnm,
5120 m_xWwFib->m_lcbSttbFnm, m_bVer67 ? 2 : 0, m_eStructCharSet,
5121 aLinkStrings, &aStringIds);
5123 for (size_t i=0; i < aLinkStrings.size() && i < aStringIds.size(); ++i)
5125 const ww::bytes& stringId = aStringIds[i];
5126 if (stringId.size() < sizeof(WW8_STRINGID))
5128 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: WW8_STRINGID is too short");
5129 continue;
5131 const WW8_STRINGID *stringIdStruct = reinterpret_cast<const WW8_STRINGID*>(stringId.data());
5132 m_aLinkStringMap[SVBT16ToUInt16(stringIdStruct->nStringId)] = aLinkStrings[i];
5135 ReadDocVars(); // import document variables as meta information.
5137 m_xProgress->Update(m_nProgress); // Update
5139 m_xLstManager.reset(new WW8ListManager(*m_pTableStream, *this));
5142 first (1) import all styles (see WW8PAR2.CXX)
5143 BEFORE the import of the lists !!
5145 m_xProgress->Update(m_nProgress); // Update
5146 m_xStyles.reset(new WW8RStyle(*m_xWwFib, this)); // Styles
5147 m_xStyles->Import();
5150 In the end: (also see WW8PAR3.CXX)
5152 Go through all Styles and attach respective List Format
5153 AFTER we imported the Styles and AFTER we imported the Lists!
5155 m_xProgress->Update(m_nProgress); // Update
5156 m_xStyles->PostProcessStyles();
5158 if (!m_vColl.empty())
5159 SetOutlineStyles();
5161 m_xSBase.reset(new WW8ScannerBase(m_pStrm,m_pTableStream,m_pDataStream, m_xWwFib.get()));
5163 if (m_xSBase->AreThereFootnotes())
5165 static const SwFootnoteNum eNumA[4] =
5167 FTNNUM_DOC, FTNNUM_CHAPTER, FTNNUM_PAGE, FTNNUM_DOC
5170 SwFootnoteInfo aInfo = m_rDoc.GetFootnoteInfo(); // Copy-Ctor private
5172 aInfo.m_ePos = FTNPOS_PAGE;
5173 aInfo.m_eNum = eNumA[m_xWDop->rncFootnote];
5174 sal_uInt16 nfcFootnoteRef = m_xWDop->nfcFootnoteRef;
5175 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcFootnoteRef));
5176 if( m_xWDop->nFootnote )
5177 aInfo.m_nFootnoteOffset = m_xWDop->nFootnote - 1;
5178 m_rDoc.SetFootnoteInfo( aInfo );
5180 if (m_xSBase->AreThereEndnotes())
5182 SwEndNoteInfo aInfo = m_rDoc.GetEndNoteInfo(); // Same as for Footnote
5183 sal_uInt16 nfcEdnRef = m_xWDop->nfcEdnRef;
5184 aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcEdnRef));
5185 if( m_xWDop->nEdn )
5186 aInfo.m_nFootnoteOffset = m_xWDop->nEdn - 1;
5187 m_rDoc.SetEndNoteInfo( aInfo );
5189 if (m_xSBase->GetEndnoteCount() > 2)
5191 // This compatibility flag only works in easy cases, disable it for anything non-trivial
5192 // for now.
5193 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, false);
5197 if (m_xWwFib->m_lcbPlcfhdd)
5198 m_xHdFt.reset(new WW8PLCF_HdFt(m_pTableStream, *m_xWwFib, *m_xWDop));
5200 if (!m_bNewDoc)
5202 // inserting into an existing document:
5203 // As only complete paragraphs are inserted, the current one
5204 // needs to be split - once or even twice.
5205 const SwPosition* pPos = m_pPaM->GetPoint();
5207 // split current paragraph to get new paragraph for the insertion
5208 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5210 // another split, if insertion position was not at the end of the current paragraph.
5211 SwTextNode const*const pTextNd = pPos->GetNode().GetTextNode();
5212 if ( pTextNd->GetText().getLength() )
5214 m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
5215 // move PaM back to the newly empty paragraph
5216 m_pPaM->Move( fnMoveBackward );
5219 // suppress insertion of tables inside footnotes.
5220 const SwNodeOffset nNd = pPos->GetNodeIndex();
5221 m_bReadNoTable = ( nNd < m_rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
5222 m_rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd );
5225 m_xProgress->Update(m_nProgress); // Update
5227 // loop for each glossary entry and add dummy section node
5228 if (pGloss)
5230 WW8PLCF aPlc(*m_pTableStream, m_xWwFib->m_fcPlcfglsy, m_xWwFib->m_lcbPlcfglsy, 0);
5232 WW8_CP nStart, nEnd;
5233 void* pDummy;
5235 for (int i = 0; i < pGloss->GetNoStrings(); ++i, aPlc.advance())
5237 SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfContent());
5238 SwTextFormatColl* pColl =
5239 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,
5240 false);
5241 SwStartNode *pNode =
5242 m_rDoc.GetNodes().MakeTextSection(aIdx.GetNode(),
5243 SwNormalStartNode,pColl);
5244 m_pPaM->GetPoint()->Assign( pNode->GetIndex()+1 );
5245 aPlc.Get( nStart, nEnd, pDummy );
5246 ReadText(nStart,nEnd-nStart-1,MAN_MAINTEXT);
5249 else // ordinary case
5251 if (m_bNewDoc && m_pStg) /*meaningless for a glossary */
5253 m_pDocShell->SetIsTemplate( m_xWwFib->m_fDot ); // point at tgc record
5254 uno::Reference<document::XDocumentPropertiesSupplier> const
5255 xDocPropSupp(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
5256 uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW );
5258 OUString sCreatedFrom = xDocProps->getTemplateURL();
5259 uno::Reference< container::XNameContainer > xPrjNameCache;
5260 uno::Reference< lang::XMultiServiceFactory> xSF(m_pDocShell->GetModel(), uno::UNO_QUERY);
5261 if ( xSF.is() )
5262 xPrjNameCache.set( xSF->createInstance( "ooo.vba.VBAProjectNameProvider" ), uno::UNO_QUERY );
5264 // Read Global templates
5265 ReadGlobalTemplateSettings( sCreatedFrom, xPrjNameCache );
5267 // Create and insert Word vba Globals
5268 uno::Any aGlobs;
5269 uno::Sequence< uno::Any > aArgs{ uno::Any(m_pDocShell->GetModel()) };
5272 aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.word.Globals", aArgs );
5274 catch (const uno::Exception&)
5276 SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: ooo.vba.word.Globals is not available");
5279 #if HAVE_FEATURE_SCRIPTING
5280 if (!m_bFuzzing)
5282 BasicManager *pBasicMan = m_pDocShell->GetBasicManager();
5283 if (pBasicMan)
5284 pBasicMan->SetGlobalUNOConstant( "VBAGlobals", aGlobs );
5286 #endif
5287 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5288 // Import vba via oox filter
5289 bool bRet = aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5291 lcl_createTemplateToProjectEntry( xPrjNameCache, sCreatedFrom, aBasicImporter.getProjectName() );
5292 WW8Customizations aCustomisations( m_pTableStream, *m_xWwFib );
5293 aCustomisations.Import( m_pDocShell );
5295 if( bRet )
5296 m_rDoc.SetContainsMSVBasic(true);
5298 StoreMacroCmds();
5300 ReadText(0, m_xWwFib->m_ccpText, MAN_MAINTEXT);
5303 m_xProgress->Update(m_nProgress); // Update
5305 if (m_pDrawPg && m_xMSDffManager && m_xMSDffManager->GetShapeOrders())
5307 // Helper array to chain the inserted frames (instead of SdrTextObj)
5308 SvxMSDffShapeTxBxSort aTxBxSort;
5310 // Ensure correct z-order of read Escher objects
5311 sal_uInt16 nShapeCount = m_xMSDffManager->GetShapeOrders()->size();
5313 for (sal_uInt16 nShapeNum=0; nShapeNum < nShapeCount; nShapeNum++)
5315 SvxMSDffShapeOrder *pOrder =
5316 (*m_xMSDffManager->GetShapeOrders())[nShapeNum].get();
5317 // Insert Pointer into new Sort array
5318 if (pOrder->nTxBxComp && pOrder->pFly)
5319 aTxBxSort.insert(pOrder);
5321 // Chain Frames
5322 if( !aTxBxSort.empty() )
5324 SwFormatChain aChain;
5325 for( SvxMSDffShapeTxBxSort::iterator it = aTxBxSort.begin(); it != aTxBxSort.end(); ++it )
5327 SvxMSDffShapeOrder *pOrder = *it;
5329 // Initialize FlyFrame Formats
5330 SwFlyFrameFormat* pFlyFormat = pOrder->pFly;
5331 SwFlyFrameFormat* pNextFlyFormat = nullptr;
5332 SwFlyFrameFormat* pPrevFlyFormat = nullptr;
5334 // Determine successor, if we can
5335 SvxMSDffShapeTxBxSort::iterator tmpIter1 = it;
5336 ++tmpIter1;
5337 if( tmpIter1 != aTxBxSort.end() )
5339 SvxMSDffShapeOrder *pNextOrder = *tmpIter1;
5340 if ((0xFFFF0000 & pOrder->nTxBxComp)
5341 == (0xFFFF0000 & pNextOrder->nTxBxComp))
5342 pNextFlyFormat = pNextOrder->pFly;
5344 // Determine predecessor, if we can
5345 if( it != aTxBxSort.begin() )
5347 SvxMSDffShapeTxBxSort::iterator tmpIter2 = it;
5348 --tmpIter2;
5349 SvxMSDffShapeOrder *pPrevOrder = *tmpIter2;
5350 if ((0xFFFF0000 & pOrder->nTxBxComp)
5351 == (0xFFFF0000 & pPrevOrder->nTxBxComp))
5352 pPrevFlyFormat = pPrevOrder->pFly;
5354 // If successor or predecessor present, insert the
5355 // chain at the FlyFrame Format
5356 if (pNextFlyFormat || pPrevFlyFormat)
5358 aChain.SetNext( pNextFlyFormat );
5359 aChain.SetPrev( pPrevFlyFormat );
5360 pFlyFormat->SetFormatAttr( aChain );
5366 bool isHideRedlines(false);
5368 if (m_bNewDoc)
5370 if( m_xWDop->fRevMarking )
5371 eMode |= RedlineFlags::On;
5372 isHideRedlines = !m_xWDop->fRMView;
5375 m_aInsertedTables.DelAndMakeTableFrames();
5376 m_aSectionManager.InsertSegments();
5378 m_vColl.clear();
5380 m_xStyles.reset();
5382 m_xFormImpl.reset();
5383 GraphicDtor();
5384 m_xMSDffManager.reset();
5385 m_xHdFt.reset();
5386 m_xSBase.reset();
5387 m_xWDop.reset();
5388 m_xFonts.reset();
5389 m_xAtnNames.reset();
5390 m_oSprmParser.reset();
5391 m_xProgress.reset();
5393 m_pDataStream = nullptr;
5394 m_pTableStream = nullptr;
5396 DeleteCtrlStack();
5397 DeleteAnchorStack();
5398 DeleteRefStacks();
5399 m_oLastAnchorPos.reset();//ensure this is deleted before UpdatePageDescs
5400 // ofz#10994 remove any trailing fly paras before processing redlines
5401 m_xWFlyPara.reset();
5402 // ofz#12660 remove any trailing fly paras before deleting extra paras
5403 m_xSFlyPara.reset();
5404 // remove extra paragraphs after attribute ctrl
5405 // stacks etc. are destroyed, and before fields
5406 // are updated
5407 m_aExtraneousParas.delete_all_from_doc();
5408 m_xRedlineStack->closeall(*m_pPaM->GetPoint());
5410 // For i120928,achieve the graphics from the special bookmark with is for graphic bullet
5412 std::vector<const SwGrfNode*> vecBulletGrf;
5413 std::vector<SwFrameFormat*> vecFrameFormat;
5415 IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
5416 if ( pMarkAccess )
5418 IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark( "_PictureBullets" );
5419 if ( ppBkmk != pMarkAccess->getBookmarksEnd() &&
5420 IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::BOOKMARK )
5422 SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().GetNode().GetTextNode();
5424 if ( pTextNode )
5426 const SwpHints* pHints = pTextNode->GetpSwpHints();
5427 for( size_t nHintPos = 0; pHints && nHintPos < pHints->Count(); ++nHintPos)
5429 const SwTextAttr *pHt = pHints->Get(nHintPos);
5430 if (pHt->Which() != RES_TXTATR_FLYCNT)
5431 continue;
5432 const sal_Int32 st = pHt->GetStart();
5433 if (st >= (*ppBkmk)->GetMarkStart().GetContentIndex())
5435 SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
5436 vecFrameFormat.push_back(pFrameFormat);
5437 const SwNodeIndex* pNdIdx = pFrameFormat->GetContent().GetContentIdx();
5438 const SwNodes* pNodesArray = (pNdIdx != nullptr)
5439 ? &(pNdIdx->GetNodes())
5440 : nullptr;
5441 const SwGrfNode *pGrf = (pNodesArray != nullptr)
5442 ? (*pNodesArray)[pNdIdx->GetIndex() + 1]->GetGrfNode()
5443 : nullptr;
5444 vecBulletGrf.push_back(pGrf);
5447 // update graphic bullet information
5448 size_t nCount = m_xLstManager->GetWW8LSTInfoNum();
5449 for (size_t i = 0; i < nCount; ++i)
5451 SwNumRule* pRule = m_xLstManager->GetNumRule(i);
5452 for (sal_uInt16 j = 0; j < MAXLEVEL; ++j)
5454 SwNumFormat aNumFormat(pRule->Get(j));
5455 const sal_Int16 nType = aNumFormat.GetNumberingType();
5456 const sal_uInt16 nGrfBulletCP = aNumFormat.GetGrfBulletCP();
5457 if ( nType == SVX_NUM_BITMAP
5458 && vecBulletGrf.size() > nGrfBulletCP
5459 && vecBulletGrf[nGrfBulletCP] != nullptr )
5461 Graphic aGraphic = vecBulletGrf[nGrfBulletCP]->GetGrf();
5462 SvxBrushItem aBrush(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
5463 const vcl::Font& aFont = numfunc::GetDefBulletFont();
5464 int nHeight = aFont.GetFontHeight() * 12;
5465 Size aPrefSize( aGraphic.GetPrefSize());
5466 if (aPrefSize.Height() * aPrefSize.Width() != 0 )
5468 int nWidth = (nHeight * aPrefSize.Width()) / aPrefSize.Height();
5469 Size aSize(nWidth, nHeight);
5470 aNumFormat.SetGraphicBrush(&aBrush, &aSize);
5472 else
5474 aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
5475 aNumFormat.SetBulletChar(0x2190);
5477 pRule->Set( j, aNumFormat );
5481 // Remove additional pictures
5482 for (SwFrameFormat* p : vecFrameFormat)
5484 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat(p);
5489 m_xLstManager.reset();
5492 m_oPosAfterTOC.reset();
5493 m_xRedlineStack.reset();
5494 mpCursor.reset();
5495 m_pPaM = nullptr;
5497 UpdateFields();
5499 // delete the pam before the call for hide all redlines (Bug 73683)
5500 if (m_bNewDoc)
5501 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(eMode);
5503 UpdatePageDescs(m_rDoc, nPageDescOffset);
5505 // can't set it on the layout or view shell because it doesn't exist yet
5506 m_rDoc.GetDocumentRedlineManager().SetHideRedlines(isHideRedlines);
5508 return ERRCODE_NONE;
5511 ErrCode SwWW8ImplReader::SetSubStreams(tools::SvRef<SotStorageStream> &rTableStream,
5512 tools::SvRef<SotStorageStream> &rDataStream)
5514 ErrCode nErrRet = ERRCODE_NONE;
5515 // 6 stands for "6 OR 7", 7 stands for "ONLY 7"
5516 switch (m_xWwFib->m_nVersion)
5518 case 6:
5519 case 7:
5520 m_pTableStream = m_pStrm;
5521 m_pDataStream = m_pStrm;
5522 break;
5523 case 8:
5524 if(!m_pStg)
5526 OSL_ENSURE( m_pStg, "Version 8 always needs to have a Storage!!" );
5527 nErrRet = ERR_SWG_READ_ERROR;
5528 break;
5531 rTableStream = m_pStg->OpenSotStream(
5532 m_xWwFib->m_fWhichTableStm ? OUString(SL::a1Table) : OUString(SL::a0Table),
5533 StreamMode::STD_READ);
5535 m_pTableStream = rTableStream.get();
5536 m_pTableStream->SetEndian( SvStreamEndian::LITTLE );
5538 rDataStream = m_pStg->OpenSotStream(SL::aData, StreamMode::STD_READ);
5540 if (rDataStream.is() && ERRCODE_NONE == rDataStream->GetError())
5542 m_pDataStream = rDataStream.get();
5543 m_pDataStream->SetEndian(SvStreamEndian::LITTLE);
5545 else
5546 m_pDataStream = m_pStrm;
5547 break;
5548 default:
5549 // Program error!
5550 OSL_ENSURE( false, "We forgot to encode nVersion!" );
5551 nErrRet = ERR_SWG_READ_ERROR;
5552 break;
5554 return nErrRet;
5557 namespace
5559 SvStream* MakeTemp(std::optional<utl::TempFileFast>& roTempFile)
5561 roTempFile.emplace();
5562 return roTempFile->GetStream(StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
5565 #define WW_BLOCKSIZE 0x200
5567 void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut)
5569 const std::size_t nLen = rIn.TellEnd();
5570 rIn.Seek(0);
5572 sal_uInt8 in[WW_BLOCKSIZE];
5573 for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
5575 std::size_t nBS = std::min<size_t>(nLen - nI, WW_BLOCKSIZE);
5576 nBS = rIn.ReadBytes(in, nBS);
5577 rCtx.InitCipher(nBlock);
5578 rCtx.Decode(in, nBS, in, nBS);
5579 rOut.WriteBytes(in, nBS);
5583 void DecryptXOR(msfilter::MSCodec_XorWord95 &rCtx, SvStream &rIn, SvStream &rOut)
5585 std::size_t nSt = rIn.Tell();
5586 std::size_t nLen = rIn.TellEnd();
5588 rCtx.InitCipher();
5589 rCtx.Skip(nSt);
5591 sal_uInt8 in[0x4096];
5592 for (std::size_t nI = nSt; nI < nLen; nI += 0x4096)
5594 std::size_t nBS = std::min<size_t>(nLen - nI, 0x4096 );
5595 nBS = rIn.ReadBytes(in, nBS);
5596 rCtx.Decode(in, nBS);
5597 rOut.WriteBytes(in, nBS);
5601 // moan, copy and paste :-(
5602 OUString QueryPasswordForMedium(SfxMedium& rMedium)
5604 OUString aPassw;
5606 const SfxItemSet* pSet = rMedium.GetItemSet();
5607 const SfxStringItem *pPasswordItem;
5609 if(pSet && (pPasswordItem = pSet->GetItemIfSet(SID_PASSWORD)))
5610 aPassw = pPasswordItem->GetValue();
5611 else
5615 uno::Reference< task::XInteractionHandler > xHandler( rMedium.GetInteractionHandler() );
5616 if( xHandler.is() )
5618 rtl::Reference<::comphelper::DocPasswordRequest> pRequest = new ::comphelper::DocPasswordRequest(
5619 ::comphelper::DocPasswordRequestType::MS, task::PasswordRequestMode_PASSWORD_ENTER,
5620 INetURLObject(rMedium.GetOrigURL())
5621 .GetLastName(INetURLObject::DecodeMechanism::WithCharset));
5623 xHandler->handle( pRequest );
5625 if( pRequest->isPassword() )
5626 aPassw = pRequest->getPassword();
5629 catch( const uno::Exception& )
5634 return aPassw;
5637 uno::Sequence< beans::NamedValue > InitXorWord95Codec( ::msfilter::MSCodec_XorWord95& rCodec, SfxMedium& rMedium, WW8Fib const * pWwFib )
5639 uno::Sequence< beans::NamedValue > aEncryptionData;
5640 const SfxUnoAnyItem* pEncryptionData = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
5641 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5642 aEncryptionData.realloc( 0 );
5644 if ( !aEncryptionData.hasElements() )
5646 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5648 OString sPassword(OUStringToOString(sUniPassword,
5649 WW8Fib::GetFIBCharset(pWwFib->m_chseTables, pWwFib->m_lid)));
5651 sal_Int32 nLen = sPassword.getLength();
5652 if( nLen <= 15 )
5654 sal_uInt8 pPassword[16];
5655 memcpy(pPassword, sPassword.getStr(), nLen);
5656 memset(pPassword+nLen, 0, sizeof(pPassword)-nLen);
5658 rCodec.InitKey( pPassword );
5659 aEncryptionData = rCodec.GetEncryptionData();
5661 // the export supports RC4 algorithm only, so we have to
5662 // generate the related EncryptionData as well, so that Save
5663 // can export the document without asking for a password;
5664 // as result there will be EncryptionData for both algorithms
5665 // in the MediaDescriptor
5666 ::msfilter::MSCodec_Std97 aCodec97;
5668 rtlRandomPool aRandomPool = rtl_random_createPool();
5669 sal_uInt8 pDocId[ 16 ];
5670 rtl_random_getBytes( aRandomPool, pDocId, 16 );
5672 rtl_random_destroyPool( aRandomPool );
5674 sal_uInt16 pStd97Pass[16] = {};
5675 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5676 pStd97Pass[nChar] = sUniPassword[nChar];
5678 aCodec97.InitKey( pStd97Pass, pDocId );
5680 // merge the EncryptionData, there should be no conflicts
5681 ::comphelper::SequenceAsHashMap aEncryptionHash( aEncryptionData );
5682 aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
5683 aEncryptionHash >> aEncryptionData;
5687 return aEncryptionData;
5690 uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 const pDocId[16], SfxMedium& rMedium)
5692 uno::Sequence< beans::NamedValue > aEncryptionData;
5693 const SfxUnoAnyItem* pEncryptionData = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
5694 if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
5695 aEncryptionData.realloc( 0 );
5697 if ( !aEncryptionData.hasElements() )
5699 OUString sUniPassword = QueryPasswordForMedium( rMedium );
5701 sal_Int32 nLen = sUniPassword.getLength();
5702 if ( nLen <= 15 )
5704 sal_uInt16 pPassword[16] = {};
5705 for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
5706 pPassword[nChar] = sUniPassword[nChar];
5708 rCodec.InitKey( pPassword, pDocId );
5709 aEncryptionData = rCodec.GetEncryptionData();
5713 return aEncryptionData;
5717 //TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different
5718 //stream thing
5719 static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream)
5721 //It is possible there are other variants in existence but these
5722 //are the defaults I get with Word 2013
5724 rStream.ReadUInt32(info.header.flags);
5725 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
5726 return false;
5728 sal_uInt32 nHeaderSize(0);
5729 rStream.ReadUInt32(nHeaderSize);
5730 sal_uInt32 actualHeaderSize = sizeof(info.header);
5732 if (nHeaderSize < actualHeaderSize)
5733 return false;
5735 rStream.ReadUInt32(info.header.flags);
5736 rStream.ReadUInt32(info.header.sizeExtra);
5737 rStream.ReadUInt32(info.header.algId);
5738 rStream.ReadUInt32(info.header.algIdHash);
5739 rStream.ReadUInt32(info.header.keyBits);
5740 rStream.ReadUInt32(info.header.providedType);
5741 rStream.ReadUInt32(info.header.reserved1);
5742 rStream.ReadUInt32(info.header.reserved2);
5744 rStream.SeekRel(nHeaderSize - actualHeaderSize);
5746 rStream.ReadUInt32(info.verifier.saltSize);
5747 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
5748 return false;
5749 rStream.ReadBytes(&info.verifier.salt, sizeof(info.verifier.salt));
5750 rStream.ReadBytes(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
5752 rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize);
5753 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
5754 return false;
5755 rStream.ReadBytes(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
5757 // check flags and algorithm IDs, required are AES128 and SHA-1
5758 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
5759 return false;
5761 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
5762 return false;
5764 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
5765 return false;
5767 // hash algorithm ID 0 defaults to SHA-1 too
5768 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
5769 return false;
5771 return true;
5774 ErrCode SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
5776 ErrCode nErrRet = ERRCODE_NONE;
5777 if (pGloss)
5778 m_xWwFib = pGloss->GetFib();
5779 else
5780 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5782 if (m_xWwFib->m_nFibError)
5783 nErrRet = ERR_SWG_READ_ERROR;
5785 tools::SvRef<SotStorageStream> xTableStream, xDataStream;
5787 if (!nErrRet)
5788 nErrRet = SetSubStreams(xTableStream, xDataStream);
5790 std::optional<utl::TempFileFast> oTempMain;
5791 std::optional<utl::TempFileFast> oTempTable;
5792 std::optional<utl::TempFileFast> oTempData;
5793 SvStream* pDecryptMain = nullptr;
5794 SvStream* pDecryptTable = nullptr;
5795 SvStream* pDecryptData = nullptr;
5797 bool bDecrypt = false;
5798 enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other;
5799 if (m_xWwFib->m_fEncrypted && !nErrRet)
5801 if (!pGloss)
5803 bDecrypt = true;
5804 if (8 != m_xWwFib->m_nVersion)
5805 eAlgo = XOR;
5806 else
5808 if (m_xWwFib->m_nKey != 0)
5809 eAlgo = XOR;
5810 else
5812 m_pTableStream->Seek(0);
5813 sal_uInt32 nEncType(0);
5814 m_pTableStream->ReadUInt32(nEncType);
5815 if (nEncType == msfilter::VERSION_INFO_1997_FORMAT)
5816 eAlgo = RC4;
5817 else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2)
5818 eAlgo = RC4CryptoAPI;
5824 if (bDecrypt)
5826 nErrRet = ERRCODE_SVX_WRONGPASS;
5827 SfxMedium* pMedium = m_pDocShell->GetMedium();
5829 if ( pMedium )
5831 switch (eAlgo)
5833 default:
5834 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5835 break;
5836 case XOR:
5838 msfilter::MSCodec_XorWord95 aCtx;
5839 uno::Sequence< beans::NamedValue > aEncryptionData = InitXorWord95Codec(aCtx, *pMedium, m_xWwFib.get());
5841 // if initialization has failed the EncryptionData should be empty
5842 if (aEncryptionData.hasElements() && aCtx.VerifyKey(m_xWwFib->m_nKey, m_xWwFib->m_nHash))
5844 nErrRet = ERRCODE_NONE;
5845 pDecryptMain = MakeTemp(oTempMain);
5847 m_pStrm->Seek(0);
5848 size_t nUnencryptedHdr =
5849 (8 == m_xWwFib->m_nVersion) ? 0x44 : 0x34;
5850 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5851 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5852 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5853 pIn.reset();
5855 DecryptXOR(aCtx, *m_pStrm, *pDecryptMain);
5857 if (!m_pTableStream || m_pTableStream == m_pStrm)
5858 m_pTableStream = pDecryptMain;
5859 else
5861 pDecryptTable = MakeTemp(oTempTable);
5862 DecryptXOR(aCtx, *m_pTableStream, *pDecryptTable);
5863 m_pTableStream = pDecryptTable;
5866 if (!m_pDataStream || m_pDataStream == m_pStrm)
5867 m_pDataStream = pDecryptMain;
5868 else
5870 pDecryptData = MakeTemp(oTempData);
5871 DecryptXOR(aCtx, *m_pDataStream, *pDecryptData);
5872 m_pDataStream = pDecryptData;
5875 pMedium->GetItemSet()->ClearItem( SID_PASSWORD );
5876 pMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5879 break;
5880 case RC4:
5881 case RC4CryptoAPI:
5883 std::unique_ptr<msfilter::MSCodec97> xCtx;
5884 msfilter::RC4EncryptionInfo info;
5885 bool bCouldReadHeaders;
5887 if (eAlgo == RC4)
5889 xCtx.reset(new msfilter::MSCodec_Std97);
5890 assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5);
5891 bCouldReadHeaders =
5892 checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) &&
5893 checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) &&
5894 checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5);
5896 else
5898 xCtx.reset(new msfilter::MSCodec_CryptoAPI);
5899 bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream);
5902 // if initialization has failed the EncryptionData should be empty
5903 uno::Sequence< beans::NamedValue > aEncryptionData;
5904 if (bCouldReadHeaders)
5905 aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium);
5906 else
5907 nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
5908 if (aEncryptionData.hasElements() && xCtx->VerifyKey(info.verifier.encryptedVerifier,
5909 info.verifier.encryptedVerifierHash))
5911 nErrRet = ERRCODE_NONE;
5913 pDecryptMain = MakeTemp(oTempMain);
5915 m_pStrm->Seek(0);
5916 std::size_t nUnencryptedHdr = 0x44;
5917 std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
5918 nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
5920 DecryptRC4(*xCtx, *m_pStrm, *pDecryptMain);
5922 pDecryptMain->Seek(0);
5923 pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
5924 pIn.reset();
5926 pDecryptTable = MakeTemp(oTempTable);
5927 DecryptRC4(*xCtx, *m_pTableStream, *pDecryptTable);
5928 m_pTableStream = pDecryptTable;
5930 if (!m_pDataStream || m_pDataStream == m_pStrm)
5931 m_pDataStream = pDecryptMain;
5932 else
5934 pDecryptData = MakeTemp(oTempData);
5935 DecryptRC4(*xCtx, *m_pDataStream, *pDecryptData);
5936 m_pDataStream = pDecryptData;
5939 pMedium->GetItemSet()->ClearItem( SID_PASSWORD );
5940 pMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
5943 break;
5947 if (nErrRet == ERRCODE_NONE)
5949 m_pStrm = pDecryptMain;
5951 m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
5952 if (m_xWwFib->m_nFibError)
5953 nErrRet = ERR_SWG_READ_ERROR;
5957 if (!nErrRet)
5958 nErrRet = CoreLoad(pGloss);
5960 oTempMain.reset();
5961 oTempTable.reset();
5962 oTempData.reset();
5964 m_xWwFib.reset();
5965 return nErrRet;
5968 void SwWW8ImplReader::SetOutlineStyles()
5970 // If we are inserted into a document then don't clobber existing outline
5971 // levels.
5972 sal_uInt16 nOutlineStyleListLevelWithAssignment = 0;
5973 if (!m_bNewDoc)
5975 ww8::ParaStyles aOutLined(sw::util::GetParaStyles(m_rDoc));
5976 sw::util::SortByAssignedOutlineStyleListLevel(aOutLined);
5977 ww8::ParaStyles::reverse_iterator aEnd = aOutLined.rend();
5978 for ( ww8::ParaStyles::reverse_iterator aIter = aOutLined.rbegin(); aIter < aEnd; ++aIter)
5980 if ((*aIter)->IsAssignedToListLevelOfOutlineStyle())
5981 nOutlineStyleListLevelWithAssignment |= 1 << (*aIter)->GetAssignedOutlineStyleLevel();
5982 else
5983 break;
5987 // Check applied WW8 list styles at WW8 Built-In Heading Styles
5988 // - Choose the list style which occurs most often as the one which provides
5989 // the list level properties for the Outline Style.
5990 // - Populate temporary list of WW8 Built-In Heading Styles for further
5991 // iteration
5992 std::vector<SwWW8StyInf*> aWW8BuiltInHeadingStyles;
5994 sal_uInt16 nStyle = 0;
5995 std::map<const SwNumRule*, int> aWW8ListStyleCounts;
5996 std::map<const SwNumRule*, bool> aPreventUseAsChapterNumbering;
5997 for (SwWW8StyInf& rSI : m_vColl)
5999 // Copy inherited numbering info since LO drops inheritance after ChapterNumbering
6000 // and only applies listLevel via style with the selected ChapterNumbering LFO.
6001 bool bReRegister = false;
6002 if (rSI.m_nBase && rSI.m_nBase < m_vColl.size()
6003 && m_vColl[rSI.m_nBase].HasWW8OutlineLevel())
6005 if (rSI.m_nLFOIndex == USHRT_MAX)
6007 rSI.m_nLFOIndex = m_vColl[rSI.m_nBase].m_nLFOIndex;
6009 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6010 // A style never inherits numbering from Chapter Numbering.
6011 if (rSI.m_nLFOIndex != USHRT_MAX)
6013 const SwNumRule* pNumRule = m_vColl[rSI.m_nBase].m_pOutlineNumrule;
6014 if (pNumRule)
6015 aPreventUseAsChapterNumbering[pNumRule] = true;
6018 if (rSI.m_nListLevel == MAXLEVEL)
6019 rSI.m_nListLevel = m_vColl[rSI.m_nBase].m_nListLevel;
6020 if (rSI.mnWW8OutlineLevel == MAXLEVEL)
6021 rSI.mnWW8OutlineLevel = m_vColl[rSI.m_nBase].mnWW8OutlineLevel;
6022 bReRegister = true;
6025 // Undefined listLevel is treated as the first level with valid numbering rule.
6026 if (rSI.m_nLFOIndex < USHRT_MAX && rSI.m_nListLevel == MAXLEVEL)
6028 rSI.m_nListLevel = 0;
6029 bReRegister = true;
6032 if (bReRegister)
6033 RegisterNumFormatOnStyle(nStyle);
6035 ++nStyle; // increment before the first "continue";
6037 if (!rSI.m_bColl || !rSI.IsWW8BuiltInHeadingStyle() || !rSI.HasWW8OutlineLevel())
6039 continue;
6042 // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
6043 if (rSI.IsOutlineNumbered() && rSI.m_nListLevel != rSI.mnWW8OutlineLevel)
6045 aPreventUseAsChapterNumbering[rSI.m_pOutlineNumrule] = true;
6048 aWW8BuiltInHeadingStyles.push_back(&rSI);
6050 const SwNumRule* pWW8ListStyle = rSI.GetOutlineNumrule();
6051 if (pWW8ListStyle != nullptr)
6053 std::map<const SwNumRule*, int>::iterator aCountIter
6054 = aWW8ListStyleCounts.find(pWW8ListStyle);
6055 if (aCountIter == aWW8ListStyleCounts.end())
6057 aWW8ListStyleCounts[pWW8ListStyle] = 1;
6059 else
6061 ++(aCountIter->second);
6066 int nCurrentMaxCount = 0;
6067 for (const auto& rEntry : aWW8ListStyleCounts)
6069 if (aPreventUseAsChapterNumbering[rEntry.first])
6070 continue;
6072 if (rEntry.second > nCurrentMaxCount)
6074 nCurrentMaxCount = rEntry.second;
6075 m_pChosenWW8OutlineStyle = rEntry.first;
6080 // - set list level properties of Outline Style - ODF's list style applied
6081 // by default to headings
6082 // - assign corresponding Heading Paragraph Styles to the Outline Style
6083 // - If a heading Paragraph Styles is not applying the WW8 list style which
6084 // had been chosen as
6085 // the one which provides the list level properties for the Outline Style,
6086 // its assignment to
6087 // the Outline Style is removed. A potential applied WW8 list style is
6088 // assigned directly and
6089 // its default outline level is applied.
6090 SwNumRule aOutlineRule(*m_rDoc.GetOutlineNumRule());
6091 if (m_pChosenWW8OutlineStyle)
6093 for (int i = 0; i < WW8ListManager::nMaxLevel; ++i)
6095 // Don't clobber existing outline levels.
6096 const sal_uInt16 nLevel = 1 << i;
6097 if (!(nOutlineStyleListLevelWithAssignment & nLevel))
6098 aOutlineRule.Set(i, m_pChosenWW8OutlineStyle->Get(i));
6102 for (const SwWW8StyInf* pStyleInf : aWW8BuiltInHeadingStyles)
6104 const sal_uInt16 nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6105 = 1 << pStyleInf->mnWW8OutlineLevel;
6106 if (nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
6107 & nOutlineStyleListLevelWithAssignment)
6109 continue;
6112 // in case that there are more styles on this level ignore them
6113 nOutlineStyleListLevelWithAssignment
6114 |= nOutlineStyleListLevelOfWW8BuiltInHeadingStyle;
6116 SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pStyleInf->m_pFormat);
6117 if (pStyleInf->GetOutlineNumrule() != m_pChosenWW8OutlineStyle
6118 || (pStyleInf->m_nListLevel < WW8ListManager::nMaxLevel
6119 && pStyleInf->mnWW8OutlineLevel != pStyleInf->m_nListLevel))
6121 // WW8 Built-In Heading Style does not apply the chosen one.
6122 // --> delete assignment to OutlineStyle, but keep its current
6123 // outline level
6124 pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
6125 // Apply existing WW8 list style a normal list style at the
6126 // Paragraph Style
6127 if (pStyleInf->GetOutlineNumrule() != nullptr)
6129 pTextFormatColl->SetFormatAttr(
6130 SwNumRuleItem(pStyleInf->GetOutlineNumrule()->GetName()));
6132 // apply default outline level of WW8 Built-in Heading Style
6133 const sal_uInt8 nOutlineLevel
6134 = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(
6135 pStyleInf->mnWW8OutlineLevel);
6136 pTextFormatColl->SetFormatAttr(
6137 SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel));
6139 else
6141 pTextFormatColl->AssignToListLevelOfOutlineStyle(
6142 pStyleInf->mnWW8OutlineLevel);
6146 if (m_pChosenWW8OutlineStyle)
6148 m_rDoc.SetOutlineNumRule(aOutlineRule);
6152 const OUString* SwWW8ImplReader::GetAnnotationAuthor(sal_uInt16 nIdx)
6154 if (!m_xAtnNames && m_xWwFib->m_lcbGrpStAtnOwners)
6156 // Determine authors: can be found in the TableStream
6157 m_xAtnNames.emplace();
6158 SvStream& rStrm = *m_pTableStream;
6160 auto nOldPos = rStrm.Tell();
6161 bool bValidPos = checkSeek(rStrm, m_xWwFib->m_fcGrpStAtnOwners);
6162 if (bValidPos)
6164 tools::Long nRead = 0, nCount = m_xWwFib->m_lcbGrpStAtnOwners;
6165 while (nRead < nCount && rStrm.good())
6167 if( m_bVer67 )
6169 m_xAtnNames->push_back(read_uInt8_PascalString(rStrm,
6170 RTL_TEXTENCODING_MS_1252));
6171 nRead += m_xAtnNames->rbegin()->getLength() + 1; // Length + sal_uInt8 count
6173 else
6175 m_xAtnNames->push_back(read_uInt16_PascalString(rStrm));
6176 // Unicode: double the length + sal_uInt16 count
6177 nRead += (m_xAtnNames->rbegin()->getLength() + 1)*2;
6181 rStrm.Seek( nOldPos );
6184 const OUString *pRet = nullptr;
6185 if (m_xAtnNames && nIdx < m_xAtnNames->size())
6186 pRet = &((*m_xAtnNames)[nIdx]);
6187 return pRet;
6190 void SwWW8ImplReader::GetSmartTagInfo(SwFltRDFMark& rMark)
6192 if (!m_pSmartTagData && m_xWwFib->m_lcbFactoidData)
6194 m_pSmartTagData.reset(new WW8SmartTagData);
6195 m_pSmartTagData->Read(*m_pTableStream, m_xWwFib->m_fcFactoidData, m_xWwFib->m_lcbFactoidData);
6198 if (!m_pSmartTagData)
6199 return;
6201 // Check if the handle is a valid smart tag bookmark index.
6202 size_t nIndex = rMark.GetHandle();
6203 if (nIndex >= m_pSmartTagData->m_aPropBags.size())
6204 return;
6206 // Check if the smart tag bookmark refers to a valid factoid type.
6207 const MSOPropertyBag& rPropertyBag = m_pSmartTagData->m_aPropBags[rMark.GetHandle()];
6208 auto& rFactoidTypes = m_pSmartTagData->m_aPropBagStore.m_aFactoidTypes;
6209 auto itPropertyBag = std::find_if(rFactoidTypes.begin(), rFactoidTypes.end(),
6210 [&rPropertyBag](const MSOFactoidType& rType) { return rType.m_nId == rPropertyBag.m_nId; });
6211 if (itPropertyBag == rFactoidTypes.end())
6212 return;
6214 // Check if the factoid is an RDF one.
6215 const MSOFactoidType& rFactoidType = *itPropertyBag;
6216 if (rFactoidType.m_aUri != "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
6217 return;
6219 // Finally put the relevant attributes to the mark.
6220 std::vector< std::pair<OUString, OUString> > aAttributes;
6221 for (const MSOProperty& rProperty : rPropertyBag.m_aProperties)
6223 OUString aKey;
6224 OUString aValue;
6225 if (rProperty.m_nKey < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6226 aKey = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nKey];
6227 if (rProperty.m_nValue < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
6228 aValue = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nValue];
6229 if (!aKey.isEmpty() && !aValue.isEmpty())
6230 aAttributes.emplace_back(aKey, aValue);
6232 rMark.SetAttributes(std::move(aAttributes));
6235 ErrCode SwWW8ImplReader::LoadDoc(WW8Glossary *pGloss)
6237 ErrCode nErrRet = ERRCODE_NONE;
6240 static const char* aNames[ 13 ] = {
6241 "WinWord/WW", "WinWord/WW8", "WinWord/WWFT",
6242 "WinWord/WWFLX", "WinWord/WWFLY",
6243 "WinWord/WWF",
6244 "WinWord/WWFA0", "WinWord/WWFA1", "WinWord/WWFA2",
6245 "WinWord/WWFB0", "WinWord/WWFB1", "WinWord/WWFB2",
6246 "WinWord/RegardHindiDigits"
6248 sal_uInt64 aVal[ 13 ];
6250 SwFilterOptions aOpt( 13, aNames, aVal );
6252 m_nIniFlags = aVal[ 0 ];
6253 m_nIniFlags1= aVal[ 1 ];
6254 // Moves Flys by x twips to the right or left
6255 m_nIniFlyDx = aVal[ 3 ];
6256 m_nIniFlyDy = aVal[ 4 ];
6258 m_nFieldFlags = aVal[ 5 ];
6259 m_nFieldTagAlways[0] = aVal[ 6 ];
6260 m_nFieldTagAlways[1] = aVal[ 7 ];
6261 m_nFieldTagAlways[2] = aVal[ 8 ];
6262 m_nFieldTagBad[0] = aVal[ 9 ];
6263 m_nFieldTagBad[1] = aVal[ 10 ];
6264 m_nFieldTagBad[2] = aVal[ 11 ];
6265 m_bRegardHindiDigits = aVal[ 12 ] > 0;
6268 sal_uInt16 nMagic(0);
6269 m_pStrm->ReadUInt16( nMagic );
6271 // Remember: 6 means "6 OR 7", 7 means "JUST 7"
6272 switch (m_nWantedVersion)
6274 case 6:
6275 case 7:
6276 if (
6277 0xa59b != nMagic && 0xa59c != nMagic &&
6278 0xa5dc != nMagic && 0xa5db != nMagic &&
6279 (nMagic < 0xa697 || nMagic > 0xa699)
6282 // Test for own 97 fake!
6283 if (m_pStg && 0xa5ec == nMagic)
6285 sal_uInt64 nCurPos = m_pStrm->Tell();
6286 if (checkSeek(*m_pStrm, nCurPos + 2))
6288 sal_uInt32 nfcMin(0);
6289 m_pStrm->ReadUInt32( nfcMin );
6290 if (0x300 != nfcMin)
6291 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6293 m_pStrm->Seek( nCurPos );
6295 else
6296 nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
6298 break;
6299 case 8:
6300 if (0xa5ec != nMagic)
6301 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6302 break;
6303 default:
6304 nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
6305 OSL_ENSURE( false, "We forgot to encode nVersion!" );
6306 break;
6309 if (!nErrRet)
6310 nErrRet = LoadThroughDecryption(pGloss);
6312 m_rDoc.PropagateOutlineRule();
6314 return nErrRet;
6317 extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOC()
6319 return new WW8Reader;
6322 namespace
6324 class FontCacheGuard
6326 public:
6327 ~FontCacheGuard()
6329 FlushFontCache();
6334 bool TestImportDOC(SvStream &rStream, const OUString &rFltName)
6336 FontCacheGuard aFontCacheGuard;
6337 std::unique_ptr<Reader> xReader(ImportDOC());
6339 tools::SvRef<SotStorage> xStorage;
6340 xReader->m_pStream = &rStream;
6341 if (rFltName != "WW6")
6345 xStorage = tools::SvRef<SotStorage>(new SotStorage(rStream));
6346 if (xStorage->GetError())
6347 return false;
6349 catch (...)
6351 return false;
6353 xReader->m_pStorage = xStorage.get();
6355 xReader->SetFltName(rFltName);
6357 SwGlobals::ensure();
6359 SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
6360 xDocSh->DoInitNew();
6361 SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
6363 SwPaM aPaM(pD->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
6364 pD->SetInReading(true);
6365 bool bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE;
6366 pD->SetInReading(false);
6368 return bRet;
6371 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW8(SvStream &rStream)
6373 return TestImportDOC(rStream, "CWW8");
6376 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW6(SvStream &rStream)
6378 return TestImportDOC(rStream, "CWW6");
6381 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW2(SvStream &rStream)
6383 return TestImportDOC(rStream, "WW6");
6386 ErrCode WW8Reader::OpenMainStream( tools::SvRef<SotStorageStream>& rRef, sal_uInt16& rBuffSize )
6388 ErrCode nRet = ERR_SWG_READ_ERROR;
6389 OSL_ENSURE(m_pStorage, "Where is my Storage?");
6390 rRef = m_pStorage->OpenSotStream( "WordDocument", StreamMode::READ | StreamMode::SHARE_DENYALL);
6392 if( rRef.is() )
6394 if( ERRCODE_NONE == rRef->GetError() )
6396 sal_uInt16 nOld = rRef->GetBufferSize();
6397 rRef->SetBufferSize( rBuffSize );
6398 rBuffSize = nOld;
6399 nRet = ERRCODE_NONE;
6401 else
6402 nRet = rRef->GetError();
6404 return nRet;
6407 static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, std::u16string_view sPrefix)
6409 SvStorageInfoList aElements;
6410 pStorage->FillInfoList(&aElements);
6411 for (const auto & aElement : aElements)
6413 OUString sStreamFullName = sPrefix.size() ? OUString::Concat(sPrefix) + "/" + aElement.GetName() : aElement.GetName();
6414 if (aElement.IsStorage())
6416 tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
6417 lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
6419 else
6421 // Read stream
6422 tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
6423 if (rStream.is())
6425 sal_Int32 nStreamSize = rStream->GetSize();
6426 css::uno::Sequence< sal_Int8 > oData;
6427 oData.realloc(nStreamSize);
6428 sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
6429 if (nStreamSize == nReadBytes)
6430 aStreamsData[sStreamFullName] <<= oData;
6436 ErrCode WW8Reader::DecryptDRMPackage()
6438 // We have DRM encrypted storage. We should try to decrypt it first, if we can
6439 uno::Sequence< uno::Any > aArguments;
6440 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
6441 uno::Reference< packages::XPackageEncryption > xPackageEncryption(
6442 xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
6443 "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY);
6445 if (!xPackageEncryption.is())
6447 // We do not know how to decrypt this
6448 return ERRCODE_IO_ACCESSDENIED;
6451 comphelper::SequenceAsHashMap aStreamsData;
6452 lcl_getListOfStreams(m_pStorage.get(), aStreamsData, u"");
6454 try {
6455 uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
6456 if (!xPackageEncryption->readEncryptionInfo(aStreams))
6458 // We failed with decryption
6459 return ERRCODE_IO_ACCESSDENIED;
6462 tools::SvRef<SotStorageStream> rContentStream = m_pStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL);
6463 if (!rContentStream.is())
6465 return ERRCODE_IO_NOTEXISTS;
6468 mDecodedStream = std::make_shared<SvMemoryStream>();
6470 uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
6471 uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*mDecodedStream));
6473 if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
6475 // We failed with decryption
6476 return ERRCODE_IO_ACCESSDENIED;
6479 mDecodedStream->Seek(0);
6481 // Further reading is done from new document
6482 m_pStorage = new SotStorage(*mDecodedStream);
6484 // Set the media descriptor data
6485 uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData("");
6486 m_pMedium->GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
6488 catch (const std::exception&)
6490 return ERRCODE_IO_ACCESSDENIED;
6493 return ERRCODE_NONE;
6496 ErrCode WW8Reader::Read(SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & /* FileName */)
6498 sal_uInt16 nOldBuffSize = 32768;
6499 bool bNew = !m_bInsertMode; // New Doc (no inserting)
6501 tools::SvRef<SotStorageStream> refStrm; // So that no one else can steal the Stream
6502 SvStream* pIn = m_pStream;
6504 ErrCode nRet = ERRCODE_NONE;
6505 sal_uInt8 nVersion = 8;
6507 const OUString sFltName = GetFltName();
6508 if ( sFltName=="WW6" )
6510 if (m_pStream)
6511 nVersion = 6;
6512 else
6514 OSL_ENSURE(false, "WinWord 95 Reader-Read without Stream");
6515 nRet = ERR_SWG_READ_ERROR;
6518 else
6520 if ( sFltName=="CWW6" )
6521 nVersion = 6;
6522 else if ( sFltName=="CWW7" )
6523 nVersion = 7;
6525 if( m_pStorage.is() )
6527 // Check if we have special encrypted content
6528 tools::SvRef<SotStorageStream> rRef = m_pStorage->OpenSotStream("\006DataSpaces/DataSpaceInfo/\011DRMDataSpace", StreamMode::READ | StreamMode::SHARE_DENYALL);
6529 if (rRef.is())
6531 nRet = DecryptDRMPackage();
6533 nRet = OpenMainStream(refStrm, nOldBuffSize);
6534 pIn = refStrm.get();
6536 else
6538 OSL_ENSURE(false, "WinWord 95/97 Reader-Read without Storage");
6539 nRet = ERR_SWG_READ_ERROR;
6543 if( !nRet )
6545 std::unique_ptr<SwWW8ImplReader> pRdr(new SwWW8ImplReader(nVersion, m_pStorage.get(), pIn, rDoc,
6546 rBaseURL, bNew, m_bSkipImages, *rPaM.GetPoint()));
6547 if (bNew)
6549 rPaM.GetBound().nContent.Assign(nullptr, 0);
6550 rPaM.GetBound(false).nContent.Assign(nullptr, 0);
6554 nRet = pRdr->LoadDoc();
6556 catch( const std::exception& )
6558 nRet = ERR_WW8_NO_WW8_FILE_ERR;
6561 if( refStrm.is() )
6563 refStrm->SetBufferSize( nOldBuffSize );
6564 refStrm.clear();
6566 else
6568 pIn->ResetError();
6572 return nRet;
6575 SwReaderType WW8Reader::GetReaderType()
6577 return SwReaderType::Storage | SwReaderType::Stream;
6580 bool WW8Reader::HasGlossaries() const
6582 return true;
6585 bool WW8Reader::ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const
6587 bool bRet=false;
6589 WW8Reader *pThis = const_cast<WW8Reader *>(this);
6591 sal_uInt16 nOldBuffSize = 32768;
6592 tools::SvRef<SotStorageStream> refStrm;
6593 if (!pThis->OpenMainStream(refStrm, nOldBuffSize))
6595 WW8Glossary aGloss( refStrm, 8, m_pStorage.get() );
6596 bRet = aGloss.Load( rBlocks, bSaveRelFiles );
6598 return bRet;
6601 bool SwMSDffManager::GetOLEStorageName(sal_uInt32 nOLEId, OUString& rStorageName,
6602 tools::SvRef<SotStorage>& rSrcStorage, uno::Reference < embed::XStorage >& rDestStorage) const
6604 bool bRet = false;
6606 sal_Int32 nPictureId = 0;
6607 if (m_rReader.m_pStg)
6609 // Via the TextBox-PLCF we get the right char Start-End positions
6610 // We should then find the EmbeddedField and the corresponding Sprms
6611 // in that Area.
6612 // We only need the Sprm for the Picture Id.
6613 sal_uInt64 nOldPos = m_rReader.m_pStrm->Tell();
6615 // #i32596# - consider return value of method
6616 // <rReader.GetTxbxTextSttEndCp(..)>. If it returns false, method
6617 // wasn't successful. Thus, continue in this case.
6618 // Note: Ask MM for initialization of <nStartCp> and <nEndCp>.
6619 // Note: Ask MM about assertions in method <rReader.GetTxbxTextSttEndCp(..)>.
6620 WW8_CP nStartCp, nEndCp;
6621 if ( m_rReader.m_bDrawCpOValid && m_rReader.GetTxbxTextSttEndCp(nStartCp, nEndCp,
6622 o3tl::narrowing<sal_uInt16>((nOLEId >> 16) & 0xFFFF),
6623 o3tl::narrowing<sal_uInt16>(nOLEId & 0xFFFF)) )
6625 WW8PLCFxSaveAll aSave;
6626 m_rReader.m_xPlcxMan->SaveAllPLCFx( aSave );
6628 nStartCp += m_rReader.m_nDrawCpO;
6629 nEndCp += m_rReader.m_nDrawCpO;
6630 WW8PLCFx_Cp_FKP* pChp = m_rReader.m_xPlcxMan->GetChpPLCF();
6631 wwSprmParser aSprmParser(*m_rReader.m_xWwFib);
6632 while (nStartCp <= nEndCp && !nPictureId)
6634 if (!pChp->SeekPos( nStartCp))
6635 break;
6636 WW8PLCFxDesc aDesc;
6637 pChp->GetSprms( &aDesc );
6639 if (aDesc.nSprmsLen && aDesc.pMemPos) // Attributes present
6641 auto nLen = aDesc.nSprmsLen;
6642 const sal_uInt8* pSprm = aDesc.pMemPos;
6644 while (nLen >= 2 && !nPictureId)
6646 sal_uInt16 nId = aSprmParser.GetSprmId(pSprm);
6647 sal_Int32 nSL = aSprmParser.GetSprmSize(nId, pSprm, nLen);
6649 if( nLen < nSL )
6650 break; // Not enough Bytes left
6652 if (0x6A03 == nId)
6654 nPictureId = SVBT32ToUInt32(pSprm +
6655 aSprmParser.DistanceToData(nId));
6656 bRet = true;
6658 pSprm += nSL;
6659 nLen -= nSL;
6662 nStartCp = aDesc.nEndPos;
6665 m_rReader.m_xPlcxMan->RestoreAllPLCFx( aSave );
6668 m_rReader.m_pStrm->Seek( nOldPos );
6671 if( bRet )
6673 rStorageName = "_";
6674 rStorageName += OUString::number(nPictureId);
6675 rSrcStorage = m_rReader.m_pStg->OpenSotStorage(SL::aObjectPool);
6676 if (!m_rReader.m_pDocShell)
6677 bRet=false;
6678 else
6679 rDestStorage = m_rReader.m_pDocShell->GetStorage();
6681 return bRet;
6685 * When reading a single Box (which possibly is part of a group), we do
6686 * not yet have enough information to decide whether we need it as a TextField
6687 * or not.
6688 * So convert all of them as a precaution.
6689 * FIXME: Actually implement this!
6691 bool SwMSDffManager::ShapeHasText(sal_uLong, sal_uLong) const
6693 return true;
6696 bool SwWW8ImplReader::InEqualOrHigherApo(int nLvl) const
6698 if (nLvl)
6699 --nLvl;
6700 // #i60827# - check size of <maApos> to assure that <maApos.begin() + nLvl> can be performed.
6701 if ( sal::static_int_cast< sal_Int32>(nLvl) >= sal::static_int_cast< sal_Int32>(m_aApos.size()) )
6703 return false;
6705 auto aIter = std::find(m_aApos.begin() + nLvl, m_aApos.end(), true);
6706 return aIter != m_aApos.end();
6709 bool SwWW8ImplReader::InEqualApo(int nLvl) const
6711 // If we are in a table, see if an apo was inserted at the level below the table.
6712 if (nLvl)
6713 --nLvl;
6714 if (nLvl < 0 || o3tl::make_unsigned(nLvl) >= m_aApos.size())
6715 return false;
6716 return m_aApos[nLvl];
6719 namespace sw::hack
6721 Position::Position(const SwPosition &rPos)
6722 : maPtNode(rPos.GetNode()), mnPtContent(rPos.GetContentIndex())
6726 Position::operator SwPosition() const
6728 return SwPosition(maPtNode, maPtNode.GetNode().GetContentNode(), mnPtContent);
6732 SwMacroInfo::SwMacroInfo()
6733 : SdrObjUserData( SdrInventor::ScOrSwDraw, SW_UD_IMAPDATA )
6734 , mnShapeId(-1)
6738 SwMacroInfo::~SwMacroInfo()
6742 std::unique_ptr<SdrObjUserData> SwMacroInfo::Clone( SdrObject* /*pObj*/ ) const
6744 return std::unique_ptr<SdrObjUserData>(new SwMacroInfo( *this ));
6747 std::unique_ptr<SfxItemSet> SwWW8ImplReader::SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet)
6749 std::unique_ptr<SfxItemSet> xRet(std::move(m_xCurrentItemSet));
6750 m_xCurrentItemSet = std::move(pItemSet);
6751 return xRet;
6754 void SwWW8ImplReader::NotifyMacroEventRead()
6756 if (m_bNotifyMacroEventRead)
6757 return;
6758 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
6759 comphelper::DocumentInfo::notifyMacroEventRead(xModel);
6760 m_bNotifyMacroEventRead = true;
6763 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */