LibreOffice Module sw (master) 1
ww8par.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <config_features.h>
21
22#include <sal/config.h>
23#include <sal/log.hxx>
24
25#include <com/sun/star/embed/Aspects.hpp>
26#include <com/sun/star/embed/ElementModes.hpp>
27#include <com/sun/star/frame/XModel.hpp>
28#include <com/sun/star/packages/XPackageEncryption.hpp>
29#include <com/sun/star/lang/XMultiServiceFactory.hpp>
30
32
36#include <rtl/random.h>
37#include <rtl/ustring.hxx>
38#include <rtl/ustrbuf.hxx>
39
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>
45
49
50#include <editeng/outlobj.hxx>
51#include <editeng/brushitem.hxx>
53#include <editeng/tstpitem.hxx>
54#include <editeng/ulspitem.hxx>
55#include <editeng/langitem.hxx>
56#include <editeng/opaqitem.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>
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>
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>
108#include "sprmids.hxx"
109
110#include "writerwordglue.hxx"
111
112#include <ndgrf.hxx>
113#include <editeng/editids.hrc>
114#include <fmtflcnt.hxx>
115#include <txatbase.hxx>
116
117#include "ww8par2.hxx"
118
119#include <com/sun/star/beans/PropertyAttribute.hpp>
120#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
121#include <com/sun/star/document/XViewDataSupplier.hpp>
122
123#include <svl/lngmisc.hxx>
124#include <svl/itemiter.hxx>
125#include <svl/whiter.hxx>
126
129#include <basic/basmgr.hxx>
130
131#include "ww8toolbar.hxx"
133#include <o3tl/safeint.hxx>
134#include <osl/file.hxx>
135
136#include <breakit.hxx>
137
138#include <sfx2/docfile.hxx>
139#include <swdll.hxx>
140#include "WW8Sttbf.hxx"
141#include "WW8FibData.hxx"
142#include <unordered_set>
143#include <memory>
144
145using namespace ::com::sun::star;
146using namespace sw::util;
147using namespace sw::types;
148using namespace nsHdFtFlags;
149
150#include <com/sun/star/i18n/XBreakIterator.hpp>
151#include <com/sun/star/i18n/ScriptType.hpp>
153#include <com/sun/star/ucb/SimpleFileAccess.hpp>
154
155#include <com/sun/star/script/vba/XVBACompatibility.hpp>
157#include <oox/ole/vbaproject.hxx>
158#include <oox/ole/olestorage.hxx>
162
164{
165 if ( pObj )
166 {
167 sal_uInt16 nCount = pObj->GetUserDataCount();
168 for( sal_uInt16 i = 0; i < nCount; i++ )
169 {
170 SdrObjUserData* pData = pObj->GetUserData( i );
171 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
172 && pData->GetId() == SW_UD_IMAPDATA)
173 {
174 return dynamic_cast<SwMacroInfo*>(pData);
175 }
176 }
178 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
179 return pData;
180 }
181
182 return nullptr;
183};
184
185static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell)
186{
187 OUStringBuffer aTmpStr;
188 while( nLevel )
189 {
190 aTmpStr.append("../");
191 --nLevel;
192 }
193 if (!aTmpStr.isEmpty())
194 aTmpStr.append(rPath);
195 else
196 aTmpStr = rPath;
197
198 if (!aTmpStr.isEmpty())
199 {
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
203 }
205
206namespace
207{
208 void lclIgnoreUString32(SvStream& rStrm)
209 {
210 sal_uInt32 nChars(0);
211 rStrm.ReadUInt32(nChars);
212 nChars *= 2;
213 rStrm.SeekRel(nChars);
214 }
215}
216
217void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
218{
219 // (0x01B8) HLINK
220 // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
221 constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001;
222 constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002;
223 constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014;
224 constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008;
225 constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080;
226 constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100;
227
228 //sal_uInt8 maGuidStdLink[ 16 ] ={
229 // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
230
231 sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
232 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
233
234 sal_uInt8 const aGuidFileMoniker[ 16 ] = {
235 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
236
237 sal_uInt8 aGuid[16];
238 sal_uInt32 nFlags(0);
239
240 rStrm.ReadBytes(aGuid, 16);
241 rStrm.SeekRel( 4 );
242 rStrm.ReadUInt32( nFlags );
243
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
247
248 // description (ignore)
249 if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
250 lclIgnoreUString32( rStrm );
251
252 // target frame
253 if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
254 {
256 }
257
258 // UNC path
259 if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
260 {
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)
266 {
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);
270 }
271 }
272 // file link or URL
273 else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
274 {
275 rStrm.ReadBytes(aGuid, 16);
276
277 if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
278 {
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
288 {
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()));
292 }
293 rStrm.SeekRel( 24 );
294
295 sal_uInt32 nStrLen(0);
296 rStrm.ReadUInt32( nStrLen );
297 if( nStrLen )
298 {
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);
306 }
307 else
308 lclGetAbsPath( *xShortName, nLevel, pDocShell);
309 }
310 else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
311 {
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
322 {
323 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
324 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
325 }
326 if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
327 lclGetAbsPath( *xLongName, 0 ,pDocShell);
328 }
329 else
330 {
331 SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
332 }
333 }
334
335 // text mark
336 if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
337 {
338 xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
339 }
340
341 if (!xLongName && xShortName)
342 xLongName.reset(new OUString(*xShortName));
343 else if (!xLongName && xTextMark)
344 xLongName.reset( new OUString );
345
346 if (xLongName)
347 {
348 if (xTextMark)
349 {
350 if (xLongName->isEmpty())
351 *xTextMark = xTextMark->replace('!', '.');
352 *xLongName += "#" + *xTextMark;
353 }
354 hlStr.hLinkAddr = *xLongName;
355 }
356}
357
358namespace {
359
360class BasicProjImportHelper
361{
362 SwDocShell& mrDocShell;
363 uno::Reference< uno::XComponentContext > mxCtx;
364public:
365 explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
367 {
368 }
369 bool import( const uno::Reference< io::XInputStream >& rxIn );
370 OUString getProjectName() const;
371};
372
373}
374
375bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
376{
377 bool bRet = false;
378 try
379 {
380 oox::ole::OleStorage root( mxCtx, rxIn, false );
381 oox::StorageRef vbaStg = root.openSubStorage( "Macros" , false );
382 if ( vbaStg )
383 {
384 oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
385 bRet = aVbaPrj.importVbaProject( *vbaStg );
386 }
387 }
388 catch( const uno::Exception& )
389 {
390 bRet = false;
391 }
392 return bRet;
393}
394
395OUString BasicProjImportHelper::getProjectName() const
396{
397 OUString sProjName( "Standard" );
398 uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
399 if ( xProps.is() )
400 {
401 try
402 {
403 uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW );
404 sProjName = xVBA->getProjectName();
405
406 }
407 catch( const uno::Exception& )
408 {
409 }
410 }
411 return sProjName;
412}
413
414namespace {
415
416class Sttb : public TBBase
417{
418struct SBBItem
419{
420 sal_uInt16 cchData;
421 OUString data;
422 SBBItem() : cchData(0){}
423};
424 sal_uInt16 m_fExtend;
425 sal_uInt16 m_cData;
426 sal_uInt16 m_cbExtra;
427
428 std::vector< SBBItem > m_dataItems;
429
430 Sttb(Sttb const&) = delete;
431 Sttb& operator=(Sttb const&) = delete;
432
433public:
434 Sttb();
435
436 bool Read(SvStream &rS) override;
437 OUString getStringAtIndex( sal_uInt32 );
438};
439
440}
441
442Sttb::Sttb()
443 : m_fExtend(0)
444 , m_cData(0)
445 , m_cbExtra(0)
446{
447}
448
449bool Sttb::Read( SvStream& rS )
450{
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 )
455 {
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 )
461 {
462 SBBItem aItem;
463 rS.ReadUInt16( aItem.cchData );
464 aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
465 m_dataItems.push_back( aItem );
466 }
467 }
468 return true;
469}
470
471OUString
472Sttb::getStringAtIndex( sal_uInt32 index )
473{
474 OUString aRet;
475 if ( index < m_dataItems.size() )
476 aRet = m_dataItems[ index ].data;
477 return aRet;
478
479}
480
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)
485{
488}
489
491{
492 sal_uInt32 nFlags(0);
494 if (rOpt.IsMathType2Math())
495 nFlags |= OLE_MATHTYPE_2_STARMATH;
496 if (rOpt.IsExcel2Calc())
497 nFlags |= OLE_EXCEL_2_STARCALC;
498 if (rOpt.IsPowerPoint2Impress())
500 if (rOpt.IsWinWord2Writer())
501 nFlags |= OLE_WINWORD_2_STARWRITER;
502 return nFlags;
503}
504
505/*
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.
509 */
510// #i32596# - consider new parameter <_nCalledByGroup>
512 const Graphic& rGrf,
513 const tools::Rectangle& rBoundRect,
514 const tools::Rectangle& rVisArea,
515 const int _nCalledByGroup ) const
516{
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 )
521 {
522 return nullptr;
523 }
524
526 OUString sStorageName;
528 uno::Reference < embed::XStorage > xDstStg;
529 if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
530 {
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;
535 m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
536 {
538 }
539 else
540 {
541 ErrCode nError = ERRCODE_NONE;
543 *pSdrModel,
544 sStorageName,
545 xSrcStg,
546 xDstStg,
547 rGrf,
548 rBoundRect,
549 rVisArea,
550 pStData,
551 nError,
553 css::embed::Aspects::MSOLE_CONTENT,
555 }
556 }
557 return pRet;
558}
559
561{
562 OSL_ENSURE(!m_pFallbackStream,
563 "if you're recursive, you're broken");
566 aEscherBlipCache.clear();
567 pStData2 = nullptr;
568}
569
571{
574 m_aOldEscherBlipCache.clear();
575 m_pFallbackStream = nullptr;
576}
577
579{
580 return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
581}
582
584{
585 return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
586}
587
589{
590 if (m_xCtrlStck)
591 m_xCtrlStck->SetToggleAttrFlags(nFlags);
592}
593
595{
596 if (m_xCtrlStck)
597 m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
598}
599
601 DffObjData& rObjData,
602 SvxMSDffClientData& rData,
603 tools::Rectangle& rTextRect,
604 SdrObject* pObj1
605 )
606{
607 rtl::Reference<SdrObject> pObj = pObj1;
608 if( !rTextRect.IsEmpty() )
609 {
610 SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
611 std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
612
613 // fill Import Record with data
614 pImpRec->nShapeId = rObjData.nShapeId;
615 pImpRec->eShapeType = rObjData.eShapeType;
616
620 if( rObjData.bClientAnchor )
623 pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
624
628 if( rObjData.bClientData )
631 pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
632
633 pImpRec->nGroupShapeBooleanProperties = 0;
634
639 {
640 sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
641 auto nAvailableBytes = rSt.remainingSize();
642 if (nBytesLeft > nAvailableBytes)
643 {
644 SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
645 nBytesLeft = nAvailableBytes;
646 }
647 while( 5 < nBytesLeft )
648 {
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)
656 {
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;
678 }
679 nBytesLeft -= 6;
680 }
681 }
682
683 // Text Frame also Title or Outline
684 sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
685 if( nTextId )
686 {
688
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)
695 {
696 // Either
697 // a) it's a simple text object or
698 // b) it's a rectangle with text and square wrapping.
699 bIsSimpleDrawingTextBox =
700 (
701 (pImpRec->eShapeType == mso_sptTextSimple) ||
702 (
703 (pImpRec->eShapeType == mso_sptRectangle)
704 && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
705 )
706 );
707 }
708
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 );
714
715 ScaleEmu( nTextLeft );
716 ScaleEmu( nTextRight );
717 ScaleEmu( nTextTop );
718 ScaleEmu( nTextBottom );
719
720 Degree100 nTextRotationAngle;
721 bool bVerticalText = false;
723 {
724 MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
725 DFF_Prop_txflTextFlow, 0) & 0xFFFF);
726 switch( eTextFlow )
727 {
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;
745 }
746 }
747
748 if (nTextRotationAngle)
749 {
750 if (nTextRotationAngle == 9000_deg100)
751 {
752 tools::Long nWidth = rTextRect.GetWidth();
753 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
754 rTextRect.SetBottom( rTextRect.Top() + nWidth );
755
756 sal_Int32 nOldTextLeft = nTextLeft;
757 sal_Int32 nOldTextRight = nTextRight;
758 sal_Int32 nOldTextTop = nTextTop;
759 sal_Int32 nOldTextBottom = nTextBottom;
760
761 nTextLeft = nOldTextBottom;
762 nTextRight = nOldTextTop;
763 nTextTop = nOldTextLeft;
764 nTextBottom = nOldTextRight;
765 }
766 else if (nTextRotationAngle == 27000_deg100)
767 {
768 tools::Long nWidth = rTextRect.GetWidth();
769 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
770 rTextRect.SetBottom( rTextRect.Top() + nWidth );
771
772 sal_Int32 nOldTextLeft = nTextLeft;
773 sal_Int32 nOldTextRight = nTextRight;
774 sal_Int32 nOldTextTop = nTextTop;
775 sal_Int32 nOldTextBottom = nTextBottom;
776
777 nTextLeft = nOldTextTop;
778 nTextRight = nOldTextBottom;
779 nTextTop = nOldTextRight;
780 nTextBottom = nOldTextLeft;
781 }
782 }
783
784 if (bIsSimpleDrawingTextBox)
785 {
786 pObj = new SdrRectObj(
787 *pSdrModel,
788 SdrObjKind::Text,
789 rTextRect);
790 }
791
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) );
797
798 // Only if it's a simple Textbox, Writer can replace the Object
799 // with a Frame, else
800 if( bIsSimpleDrawingTextBox )
801 {
802 std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
803 std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
804
805 SvxMSDffShapeInfos_ById::const_iterator const it =
806 GetShapeInfos()->find(xTmpRec);
807 if (it != GetShapeInfos()->end())
808 {
809 SvxMSDffShapeInfo& rInfo = **it;
810 pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
811 }
812
813 ApplyAttributes(rSt, aSet, rObjData);
814 }
815
817 {
818 aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
820 aNewRect.Bottom() - aNewRect.Top() ) );
822 aNewRect.Right() - aNewRect.Left() ) );
823 }
824 else
825 {
826 aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
827 aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
828 }
829
831 {
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:
840 ;
841 }
842
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;
852
853 // Taking the correct default (which is mso_anchorTop)
854 sal_uInt32 eTextAnchor =
856
857 SdrTextVertAdjust eTVA = bVerticalText
860 SdrTextHorzAdjust eTHA = bVerticalText
863
864 switch( eTextAnchor )
865 {
866 case mso_anchorTop:
867 {
868 if ( bVerticalText )
870 else
872 }
873 break;
875 {
876 if ( bVerticalText )
878 else
880 }
881 break;
882 case mso_anchorMiddle:
883 break;
885 break;
886 case mso_anchorBottom:
887 {
888 if ( bVerticalText )
890 else
892 }
893 break;
895 {
896 if ( bVerticalText )
898 else
900 }
901 break;
902 default:
903 ;
904 }
905
906 aSet.Put( SdrTextVertAdjustItem( eTVA ) );
907 aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
908
909 if (pObj != nullptr)
910 {
911 pObj->SetMergedItemSet(aSet);
912
913 if (bVerticalText)
914 {
915 SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
916 if (pTextObj)
917 pTextObj->SetVerticalWriting(true);
918 }
919
920 if ( bIsSimpleDrawingTextBox )
921 {
922 if ( nTextRotationAngle )
923 {
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);
931 }
932 }
933
934 if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
935 {
936 SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
937 if (pCustomShape)
938 {
939 double fExtraTextRotation = 0.0;
941 { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
942 fExtraTextRotation = -mnFix16Angle.get();
943 }
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.
947 }
948 fExtraTextRotation += nTextRotationAngle.get();
949 if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
950 {
951 fExtraTextRotation /= 100.0;
953 css::beans::PropertyValue aPropVal;
954 aPropVal.Name = "TextRotateAngle";
955 aPropVal.Value <<= fExtraTextRotation;
956 aGeometryItem.SetPropertyValue( aPropVal );
957 pCustomShape->SetMergedItem( aGeometryItem );
958 }
959 }
960 }
961 else if ( mnFix16Angle )
962 {
963 // rotate text with shape ?
964 pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
965 }
966 }
967 }
968 else if( !pObj )
969 {
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);
976
978 ApplyAttributes( rSt, aSet, rObjData );
979
980 SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
981 if( SfxItemState::DEFAULT == eState )
982 aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
983 pObj->SetMergedItemSet(aSet);
984 }
985
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 );
994
995 if ( nTextId )
996 {
997 pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
998 pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
999 }
1000
1001 pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
1003 pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
1005 pImpRec->nDxWrapDistRight
1008 pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
1010 // 16.16 fraction times total image width or height, as appropriate.
1011
1013 {
1014 pImpRec->pWrapPolygon.reset();
1015
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))
1020 {
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;
1025 }
1026 if (bOk)
1027 {
1028 pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
1029 for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
1030 {
1031 sal_Int32 nX(0), nY(0);
1032 if (nElemSizeVert == 8)
1033 rSt.ReadInt32( nX ).ReadInt32( nY );
1034 else
1035 {
1036 sal_Int16 nSmallX(0), nSmallY(0);
1037 rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
1038 nX = nSmallX;
1039 nY = nSmallY;
1040 }
1041 (*(pImpRec->pWrapPolygon))[i].setX( nX );
1042 (*(pImpRec->pWrapPolygon))[i].setY( nY );
1043 }
1044 }
1045 }
1046
1047 pImpRec->nCropFromTop = GetPropertyValue(
1049 pImpRec->nCropFromBottom = GetPropertyValue(
1051 pImpRec->nCropFromLeft = GetPropertyValue(
1053 pImpRec->nCropFromRight = GetPropertyValue(
1055
1056 sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
1057
1059 pImpRec->eShapeType == mso_sptPictureFrame )
1060 {
1061 nLineFlags &= ~0x08;
1062 }
1063
1064 pImpRec->eLineStyle = (nLineFlags & 8)
1065 ? static_cast<MSO_LineStyle>(GetPropertyValue(
1068 : MSO_LineStyle(USHRT_MAX);
1069 pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
1071
1072 pImpRec->nFlags = rObjData.nSpFlags;
1073
1074 if( pImpRec->nShapeId )
1075 {
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));
1082
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 )
1086 ||
1087 ( (rObjData.nSpFlags & ShapeFlag::Group)
1088 && (rObjData.nCalledByGroup < 2) )
1089 )
1090 {
1091 StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
1092 }
1093 }
1094 else
1095 pImpRec.reset();
1096 }
1097
1098 sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
1099 if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
1100 {
1101 SvMemoryStream aMemStream;
1102 struct HyperLinksTable hlStr;
1103 aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
1104
1105 // copy from DFF stream to memory stream
1106 std::vector< sal_uInt8 > aBuffer( nBufferSize );
1107 if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
1108 {
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)
1118 {
1119 m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
1120 }
1121 }
1122
1123 if (pObj && !hlStr.hLinkAddr.isEmpty())
1124 {
1125 SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
1126 if( pInfo )
1127 {
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 );
1135 }
1136 }
1137 }
1138
1139 return pObj;
1140}
1141
1145void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
1146{
1147 if (nLen < 0)
1148 {
1149 m_bCpxStyle = false;
1150 return;
1151 }
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())
1158 {
1160 m_bCpxStyle = true;
1161 }
1162}
1163
1167void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
1168{
1169}
1170
1175 const SfxPoolItem& rAttr)
1176{
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);
1186}
1187
1189 bool bTstEnd, tools::Long nHand, bool )
1190{
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())
1196 {
1197 size_t nCnt = size();
1198 for (size_t i=0; i < nCnt; ++i)
1199 {
1200 SwFltStackEntry& rEntry = (*this)[i];
1201 if (nAttrId == rEntry.m_pAttr->Which())
1202 {
1204 --nCnt;
1205 }
1206 }
1207 }
1208 else // Normal case, set the attribute into the document
1209 pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
1210 return pRet;
1211}
1212
1214{
1216 "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1217
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;
1227}
1228
1230 SvxTextLeftMarginItem const& rLeftMargin, const SwNumFormat &rFormat,
1231 tools::Long &rFirstLinePos)
1232{
1234 "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1235
1236 const tools::Long nBodyIndent = rLeftMargin.GetTextLeft();
1237 const tools::Long nFirstLineDiff = rFirstLine.GetTextFirstLineOffset();
1238 rFirstLinePos = nBodyIndent + nFirstLineDiff;
1239
1240 const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
1241 const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
1242 tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
1243
1244 return std::max<tools::Long>(nExtraListIndent, 0);
1245}
1246
1247// #i103711#
1248// #i105414#
1250 SvxTextLeftMarginItem & rLeftMargin,
1251 const SwNumFormat &rFormat,
1252 const bool bFirstLineOfstSet,
1253 const bool bLeftIndentSet )
1254{
1256 {
1257 tools::Long nWantedFirstLinePos;
1258 tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, rLeftMargin, rFormat, nWantedFirstLinePos);
1259 rLeftMargin.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
1260 rFirstLine.SetTextFirstLineOffset(0);
1261 }
1263 {
1264 if ( !bFirstLineOfstSet && bLeftIndentSet &&
1265 rFormat.GetFirstLineIndent() != 0 )
1266 {
1267 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1268 }
1269 else if ( bFirstLineOfstSet && !bLeftIndentSet &&
1270 rFormat.GetIndentAt() != 0 )
1271 {
1272 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1273 }
1274 else if (!bFirstLineOfstSet && !bLeftIndentSet )
1275 {
1276 if ( rFormat.GetFirstLineIndent() != 0 )
1277 {
1278 rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
1279 }
1280 if ( rFormat.GetIndentAt() != 0 )
1281 {
1282 rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
1283 }
1284 }
1285 }
1286}
1287
1289 const SwTextNode &rTextNode)
1290{
1291 const SwNumFormat *pRet = nullptr;
1292 const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
1293 if (pItem && rTextNode.GetNumRule())
1294 {
1295 if (rTextNode.IsCountedInList())
1296 {
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());
1301 }
1302 }
1303 return pRet;
1304}
1305
1307 SwFltStackEntry& rEntry )
1308{
1309 switch( rEntry.m_pAttr->Which() )
1310 {
1311 case RES_FLTR_BOOKMARK:
1312 {
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;
1316
1317 SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
1318 if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
1319 {
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() )
1323 {
1324 bInsertBookmarkIntoDoc = false;
1325 }
1326 }
1327 if ( bInsertBookmarkIntoDoc )
1328 {
1329 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1330 }
1331 break;
1332 }
1333 default:
1334 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1335 break;
1336 }
1337
1338}
1339
1341 SwFltStackEntry& rEntry)
1342{
1343 switch (rEntry.m_pAttr->Which())
1344 {
1345 case RES_LR_SPACE:
1346 assert(false);
1347 break;
1350 {
1351 /*
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
1357 */
1358 SwPaM aRegion(rTmpPos);
1360 {
1363 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1364 {
1365 SvxFirstLineIndentItem const firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
1366 firstLineNew.SetTextFirstLineOffset(firstLineEntry.GetTextFirstLineOffset(), firstLineEntry.GetPropTextFirstLineOffset());
1367 firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
1368 }
1369 else
1370 {
1371 SvxTextLeftMarginItem const leftMarginEntry(*static_cast<SvxTextLeftMarginItem*>(rEntry.m_pAttr.get()));
1372 leftMarginNew.SetTextLeft(leftMarginEntry.GetTextLeft(), leftMarginEntry.GetPropLeft());
1373 }
1374 SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
1375 SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
1376 for(; nStart <= nEnd; ++nStart)
1377 {
1378 SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
1379 if (!pNode || !pNode->IsTextNode())
1380 continue;
1381
1382 SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
1385 if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
1386 {
1387 leftMarginNew.SetTextLeft(leftMarginOld.GetTextLeft(), leftMarginOld.GetPropLeft());
1388 }
1389 else
1390 {
1391 firstLineNew.SetTextFirstLineOffset(firstLineOld.GetTextFirstLineOffset(), firstLineOld.GetPropTextFirstLineOffset());
1392 firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
1393 }
1394
1395 SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
1396
1397 const SwNumFormat* pNum
1398 = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
1399 if (!pNum)
1400 {
1401 pNum = GetNumFormatFromTextNode(*pTextNode);
1402 }
1403
1404 if ( pNum )
1405 {
1406 // #i103711#
1407 const bool bFirstLineIndentSet =
1410 // #i105414#
1411 const bool bLeftIndentSet =
1414 SyncIndentWithList(firstLineNew, leftMarginNew, *pNum,
1415 bFirstLineIndentSet,
1416 bLeftIndentSet );
1417 }
1418
1419 if (firstLineNew != firstLineOld)
1420 {
1421 pNd->SetAttr(firstLineNew);
1422 }
1423 if (leftMarginNew != leftMarginOld)
1424 {
1425 pNd->SetAttr(leftMarginNew);
1426 }
1427 }
1428 }
1429 }
1430 break;
1431
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;
1436
1438 OSL_ENSURE(false, "What is an annotation doing in the control stack,"
1439 "probably should have been in the endstack");
1440 break;
1441
1443 OSL_ENSURE(false, "What is an input field doing in the control stack,"
1444 "probably should have been in the endstack");
1445 break;
1446
1447 case RES_TXTATR_INETFMT:
1448 {
1449 SwPaM aRegion(rTmpPos);
1451 {
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.
1457 if (nullptr != pFrame)
1458 {
1459 const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
1460 rEntry.m_pAttr.get());
1462 aURL.SetURL(pAttr->GetValue(), false);
1463 aURL.SetTargetFrameName(pAttr->GetTargetFrame());
1464 pFrame->SetFormatAttr(aURL);
1465 }
1466 else
1467 {
1469 }
1470 }
1471 }
1472 break;
1473 default:
1474 SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
1475 break;
1476 }
1477}
1478
1480 sal_uInt16 nWhich)
1481{
1482 const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
1483 if (!pItem)
1484 {
1485 SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
1486 if (!pNd)
1487 pItem = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
1488 else
1489 {
1490 /*
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)
1495 */
1496 if (nWhich == RES_MARGIN_FIRSTLINE
1497 || nWhich == RES_MARGIN_TEXTLEFT
1498 || nWhich == RES_MARGIN_RIGHT)
1499 {
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())
1504 {
1505 switch (nWhich)
1506 {
1508 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordFirstLine.get();
1509 break;
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;
1516 }
1517 }
1518 }
1519
1520 /*
1521 If we're hunting for a character property, try and exact position
1522 within the text node for lookup
1523 */
1524 if (pNd->IsTextNode())
1525 {
1526 const sal_Int32 nPos = rPos.GetContentIndex();
1527 m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
1529 pItem = m_xScratchSet->GetItem(nWhich);
1530 }
1531
1532 if (!pItem)
1533 pItem = &pNd->GetAttr(nWhich);
1534 }
1535 }
1536 return pItem;
1537}
1538
1540 sal_uInt16 nWhich)
1541{
1542 SwFltPosition aFltPos(rPos);
1543
1544 size_t nSize = size();
1545 while (nSize)
1546 {
1547 const SwFltStackEntry& rEntry = (*this)[ --nSize ];
1548 if (rEntry.m_pAttr->Which() == nWhich)
1549 {
1550 if ( (rEntry.m_bOpen) ||
1551 (
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)
1556 )
1557 )
1558 /*
1559 * e.g. half-open range [0-3) so asking for properties at 3
1560 * means props that end at 3 are not included
1561 */
1562 {
1563 return rEntry.m_pAttr.get();
1564 }
1565 }
1566 }
1567 return nullptr;
1568}
1569
1571 const SwFormatField& rFormatField,
1572 sal_uInt16& rBkmNo)
1573{
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())
1579 {
1580 const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
1582 pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
1583 if(ppBkmk != pMarkAccess->getAllMarksEnd())
1584 {
1585 // find Sequence No of corresponding Foot-/Endnote
1586 rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
1587 return true;
1588 }
1589 }
1590 return false;
1591}
1592
1594 SwFltStackEntry& rEntry)
1595{
1596 switch (rEntry.m_pAttr->Which())
1597 {
1598 /*
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
1602 */
1603 case RES_TXTATR_FIELD:
1606 {
1607 SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
1608
1609 SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
1610 SwField* pField = rFormatField.GetField();
1611
1612 if (!RefToVar(pField, rEntry))
1613 {
1614 sal_uInt16 nBkmNo;
1615 if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
1616 {
1617 ::sw::mark::IMark const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
1618
1619 const SwPosition& rBkMrkPos = pMark->GetMarkPos();
1620
1621 SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
1622 if( pText && rBkMrkPos.GetContentIndex() )
1623 {
1624 SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
1625 rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
1626 if( pFootnote )
1627 {
1628 sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
1629
1630 static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
1631
1632 if( pFootnote->GetFootnote().IsEndNote() )
1633 static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
1634 }
1635 }
1636 }
1637 }
1638
1640 MoveAttrs(*aPaM.GetPoint());
1641 }
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;
1651 }
1652}
1653
1654/*
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.
1658
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
1663*/
1664void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
1665{
1666 if (nLen < 0)
1667 {
1669 return;
1670 }
1671
1672 sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
1673 const sal_uInt8* pDel = pData + 1; // Del - Array
1674
1675 sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
1676 const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
1677
1678 short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
1679 if (nRequiredLength > nLen)
1680 {
1681 // would require more data than available to describe!
1682 // discard invalid record
1683 nIns = 0;
1684 nDel = 0;
1685 }
1686
1687 WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
1688
1689 std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
1690
1691 const SwFormat * pSty = nullptr;
1692 sal_uInt16 nTabBase;
1693 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
1694 {
1695 nTabBase = m_vColl[m_nCurrentColl].m_nBase;
1696 if (nTabBase < m_vColl.size()) // Based On
1697 pSty = m_vColl[nTabBase].m_pFormat;
1698 }
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
1705 }
1706
1707 bool bFound = false;
1708 std::unordered_set<size_t> aLoopWatch;
1709 while (pSty && !bFound)
1710 {
1711 const SvxTabStopItem* pTabs;
1712 bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
1713 &pTabs) == SfxItemState::SET;
1714 if( bFound )
1715 {
1716 aAttr.reset(pTabs->Clone());
1717 }
1718 else
1719 {
1720 sal_uInt16 nOldTabBase = nTabBase;
1721 // If based on another
1722 if (nTabBase < m_vColl.size())
1723 nTabBase = m_vColl[nTabBase].m_nBase;
1724
1725 if (
1726 nTabBase < m_vColl.size() &&
1727 nOldTabBase != nTabBase &&
1728 nTabBase != ww::stiNil
1729 )
1730 {
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
1737
1738 if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
1739 aLoopWatch.end())
1740 pSty = nullptr;
1741 }
1742 else
1743 pSty = nullptr; // Give up on the search
1744 }
1745 }
1746
1747 SvxTabStop aTabStop;
1748 for (short i=0; i < nDel; ++i)
1749 {
1750 sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
1751 if( nPos != SVX_TAB_NOTFOUND )
1752 aAttr->Remove( nPos );
1753 }
1754
1755 for (short i=0; i < nIns; ++i)
1756 {
1757 short nPos = SVBT16ToUInt16(pIns + i*2);
1758 aTabStop.GetTabPos() = nPos;
1759 switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
1760 {
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
1775 }
1776
1777 switch( pTyp[i].aBits1 >> 3 & 0x7 )
1778 {
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;
1792 }
1793
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);
1798 }
1799
1800 if (nIns || nDel)
1801 NewAttr(*aAttr);
1802 else
1803 {
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
1812 {
1813 SvxTabStopItem aOrig = pSty ?
1816 NewAttr(aOrig);
1817 }
1818 }
1819}
1820
1825{
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())
1833 {
1834 DateTime aLastPrinted(
1835 msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
1836 ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
1837 xDocuProps->setPrintDate(uDT);
1838 }
1839
1840 // COMPATIBILITY FLAGS START
1841
1842 // #i78951# - remember the unknown compatibility options
1843 // so as to export them out
1846
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
1851 // move tabs on alignment
1853 // #i24363# tab stops relative to indent
1855 // tdf#117923
1860 // tdf#128195
1867
1868 // Import Default Tabs
1869 tools::Long nDefTabSiz = m_xWDop->dxaTab;
1870 if( nDefTabSiz < 56 )
1871 nDefTabSiz = 709;
1872
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;
1876
1878
1879 // Import zoom factor
1880 if (m_xWDop->wScaleSaved)
1881 {
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;
1889 }
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 }));
1895
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);
1900 }
1901
1911
1912 // #i25901# - set new compatibility option
1913 // 'Add paragraph and table spacing at bottom of table cells'
1916
1917 // #i11860# - set new compatibility option
1918 // 'Use former object positioning' to <false>
1920
1921 // #i27767# - set new compatibility option
1922 // 'Consider Wrapping mode when positioning object' to <true>
1924
1926
1927 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
1928
1930
1939 // rely on default for HYPHENATE_URLS=false
1940
1942 if (m_xWDop->fDontBreakWrappedTables)
1943 {
1945 }
1946
1947 // COMPATIBILITY FLAGS END
1948
1949 // Import magic doptypography information, if it's there
1950 if (m_xWwFib->m_nFib > 105)
1951 ImportDopTypography(m_xWDop->doptypography);
1952
1953 // disable form design mode to be able to use imported controls directly
1954 // #i31239# always disable form design mode, not only in protected docs
1955 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1956 if (xDocProps.is())
1957 {
1958 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1959 if (xInfo.is())
1960 {
1961 if (xInfo->hasPropertyByName("ApplyFormDesignMode"))
1962 xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::Any(false));
1963 }
1964
1965 // for the benefit of DOCX - if this is ever saved in that format.
1966 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1967 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1968 { "name", uno::Any(OUString("compatibilityMode")) },
1969 { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
1970 { "val", uno::Any(OUString("11")) } //11: Use features specified in MS-DOC.
1971 }));
1972
1973 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
1974 { "compatSetting", uno::Any(aCompatSetting) }
1975 }));
1976
1977 aGrabBag["CompatSettings"] <<= aValue;
1978 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1979 }
1980
1981 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
1982 // Treat comments-only like read-only since Writer has no support for that.
1983 // Still allow editing of form fields, without requiring the password.
1984 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
1985 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
1987 else if ( xDocProps.is() )
1988 {
1989 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1990 aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc;
1991 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1992 }
1993
1995 if (rOpt.IsUseEnhancedFields())
1997
1998 if (m_xWDop->iGutterPos)
1999 {
2001 }
2002}
2003
2005{
2006 switch (rTypo.m_iLevelOfKinsoku)
2007 {
2008 case 2: // custom
2009 {
2010 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
2011 OUString(+rTypo.m_rgxchLPunct));
2012 // unary + makes sure not to accidentally call the
2013 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
2014 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
2015 // prefix leading up to the first NUL
2017 aForbidden);
2018 // Obviously cannot set the standard level 1 for japanese, so
2019 // bail out now while we can.
2020 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
2021 return;
2022 }
2023 break;
2024 default:
2025 break;
2026 }
2027
2028 /*
2029 This MS hack means that level 2 of japanese is not in operation, so we put
2030 in what we know are the MS defaults, there is a complementary reverse
2031 hack in the writer. Its our default as well, but we can set it anyway
2032 as a flag for later.
2033 */
2034 if (!rTypo.m_reserved2)
2035 {
2036 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
2039 }
2040
2043}
2044
2049 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
2050 mxOldStck(std::move(pRdr->m_xCtrlStck)),
2051 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2052 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2053 mxOldPlcxMan(pRdr->m_xPlcxMan),
2054 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2055 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2056 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2057 mpPrevNumRule(pRdr->m_pPrevNumRule),
2058 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2059 mnInTable(pRdr->m_nInTable),
2060 mnCurrentColl(pRdr->m_nCurrentColl),
2061 mcSymbol(pRdr->m_cSymbol),
2062 mbIgnoreText(pRdr->m_bIgnoreText),
2063 mbSymbol(pRdr->m_bSymbol),
2064 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2065 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2066 mbAnl(pRdr->m_bAnl),
2067 mbInHyperlink(pRdr->m_bInHyperlink),
2068 mbPgSecBreak(pRdr->m_bPgSecBreak),
2069 mbWasParaEnd(pRdr->m_bWasParaEnd),
2070 mbHasBorder(pRdr->m_bHasBorder),
2071 mbFirstPara(pRdr->m_bFirstPara)
2072{
2073 pRdr->m_bSymbol = false;
2074 pRdr->m_bHdFtFootnoteEdn = true;
2075 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2076 = pRdr->m_bHasBorder = false;
2077 pRdr->m_bFirstPara = true;
2078 pRdr->m_nInTable = 0;
2079 pRdr->m_pPreviousNumPaM = nullptr;
2080 pRdr->m_pPrevNumRule = nullptr;
2081 pRdr->m_nCurrentColl = 0;
2082
2083 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2084 *pRdr));
2085
2086 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2087
2088 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2089
2090 // Save the attribute manager: we need this as the newly created PLCFx Manager
2091 // access the same FKPs as the old one and their Start-End position changes.
2092 if (pRdr->m_xPlcxMan)
2093 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2094
2095 if (nStartCp != -1)
2096 {
2097 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2098 mxOldPlcxMan->GetManType(), nStartCp);
2099 }
2100
2101 maOldApos.push_back(false);
2102 maOldApos.swap(pRdr->m_aApos);
2103 maOldFieldStack.swap(pRdr->m_aFieldStack);
2104}
2105
2107{
2108 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2109 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2112 pRdr->m_xTableDesc = std::move(mxTableDesc);
2113 pRdr->m_cSymbol = mcSymbol;
2114 pRdr->m_bSymbol = mbSymbol;
2118 pRdr->m_nInTable = mnInTable;
2119 pRdr->m_bAnl = mbAnl;
2124 pRdr->m_bHasBorder = mbHasBorder;
2125 pRdr->m_bFirstPara = mbFirstPara;
2126
2127 // Close all attributes as attributes could be created that extend the Fly
2128 pRdr->DeleteCtrlStack();
2129 pRdr->m_xCtrlStck = std::move(mxOldStck);
2130
2131 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2132
2133 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2134 // place, or somewhere close if that place got destroyed
2135 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2136 pRdr->m_oLastAnchorPos.reset();
2137
2138 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2139
2140 if (xLastAnchorCursor)
2141 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2142
2143 pRdr->DeleteAnchorStack();
2144 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2145
2146 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2147
2148 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2149 pRdr->m_xPlcxMan = mxOldPlcxMan;
2150 if (pRdr->m_xPlcxMan)
2151 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2152 pRdr->m_aApos.swap(maOldApos);
2153 pRdr->m_aFieldStack.swap(maOldFieldStack);
2154}
2155
2157 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2158{
2159 if (nStartCp < 0 || nLen < 0)
2160 return;
2161
2162 // Saves Flags (amongst other things) and resets them
2163 WW8ReaderSave aSave( this );
2164
2165 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2166
2167 // Read Text for Header, Footer or Footnote
2168 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2169 aSave.Restore( this );
2170}
2171
2176{
2177 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2178 if (!pSD)
2179 return 0;
2180
2181 const void* pData = pSD->GetData();
2182 if (!pData)
2183 return 0;
2184
2185 OUString sAuthor;
2186 OUString sInitials;
2187 if( m_bVer67 )
2188 {
2189 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2190 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2191 if (pA)
2192 sAuthor = *pA;
2193 else
2194 {
2195 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2196 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2197 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2198 }
2199 }
2200 else
2201 {
2202 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2203 {
2204 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2205 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2206 OUStringBuffer aBuf;
2207 aBuf.setLength(nLen);
2208 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2209 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2210 sInitials = aBuf.makeStringAndClear();
2211 }
2212
2213 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2214 sAuthor = *pA;
2215 else
2216 sAuthor = sInitials;
2217 }
2218
2219 sal_uInt32 nDateTime = 0;
2220
2221 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2222 {
2223 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2224 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2225 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2226 }
2227
2228 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2229
2230 OUString sText;
2232 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2233
2235 SwPostItField aPostIt(
2237 sText, sInitials, OUString(), aDate );
2238 aPostIt.SetTextObject(std::move(pOutliner));
2239
2240 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2241 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2243 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2244 // If this is a range, make sure that it ends after the just inserted character, not before it.
2246
2247 return 0;
2248}
2249
2251 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2252{
2253 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2254 OSL_ENSURE(pSttIdx, "impossible");
2255 if (!pSttIdx)
2256 return;
2257
2258 SwPosition aTmpPos(*m_pPaM->GetPoint());
2259
2260 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2261
2262 // tdf#122425: Explicitly remove borders and spacing
2265
2266 SwFlyFrameFormat* pFrame
2267 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2268
2269 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2270 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2271 pFrame->SetFormatAttr( aAnch );
2273 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2274 if( eFrameSize != aSz.GetWidthSizeType() )
2275 aSz.SetWidthSizeType( eFrameSize );
2276 pFrame->SetFormatAttr(aSz);
2277 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2278 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2279
2280 // #i43427# - send frame for header/footer into background.
2281 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2282 SdrObject* pFrameObj = CreateContactObject( pFrame );
2283 OSL_ENSURE( pFrameObj,
2284 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2285 if ( pFrameObj )
2286 {
2287 pFrameObj->SetOrdNum( 0 );
2288 }
2289 MoveInsideFly(pFrame);
2290
2291 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2292
2293 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2294
2295 MoveOutsideFly(pFrame, aTmpPos);
2296}
2297
2298void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2299{
2300 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2301 if (!pSttIdx)
2302 return;
2303
2304 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2305
2306 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2307
2308 *m_pPaM->GetPoint() = aTmpPos;
2309}
2310
2312{
2313 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2314 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2315}
2316
2318 int nSect)
2319{
2320 if (m_xHdFt)
2321 {
2322 WW8_CP nStart, nLen;
2323 sal_uInt8 nNumber = 5;
2324
2325 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2326 {
2327 if (nI & nWhichItems)
2328 {
2329 bool bOk = true;
2330 if( m_bVer67 )
2331 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2332 else
2333 {
2334 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2335 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2336 }
2337
2338 if (bOk)
2339 return true;
2340 }
2341 }
2342 }
2343 return false;
2344}
2345
2346void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2347 const wwSection &rSection)
2348{
2349 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2350 SwPageDesc *pPD = rSection.mpPage;
2351
2352 if( !m_xHdFt )
2353 return;
2354
2355 WW8_CP nStart, nLen;
2356 sal_uInt8 nNumber = 5;
2357
2358 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2359 // corresponding to bit fields in grpfIhdt indicating which
2360 // header/footer(s) are present in this section
2361 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2362 {
2363 if (nI & grpfIhdt)
2364 {
2365 bool bOk = true;
2366 if( m_bVer67 )
2367 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2368 else
2369 {
2370 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2371 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2372 }
2373
2374 bool bUseLeft
2375 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2376 bool bUseFirst
2377 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2378
2379 // If we are loading a first-page header/footer which is not
2380 // actually enabled in this section (it still needs to be
2381 // loaded as it may be inherited by a later section)
2382 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2383
2384 bool bFooter
2385 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2386
2387 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2388 : bUseFirst ? pPD->GetFirstMaster()
2389 : pPD->GetMaster();
2390
2391 SwFrameFormat* pHdFtFormat;
2392 // If we have empty first page header and footer.
2393 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2394 if (bFooter)
2395 {
2396 m_bIsFooter = true;
2397 //#i17196# Cannot have left without right
2398 if (!bDisabledFirst
2399 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2401 if (bUseLeft)
2402 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2403 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2405 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2406 }
2407 else
2408 {
2409 m_bIsHeader = true;
2410 //#i17196# Cannot have left without right
2411 if (!bDisabledFirst
2412 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2414 if (bUseLeft)
2415 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2416 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2418 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2419 }
2420
2421 if (bOk)
2422 {
2423 bool bHackRequired = false;
2424 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2425 bHackRequired = true;
2426 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2427 bHackRequired = true;
2428
2429 if (bHackRequired)
2430 {
2431 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2432 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2433 }
2434 else
2435 Read_HdFtText(nStart, nLen, pHdFtFormat);
2436 }
2437 else if (pPrev)
2438 CopyPageDescHdFt(pPrev, pPD, nI);
2439
2440 m_bIsHeader = m_bIsFooter = false;
2441 }
2442 }
2443}
2444
2446{
2447 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2448}
2449
2450void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2451 const wwSection *pPrevious)
2452{
2453 // Header/Footer not present
2454 if (!rSection.maSep.grpfIhdt)
2455 return;
2456
2457 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2458 if (rSection.mpPage)
2459 {
2460 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2461 rSection);
2462 }
2463
2464 // Header/Footer - Update Index
2465 // So that the index is still valid later on
2466 if (mrReader.m_xHdFt)
2467 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2468
2469}
2470
2472{
2474
2475 const SwNumRule* pRule = nullptr;
2476
2477 if (pText != nullptr)
2478 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2479
2480 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2481 // set it as a separate paragraph property, just like we do for DOCX.
2482 // This is only being used for numbering currently, so limiting to that context.
2483 if (pRule)
2484 {
2487 items(m_pPaM->GetDoc().GetAttrPool());
2488
2489 SfxWhichIter aIter(items);
2490 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2491 {
2492 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2493 if (pItem)
2494 items.Put(*pItem);
2495 }
2497 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2498 pText->SetAttr(item);
2499 }
2500
2501 if (
2502 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2504 )
2505 {
2506 // If after spacing is set to auto, set the after space to 0
2507 if (m_bParaAutoAfter)
2509
2510 // If the previous textnode had numbering and
2511 // and before spacing is set to auto, set before space to 0
2514
2515 // If the previous numbering rule was different we need
2516 // to insert a space after the previous paragraph
2517 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2519
2520 // cache current paragraph
2522 {
2523 delete m_pPreviousNumPaM;
2524 m_pPreviousNumPaM = nullptr;
2525 }
2526
2528 m_pPrevNumRule = pRule;
2529 }
2530 else if(!pRule && m_pPreviousNumPaM)
2531 {
2532 // If the previous paragraph has numbering but the current one does not
2533 // we need to add a space after the previous paragraph
2535 delete m_pPreviousNumPaM;
2536 m_pPreviousNumPaM = nullptr;
2537 m_pPrevNumRule = nullptr;
2538 }
2539 else
2540 {
2541 // clear paragraph cache
2543 {
2544 delete m_pPreviousNumPaM;
2545 m_pPreviousNumPaM = nullptr;
2546 }
2547 m_pPrevNumRule = pRule;
2548 }
2549
2550 // If this is the first paragraph in the document and
2551 // Auto-spacing before paragraph is set,
2552 // set the upper spacing value to 0
2553 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2555
2556 m_bFirstPara = false;
2557
2559
2560 // We can flush all anchored graphics at the end of a paragraph.
2561 m_xAnchorStck->Flush();
2562}
2563
2564bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2565{
2566 bool bRet = false;
2567 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2568
2569 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2570
2571 if(pULSpaceItem != nullptr)
2572 {
2573 SvxULSpaceItem aUL(*pULSpaceItem);
2574
2575 if(bIsUpper)
2576 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2577 else
2578 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2579
2580 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2581 rMyPam.GetPoint()->SetContent(0);
2582 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2583 rMyPam.GetPoint()->SetContent(nEnd);
2584 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2585 bRet = true;
2586 }
2587 return bRet;
2588}
2589
2591{
2592 return SetSpacing(rMyPam, nSpace, false);
2593}
2594
2596{
2597 return SetSpacing(rMyPam, nSpace, true);
2598}
2599
2600sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2601{
2602 if (m_bVer67)
2603 return NS_sprm::v6::sprmPTtp;
2605}
2606
2608{
2609 // Frame/Table/Anl
2610 if (m_bAnl)
2611 StopAllAnl(); // -> bAnl = false
2612
2613 while(m_aApos.size() > 1)
2614 {
2615 StopTable();
2616 m_aApos.pop_back();
2617 --m_nInTable;
2618 if (m_aApos[m_nInTable])
2619 StopApo();
2620 }
2621
2622 if (m_aApos[0])
2623 StopApo();
2624
2625 OSL_ENSURE(!m_nInTable, "unclosed table!");
2626}
2627
2628bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2629{
2630 // Frame/Table/Anl
2631 if (m_bInHyperlink)
2632 return false;
2633
2634 rbReSync = false;
2635
2636 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2637
2638 // TabRowEnd
2639 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2640
2641// Unfortunately, for every paragraph we need to check first whether
2642// they contain a sprm 29 (0x261B), which starts an APO.
2643// All other sprms then refer to that APO and not to the normal text
2644// surrounding it.
2645// The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2646
2647// WW: Table in APO is possible (Both Start-Ends occur at the same time)
2648// WW: APO in Table not possible
2649
2650// This mean that of a Table is the content of an APO, the APO start needs
2651// to be edited first, so that the Table remains in the APO and not the
2652// other way around.
2653// At the End, however, we need to edit the Table End first as the APO
2654// must end after that Table (or else we never find the APO End).
2655
2656// The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2657
2658// If the Table is within an APO the TabRowEnd Area misses the
2659// APO settings.
2660// To not end the APO there, we do not call ProcessApo
2661
2662// KHZ: When there is a table inside the Apo the Apo-flags are also
2663// missing for the 2nd, 3rd... paragraphs of each cell.
2664
2665// 1st look for in-table flag, for 2000+ there is a subtable flag to
2666// be considered, the sprm 6649 gives the level of the table
2667 sal_uInt8 nCellLevel = 0;
2668
2669 if (m_bVer67)
2670 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2671 else
2672 {
2673 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2674 if (!nCellLevel)
2675 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2676 }
2677 do
2678 {
2679 WW8_TablePos *pTabPos=nullptr;
2680 WW8_TablePos aTabPos;
2681 if(nCellLevel && !m_bVer67)
2682 {
2683 WW8PLCFxSave1 aSave;
2684 m_xPlcxMan->GetPap()->Save( aSave );
2685 rbReSync = true;
2686 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2687 WW8_CP nMyStartCp=nStartCp;
2688
2689 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2690 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2691 nCellLevel = *aLevel.pSprm;
2692
2693 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2694
2695 // Bad Table, remain unchanged in level, e.g. #i19667#
2696 if (!bHasRowEnd)
2697 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2698
2699 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2700 pTabPos = &aTabPos;
2701
2702 m_xPlcxMan->GetPap()->Restore( aSave );
2703 }
2704
2705 // Then look if we are in an Apo
2706
2707 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2708
2709 // Look to see if we are in a Table, but Table in foot/end note not allowed
2710 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2711
2712 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2713
2714 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2715 // WW8TabDesc::TableCellEnd() from making nonsense
2716
2717 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2718 bStopTab = bStartTab = true; // Required to stop and start table
2719
2720 // Test for Anl (Numbering) and process all events in the right order
2721 if( m_bAnl && !bTableRowEnd )
2722 {
2723 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2724 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2725 if (pSprm13 && aSprm13.nRemainingData >= 1)
2726 { // Still Anl left?
2727 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2728 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2729 || aApo.HasStartStop() // Forced Anl end
2730 || bStopTab || bStartTab )
2731 {
2732 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2733 }
2734 else
2735 {
2736 NextAnlLine( pSprm13 ); // Next Anl Line
2737 }
2738 }
2739 else
2740 { // Regular Anl end
2741 StopAllAnl(); // Actual end
2742 }
2743 }
2744 if (bStopTab)
2745 {
2746 StopTable();
2747 m_aApos.pop_back();
2748 --m_nInTable;
2749 }
2750 if (aApo.mbStopApo)
2751 {
2752 StopApo();
2753 m_aApos[m_nInTable] = false;
2754 }
2755
2756 if (aApo.mbStartApo)
2757 {
2758 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2759 // We need an ReSync after StartApo
2760 // (actually only if the Apo extends past a FKP border)
2761 rbReSync = true;
2762 }
2763 if (bStartTab)
2764 {
2765 WW8PLCFxSave1 aSave;
2766 m_xPlcxMan->GetPap()->Save( aSave );
2767
2768 // Numbering for cell borders causes a crash -> no Anls in Tables
2769 if (m_bAnl)
2770 StopAllAnl();
2771
2772 if(m_nInTable < nCellLevel)
2773 {
2774 if (StartTable(nStartCp))
2775 ++m_nInTable;
2776 else
2777 break;
2778 m_aApos.push_back(false);
2779 }
2780
2781 if(m_nInTable >= nCellLevel)
2782 {
2783 // We need an ReSync after StartTable
2784 // (actually only if the Apo extends past a FKP border)
2785 rbReSync = true;
2786 m_xPlcxMan->GetPap()->Restore( aSave );
2787 }
2788 }
2789 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2790 return bTableRowEnd;
2791}
2792
2794{
2795 /*
2796 #i22206#/#i52786#
2797 The (default) character set used for a run of text is the default
2798 character set for the version of Word that last saved the document.
2799
2800 This is a bit tentative, more might be required if the concept is correct.
2801 When later version of word write older 6/95 documents the charset is
2802 correctly set in the character runs involved, so it's hard to reproduce
2803 documents that require this to be sure of the process involved.
2804 */
2805 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
2806 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2807 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2809}
2810
2812{
2813 /*
2814 #i22206#/#i52786#
2815 The (default) character set used for a run of text is the default
2816 character set for the version of Word that last saved the document.
2817
2818 This is a bit tentative, more might be required if the concept is correct.
2819 When later version of word write older 6/95 documents the charset is
2820 correctly set in the character runs involved, so it's hard to reproduce
2821 documents that require this to be sure of the process involved.
2822 */
2823 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_CJK_LANGUAGE));
2824 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2825 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2827}
2828
2830{
2831 /*
2832 #i2015
2833 If the hard charset is set use it, if not see if there is an open
2834 character run that has set the charset, if not then fallback to the
2835 current underlying paragraph style.
2836 */
2837 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2838 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2839 {
2840 if (!m_bVer67)
2841 eSrcCharSet = GetCharSetFromLanguage();
2842 else if (!m_aFontSrcCharSets.empty())
2843 eSrcCharSet = m_aFontSrcCharSets.top();
2844 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2845 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2846 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2847 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2848 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2849 eSrcCharSet = GetCharSetFromLanguage();
2850 }
2851 return eSrcCharSet;
2852}
2853
2854//Takashi Ono for CJK
2856{
2857 /*
2858 #i2015
2859 If the hard charset is set use it, if not see if there is an open
2860 character run that has set the charset, if not then fallback to the
2861 current underlying paragraph style.
2862 */
2863 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2864 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2865 {
2866 if (!m_aFontSrcCJKCharSets.empty())
2867 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2868 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2869 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2870 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2871 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2872 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2873 eSrcCharSet = GetCJKCharSetFromLanguage();
2874 }
2875 return eSrcCharSet;
2876}
2877
2879{
2880 if (m_pPostProcessAttrsInfo == nullptr)
2881 return;
2882
2883 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2884
2885 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2886 {
2887 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2888 *pItem);
2889 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2890 pItem->Which());
2891 }
2892
2894}
2895
2896/*
2897 #i9240#
2898 It appears that some documents that are in a baltic 8 bit encoding which has
2899 some undefined characters can have use made of those characters, in which
2900 case they default to CP1252. If not then it's perhaps that the font encoding
2901 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2902 is always 1252.
2903
2904 So an encoding converter that on an undefined character attempts to
2905 convert from 1252 on the undefined character
2906*/
2907static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2908 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2909{
2910 const sal_uInt32 nFlags =
2911 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2912 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2913 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2914 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2915
2916 const sal_uInt32 nFlags2 =
2917 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2918 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2919 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2920 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2921
2922 std::size_t nDestChars=0;
2923 std::size_t nConverted=0;
2924
2925 do
2926 {
2927 sal_uInt32 nInfo = 0;
2928 sal_Size nThisConverted=0;
2929
2930 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2931 pIn+nConverted, nInLen-nConverted,
2932 pOut+nDestChars, nOutLen-nDestChars,
2933 nFlags, &nInfo, &nThisConverted);
2934
2935 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2936
2937 nConverted += nThisConverted;
2938
2939 if (
2940 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2941 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2942 )
2943 {
2944 sal_Size nOtherConverted;
2945 rtl_TextToUnicodeConverter hCP1252Converter =
2946 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2947 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2948 pIn+nConverted, 1,
2949 pOut+nDestChars, nOutLen-nDestChars,
2950 nFlags2, &nInfo, &nOtherConverted);
2951 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2952 nConverted+=1;
2953 }
2954 } while (nConverted < nInLen);
2955
2956 return nDestChars;
2957}
2958
2960{
2961 bool bResult = false;
2962
2963 switch (static_cast<sal_uInt16>(nLang))
2964 {
2965 case 0x1401: // Arabic(Algeria)
2966 case 0x3c01: // Arabic(Bahrain)
2967 case 0xc01: // Arabic(Egypt)
2968 case 0x801: // Arabic(Iraq)
2969 case 0x2c01: // Arabic (Jordan)
2970 case 0x3401: // Arabic(Kuwait)
2971 case 0x3001: // Arabic(Lebanon)
2972 case 0x1001: // Arabic(Libya)
2973 case 0x1801: // Arabic(Morocco)
2974 case 0x2001: // Arabic(Oman)
2975 case 0x4001: // Arabic(Qatar)
2976 case 0x401: // Arabic(Saudi Arabia)
2977 case 0x2801: // Arabic(Syria)
2978 case 0x1c01: // Arabic(Tunisia)
2979 case 0x3801: // Arabic(U.A.E)
2980 case 0x2401: // Arabic(Yemen)
2981 bResult = true;
2982 break;
2983 default:
2984 break;
2985 }
2986
2987 return bResult;
2988}
2989
2991{
2992 if (nChar >= 0x0030 && nChar <= 0x0039)
2993 return nChar + 0x0630;
2994
2995 return nChar;
2996}
2997
2998namespace
2999{
3000 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3001 {
3002 //if read len was in or around that of allocated len, just reuse pStr
3003 if (nAllocLen < pStr->length + 256)
3004 return OUString(pStr, SAL_NO_ACQUIRE);
3005 //otherwise copy the shorter used section to release extra mem
3006 OUString sRet(pStr->buffer, pStr->length);
3007 rtl_uString_release(pStr);
3008 return sRet;
3009 }
3010}
3011
3015bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3016{
3017 sal_Int32 nRequestedStrLen = nEnd - rPos;
3018
3019 OSL_ENSURE(nRequestedStrLen, "String is 0");
3020 if (nRequestedStrLen <= 0)
3021 return true;
3022
3023 WW8_CP nCp;
3024 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3025 if (bFail)
3026 {
3027 rPos+=nRequestedStrLen;
3028 return true;
3029 }
3030
3031 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3032 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3033 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3034 if (!bValidPos)
3035 {
3036 // Swallow missing range, e.g. #i95550#
3037 rPos+=nRequestedStrLen;
3038 return true;
3039 }
3040
3041 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3042 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3043 if (!nAvailableStrLen)
3044 {
3045 // Swallow missing range, e.g. #i95550#
3046 rPos+=nRequestedStrLen;
3047 return true;
3048 }
3049
3050 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3051
3052 // Reset Unicode flag and correct FilePos if needed.
3053 // Note: Seek is not expensive, as we're checking inline whether or not
3054 // the correct FilePos has already been reached.
3055 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3056
3057 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3058 RTL_TEXTENCODING_MS_1252;
3059 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3060 {
3061 /*
3062 fdo#82904
3063
3064 Older documents exported as word 95 that use unicode aware fonts will
3065 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3066 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3067 pain.
3068
3069 We will try and use a fallback encoding if the conversion from
3070 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3071 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3072 valid RTL_TEXTENCODING_MS_932 by chance :-(
3073
3074 We're not the only ones that struggle with this: Here's the help from
3075 MSOffice 2003 on the topic:
3076
3077 <<
3078 Earlier versions of Microsoft Word were sometimes used in conjunction with
3079 third-party language-processing add-in programs designed to support Chinese or
3080 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3081 results in incorrect text display in more recent versions of Word.
3082
3083 However, you can set options to convert these documents so that text is
3084 displayed correctly. On the Tools menu, click Options, and then click the
3085 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3086 text (to have Word interpret the text as Asian code page data, regardless of
3087 its font) or Automatically detect Asian text (to have Word attempt to determine
3088 which parts of the text are meant to be Asian).
3089 >>
3090
3091 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3092 the language is not Japanese
3093 */
3094
3096 if (pItem != nullptr && LANGUAGE_JAPANESE != static_cast<const SvxLanguageItem *>(pItem)->GetLanguage())
3097 {
3098 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3099 eSrcCharSet = GetCharSetFromLanguage();
3100 }
3101 }
3102 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3103 RTL_TEXTENCODING_MS_1252;
3104
3105 // allocate unicode string data
3106 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3107 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3108 sal_Unicode* pBuffer = xStr->buffer;
3109 sal_Unicode* pWork = pBuffer;
3110
3111 std::unique_ptr<char[]> p8Bits;
3112
3113 rtl_TextToUnicodeConverter hConverter = nullptr;
3114 if (!m_bIsUnicode || m_bVer67)
3115 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3116
3117 if (!m_bIsUnicode)
3118 p8Bits.reset( new char[nStrLen] );
3119
3120 // read the stream data
3121 sal_uInt8 nBCode = 0;
3122 sal_uInt16 nUCode;
3123
3124 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3126 if (pItem != nullptr)
3127 nCTLLang = static_cast<const SvxLanguageItem *>(pItem)->GetLanguage();
3128
3129 sal_Int32 nL2;
3130 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3131 {
3132 if (m_bIsUnicode)
3133 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3134 else
3135 {
3136 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3137 nUCode = nBCode;
3138 }
3139
3140 if (!m_pStrm->good())
3141 {
3142 rPos = WW8_CP_MAX-10; // -> eof or other error
3143 return true;
3144 }
3145
3146 if ((32 > nUCode) || (0xa0 == nUCode))
3147 {
3148 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3149 break; // Special character < 32, == 0xa0 found
3150 }
3151
3152 if (m_bIsUnicode)
3153 {
3154 if (!m_bVer67)
3155 *pWork++ = nUCode;
3156 else
3157 {
3158 if (nUCode >= 0x3000) //0x8000 ?
3159 {
3160 char aTest[2];
3161 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3162 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3163 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3164 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3165 *pWork++ = aTemp[0];
3166 }
3167 else
3168 {
3169 char cTest = static_cast< char >(nUCode & 0x00FF);
3170 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3171 }
3172 }
3173 }
3174 else
3175 p8Bits[nL2] = nBCode;
3176 }
3177
3178 if (nL2)
3179 {
3180 const sal_Int32 nEndUsed = !m_bIsUnicode
3181 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3182 : pWork - pBuffer;
3183
3185 {
3186 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3187 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3188 }
3189
3190 xStr->buffer[nEndUsed] = 0;
3191 xStr->length = nEndUsed;
3192
3193 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3194 rPos += nL2;
3195 if (!m_aApos.back()) // a para end in apo doesn't count
3196 m_bWasParaEnd = false; // No CR
3197 }
3198
3199 if (hConverter)
3200 rtl_destroyTextToUnicodeConverter(hConverter);
3201 return nL2 >= nStrLen;
3202}
3203
3204#define MSASCII SAL_MAX_INT16
3205
3206namespace
3207{
3208 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
3209 sal_Int16 lcl_getScriptType(
3210 const uno::Reference<i18n::XBreakIterator>& rBI,
3211 const OUString &rString, sal_Int32 nPos)
3212 {
3213 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3214 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3215 nScript = MSASCII;
3216 return nScript;
3217 }
3218
3219 // We want to know about WEAK segments, so endOfScript isn't
3220 // useful, and see lcl_getScriptType anyway
3221 sal_Int32 lcl_endOfScript(
3222 const uno::Reference<i18n::XBreakIterator>& rBI,
3223 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3224 {
3225 while (nPos < rString.getLength())
3226 {
3227 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3228 if (nScript != nNewScript)
3229 break;
3230 ++nPos;
3231 }
3232 return nPos;
3233 }
3234
3235 sal_Int32 lcl_getWriterScriptType(
3236 const uno::Reference<i18n::XBreakIterator>& rBI,
3237 const OUString &rString, sal_Int32 nPos)
3238 {
3239 sal_Int16 nScript = i18n::ScriptType::WEAK;
3240
3241 if (rString.isEmpty())
3242 return nScript;
3243
3244 while (nPos >= 0)
3245 {
3246 nScript = rBI->getScriptType(rString, nPos);
3247 if (nScript != i18n::ScriptType::WEAK)
3248 break;
3249 --nPos;
3250 }
3251
3252 return nScript;
3253 }
3254
3255 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3256 {
3257 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3258 }
3259
3260 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3261 {
3262 // Ignoring CharSet, and ignoring unknown pitch
3263 return rA.GetFamilyName() == rB.GetFamilyName() &&
3264 rA.GetStyleName() == rB.GetStyleName() &&
3265 rA.GetFamily() == rB.GetFamily() &&
3266 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3267 }
3268}
3269
3270// In writer we categorize text into CJK, CTL and "Western" for everything else.
3271// Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3272// NonEastAsian/HighAnsi, with some shared characters and some properties to
3273// hint as to which way to bias those shared characters.
3274
3275// That's four categories, we however have three categories. Given that problem
3276// here we would ideally find out "what would word do" to see what font/language
3277// word would assign to characters based on the unicode range they fall into and
3278// hack the word one onto the range we use. However it's unclear what word's
3279// categorization is. So we don't do that here yet.
3280
3281// Additional to the categorization, when word encounters weak text for ambiguous
3282// chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3283// feature in writer.
3284
3285// So what we currently do here then is to split our text into non-weak/weak
3286// sections and uses word's idcthint to determine what font it would use and
3287// force that on for the segment. Following what we *do* know about word's
3288// categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3289// word, something we map to LATIN, so we consider all weak chars in that range
3290// to auto-bias to LATIN.
3291
3292// See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
3294{
3295 if (rAddString.isEmpty())
3296 return;
3297
3298 if (m_bFuzzing)
3299 {
3300 simpleAddTextToParagraph(rAddString);
3301 return;
3302 }
3303
3304 uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
3305 assert(xBI.is());
3306
3307 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3308 sal_Int32 nLen = rAddString.getLength();
3309
3310 OUString sParagraphText;
3311 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3312 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3313 if (pNd)
3314 sParagraphText = pNd->GetText();
3315 sal_Int32 nParaOffset = sParagraphText.getLength();
3316 sParagraphText = sParagraphText + rAddString;
3317
3318 sal_Int32 nPos = 0;
3319 while (nPos < nLen)
3320 {
3321 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3322 if (nEnd < 0)
3323 break;
3324
3325 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3326 const sal_uInt16 aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3327 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3328 bool aForced[] = {false, false, false};
3329
3330 int nLclIdctHint = 0xFF;
3331 if (nScript == i18n::ScriptType::WEAK)
3332 {
3333 const SfxInt16Item *pIdctHint = static_cast<const SfxInt16Item*>(GetFormatAttr(RES_CHRATR_IDCTHINT));
3334 nLclIdctHint = pIdctHint->GetValue();
3335 }
3336 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3337 nLclIdctHint = 0;
3338
3339 sal_uInt16 nForceFromFontId = 0;
3340 if (nLclIdctHint != 0xFF)
3341 {
3342 switch (nLclIdctHint)
3343 {
3344 case 0:
3345 nForceFromFontId = RES_CHRATR_FONT;
3346 break;
3347 case 1:
3348 nForceFromFontId = RES_CHRATR_CJK_FONT;
3349 break;
3350 case 2:
3351 nForceFromFontId = RES_CHRATR_CTL_FONT;
3352 break;
3353 default:
3354 break;
3355 }
3356 }
3357
3358 if (nForceFromFontId != 0)
3359 {
3360 // Now we know that word would use the nForceFromFontId font for this range
3361 // Try and determine what script writer would assign this range to
3362
3363 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3364 nPos + nParaOffset);
3365
3366 bool bWriterWillUseSameFontAsWordAutomatically = false;
3367
3368 if (nWriterScript != i18n::ScriptType::WEAK)
3369 {
3370 if (
3371 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3372 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3373 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3374 )
3375 {
3376 bWriterWillUseSameFontAsWordAutomatically = true;
3377 }
3378 else
3379 {
3380 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3381 sal_uInt16 nDestId = aIds[nWriterScript-1];
3382 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(nDestId));
3383 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3384 }
3385 }
3386
3387 // Writer won't use the same font as word, so force the issue
3388 if (!bWriterWillUseSameFontAsWordAutomatically)
3389 {
3390 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3391
3392 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3393 {
3394 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(aIds[i]));
3395 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3396 if (aForced[i])
3397 {
3398 pOverriddenItems[i] =
3399 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3400
3401 SvxFontItem aForceFont(*pSourceFont);
3402 aForceFont.SetWhich(aIds[i]);
3403 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3404 }
3405 }
3406 }
3407 }
3408
3410
3411 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3412 {
3413 if (aForced[i])
3414 {
3415 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3416 if (pOverriddenItems[i])
3417 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3418 }
3419 }
3420
3421 nPos = nEnd;
3422 if (nPos < nLen)
3423 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3424 }
3425}
3426
3427namespace sw {
3428
3429auto FilterControlChars(std::u16string_view aString) -> OUString
3430{
3431 OUStringBuffer buf(aString.size());
3432 for (size_t i = 0; i < aString.size(); ++i)
3433 {
3434 sal_Unicode const ch(aString[i]);
3435 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3436 {
3437 buf.append(ch);
3438 }
3439 else
3440 {
3441 SAL_INFO("sw.ww8", "filtering control character");
3442 }
3443 }
3444 return buf.makeStringAndClear();
3445}
3446
3447} // namespace sw
3448
3449void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
3450{
3451 OUString const addString(sw::FilterControlChars(aAddString));
3452
3453 if (addString.isEmpty())
3454 return;
3455
3456 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3457 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3458
3459 OSL_ENSURE(pNd, "What the hell, where's my text node");
3460
3461 if (!pNd)
3462 return;
3463
3464 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3465 if (nCharsLeft > 0)
3466 {
3467 if (addString.getLength() <= nCharsLeft)
3468 {
3470 }
3471 else
3472 {
3473 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3475 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3476 }
3477 }
3478 else
3479 {
3482 }
3483
3484 m_bReadTable = false;
3485}
3486
3490bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3491 tools::Long nCpOfs)
3492{
3493 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3494
3495 if (m_bSymbol || m_bIgnoreText)
3496 {
3497 WW8_CP nRequested = nEnd - rPos;
3498 if (m_bSymbol) // Insert special chars
3499 {
3500 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3501 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3502 {
3503 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3504 nRequested = nMaxPossible;
3505 }
3506
3508 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3509 {
3510 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3511 {
3513 }
3517 }
3518 }
3519 m_pStrm->SeekRel(nRequested);
3520 rPos = nEnd; // Ignore until attribute end
3521 return false;
3522 }
3523
3524 while (true)
3525 {
3526 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3527 return false; // Done
3528
3529 bool bStartLine = ReadChar(rPos, nCpOfs);
3530 rPos++;
3531 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3532 {
3533 return bStartLine;
3534 }
3535 }
3536}
3537
3539{
3540 bool bParaEndAdded = false;
3541 // #i1909# section/page breaks should not occur in tables, word
3542 // itself ignores them in this case.
3543 if (!m_nInTable)
3544 {
3545 bool IsTemp=true;
3547 if (pTemp && pTemp->GetText().isEmpty()
3549 {
3550 IsTemp = false;
3553 }
3554
3555 m_bPgSecBreak = true;
3556 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3557 /*
3558 If it's a 0x0c without a paragraph end before it, act like a
3559 paragraph end, but nevertheless, numbering (and perhaps other
3560 similar constructs) do not exist on the para.
3561 */
3562 if (!m_bWasParaEnd && IsTemp)
3563 {
3564 bParaEndAdded = true;
3565 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3566 {
3567 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3568 {
3569 pTextNode->SetAttr(
3571 }
3572 }
3573 }
3574 }
3575 return bParaEndAdded;
3576}
3577
3579{
3580 bool bNewParaEnd = false;
3581 // Reset Unicode flag and correct FilePos if needed.
3582 // Note: Seek is not expensive, as we're checking inline whether or not
3583 // the correct FilePos has already been reached.
3584 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3585 if (!checkSeek(*m_pStrm, nRequestedPos))
3586 return false;
3587
3588 sal_uInt16 nWCharVal(0);
3589 if( m_bIsUnicode )
3590 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3591 else
3592 {
3593 sal_uInt8 nBCode(0);
3594 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3595 nWCharVal = nBCode;
3596 }
3597
3598 sal_Unicode cInsert = '\x0';
3599 bool bParaMark = false;
3600
3601 if ( 0xc != nWCharVal )
3602 m_bFirstParaOfPage = false;
3603
3604 switch (nWCharVal)
3605 {
3606 case 0:
3607 if (!m_bFuzzing)
3608 {
3609 // Page number
3610 SwPageNumberField aField(
3614 }
3615 else
3616 {
3617 // extremely slow, so skip for fuzzing, and insert a space instead
3618 cInsert = ' ';
3619 }
3620 break;
3621 case 0xe:
3622 // if there is only one column word treats a column break like a pagebreak.
3624 bParaMark = HandlePageBreakChar();
3625 else if (!m_nInTable)
3626 {
3627 // Always insert a txtnode for a column break, e.g. ##
3629 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3632 }
3633 break;
3634 case 0x7:
3635 {
3636 bNewParaEnd = true;
3637 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3638 //The last paragraph of each cell is terminated by a special
3639 //paragraph mark called a cell mark. Following the cell mark
3640 //that ends the last cell of a table row, the table row is
3641 //terminated by a special paragraph mark called a row mark
3642 //
3643 //So the 0x7 should be right at the end of the previous
3644 //range to be a real cell-end.
3645 if (pPap->nOrigStartPos == nPosCp+1 ||
3646 pPap->nOrigStartPos == WW8_CP_MAX)
3647 {
3648 TabCellEnd(); // Table cell/row end
3649 }
3650 else
3651 bParaMark = true;
3652 }
3653 break;
3654 case 0xf:
3655 if( !m_bSpec ) // "Satellite"
3656 cInsert = u'\x00a4';
3657 break;
3658 case 0x14:
3659 if( !m_bSpec ) // "Para End" char
3660 cInsert = u'\x00b5';
3661 //TODO: should this be U+00B6 PILCROW SIGN rather than
3662 // U+00B5 MICRO SIGN?
3663 break;
3664 case 0x15:
3665 if( !m_bSpec ) // Juristenparagraph
3666 {
3667 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3668 if (aItr == m_aTOXEndCps.end())
3669 cInsert = u'\x00a7';
3670 else
3671 m_aTOXEndCps.erase(aItr);
3672 }
3673 break;
3674 case 0x9:
3675 cInsert = '\x9'; // Tab
3676 break;
3677 case 0xb:
3678 cInsert = '\xa'; // Hard NewLine
3679 break;
3680 case 0xc:
3681 bParaMark = HandlePageBreakChar();
3682 break;
3683 case 0x1e: // Non-breaking hyphen
3685 break;
3686 case 0x1f: // Non-required hyphens
3688 break;
3689 case 0xa0: // Non-breaking spaces
3691 break;
3692 case 0x1:
3693 /*
3694 Current thinking is that if bObj is set then we have a
3695 straightforward "traditional" ole object, otherwise we have a
3696 graphic preview of an associated ole2 object (or a simple
3697 graphic of course)
3698
3699 normally in the canvas field, the code is 0x8 0x1.
3700 in a special case, the code is 0x1 0x1, which yields a simple picture
3701 */
3702 {
3703 bool bReadObj = IsInlineEscherHack();
3704 if( bReadObj )
3705 {
3706 sal_uInt64 nCurPos = m_pStrm->Tell();
3707 sal_uInt16 nWordCode(0);
3708
3709 if( m_bIsUnicode )
3710 m_pStrm->ReadUInt16( nWordCode );
3711 else
3712 {
3713 sal_uInt8 nByteCode(0);
3714 m_pStrm->ReadUChar( nByteCode );
3715 nWordCode = nByteCode;
3716 }
3717 if( nWordCode == 0x1 )
3718 bReadObj = false;
3719 m_pStrm->Seek( nCurPos );
3720 }
3721 if( !bReadObj )
3722 {
3723 SwFrameFormat *pResult = nullptr;
3724 if (m_bObj)
3725 pResult = ImportOle();
3726 else if (m_bSpec)
3727 {
3728 SwFrameFormat* pAsCharFlyFormat =
3730 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3731 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3732 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3733 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3734 }
3735
3736
3737 // If we have a bad 0x1 insert a space instead.
3738 if (!pResult)
3739 {
3740 cInsert = ' ';
3741 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3742 "WW8: Please report this document, it may have a "
3743 "missing graphic");
3744 }
3745 else
3746 {
3747 // reset the flags.
3748 m_bObj = m_bEmbeddObj = false;
3749 m_nObjLocFc = 0;
3750 }
3751 }
3752 }
3753 break;
3754 case 0x8:
3755 if( !m_bObj )
3756 Read_GrafLayer( nPosCp );
3757 break;
3758 case 0xd:
3759 bNewParaEnd = bParaMark = true;
3760 if (m_nInTable > 1)
3761 {
3762 /*
3763 #i9666#/#i23161#
3764 Yes complex, if there is an entry in the undocumented PLCF
3765 which I believe to be a record of cell and row boundaries
3766 see if the magic bit which I believe to mean cell end is
3767 set. I also think btw that the third byte of the 4 byte
3768 value is the level of the cell
3769 */
3770 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3771 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3772 pTest->Where() == nPosCp+1+nCpOfs)
3773 {
3774 WW8_FC nPos;
3775 void *pData;
3776 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3777 : 0;
3778 if (nData & 0x2) // Might be how it works
3779 {
3780 TabCellEnd();
3781 bParaMark = false;
3782 }
3783 }
3784 // tdf#106799: We expect TTP marks to be also cell marks,
3785 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3787 {
3788 TabCellEnd();
3789 bParaMark = false;
3790 }
3791 }
3792
3793 m_bWasTabCellEnd = false;
3794
3795 break; // line end
3796 case 0x5: // Annotation reference
3797 case 0x13:
3798 break;
3799 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3800 if (!m_aFootnoteStack.empty())
3801 cInsert = '?';
3802 break;
3803 default:
3804 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3805 break;
3806 }
3807
3808 if( '\x0' != cInsert )
3809 {
3810 OUString sInsert(cInsert);
3812 }
3813 if (!m_aApos.back()) // a para end in apo doesn't count
3814 m_bWasParaEnd = bNewParaEnd;
3815 return bParaMark;
3816}
3817
3819 bool* pStartAttr, bool bCallProcessSpecial)
3820{
3821 sal_uInt16 nOldColl = m_nCurrentColl;
3822 m_nCurrentColl = m_xPlcxMan->GetColl();
3823
3824 // Invalid Style-Id
3825 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3826 {
3827 m_nCurrentColl = 0;
3828 m_bParaAutoBefore = false;
3829 m_bParaAutoAfter = false;
3830 }
3831 else
3832 {
3833 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3834 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3835 }
3836
3837 if (nOldColl >= m_vColl.size())
3838 nOldColl = 0; // guess! TODO make sure this is what we want
3839
3840 bool bTabRowEnd = false;
3841 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3842 {
3843 bool bReSync;
3844 // Frame/Table/Autonumbering List Level
3845 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3846 if( bReSync )
3847 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3848 }
3849
3850 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3851 {
3853 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3854 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3855 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3856 }
3857}
3858
3859tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3860{
3861 tools::Long nSkipChars = 0;
3862 WW8PLCFManResult aRes;
3863
3864 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3865 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3866 aRes.nCurrentCp = rTextPos; // Current Cp position
3867
3868 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3869 if ( bNewSection ) // New Section
3870 {
3871 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3872 // Create PageDesc and fill it
3873 m_aSectionManager.CreateSep(rTextPos);
3874 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3875 // Create PageDesc and fill it
3876 m_bPgSecBreak = false;
3877 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3878 }
3879
3880 // New paragraph over Plcx.Fkp.papx
3881 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3882 {
3883 ProcessCurrentCollChange( aRes, &bStartAttr,
3885 !m_bIgnoreText );
3886 rbStartLine = false;
3887 }
3888
3889 // position of last CP that's to be ignored
3890 tools::Long nSkipPos = -1;
3891
3892 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3893 {
3894 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3895 {
3896 if( bStartAttr ) // WW attributes
3897 {
3898 if( aRes.nMemLen >= 0 )
3899 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3900 }
3901 else
3902 EndSprm( aRes.nSprmId ); // Switch off Attr
3903 }
3904 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3905 {
3906 if (bStartAttr)
3907 {
3908 nSkipChars = ImportExtSprm(&aRes);
3909 if (
3910 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3911 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3912 )
3913 {
3914 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3915 // Skip Field/Footnote-/End-Note here
3916 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3917 nSkipPos = rTextPos-1;
3918 }
3919 }
3920 else
3921 EndExtSprm( aRes.nSprmId );
3922 }
3923 }
3924
3925 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3926 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3927 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3928
3929 // Find next Attr position (and Skip attributes of field contents if needed)
3930 if (nSkipChars && !m_bIgnoreText)
3931 m_xCtrlStck->MarkAllAttrsOld();
3932 bool bOldIgnoreText = m_bIgnoreText;
3933 m_bIgnoreText = true;
3934 sal_uInt16 nOldColl = m_nCurrentColl;
3935 bool bDoPlcxManPlusPLus = true;
3936 tools::Long nNext;
3937 do
3938 {
3939 if( bDoPlcxManPlusPLus )
3940 m_xPlcxMan->advance();
3941 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3942
3944 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3945 {
3946 m_pPostProcessAttrsInfo->mbCopy = true;
3947 }
3948
3949 if( (0 <= nNext) && (nSkipPos >= nNext) )
3950 {
3951 if (nDepthGuard >= 1024)
3952 {
3953 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3954 nNext = nTextEnd;
3955 }
3956 else
3957 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3958 bDoPlcxManPlusPLus = false;
3959 m_bIgnoreText = true;
3960 }
3961
3963 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3964 {
3965 m_pPostProcessAttrsInfo->mbCopy = false;
3966 }
3967 }
3968 while( nSkipPos >= nNext );
3969 m_bIgnoreText = bOldIgnoreText;
3970 if( nSkipChars )
3971 {
3972 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
3973 if( nOldColl != m_xPlcxMan->GetColl() )
3974 ProcessCurrentCollChange(aRes, nullptr, false);
3975 }
3976
3977 return nNext;
3978}
3979
3980void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
3981{
3982 // Do we have attributes?
3983 if( rTextPos >= rNext )
3984 {
3985 do
3986 {
3987 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
3988 if (rTextPos == rNext && rTextPos >= nTextEnd)
3989 break;
3990 }
3991 while( rTextPos >= rNext );
3992
3993 }
3994 else if ( rbStartLine )
3995 {
3996 /* No attributes, but still a new line.
3997 * If a line ends with a line break and paragraph attributes or paragraph templates
3998 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
3999 * is false.
4000 * Due to this we need to set the template here as a kind of special treatment.
4001 */
4002 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4004 rbStartLine = false;
4005 }
4006}
4007
4014{
4015 // If there are any unclosed sprms then copy them to
4016 // another stack and close the ones that must be closed
4017 std::stack<sal_uInt16> aStack;
4018 m_xPlcxMan->TransferOpenSprms(aStack);
4019
4020 while (!aStack.empty())
4021 {
4022 sal_uInt16 nSprmId = aStack.top();
4023 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4024 EndSprm(nSprmId);
4025 aStack.pop();
4026 }
4027
4028 EndSpecial();
4029}
4030
4031bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4032{
4033 bool bJoined=false;
4034
4035 bool bStartLine = true;
4036 short nCrCount = 0;
4037 short nDistance = 0;
4038
4039 m_bWasParaEnd = false;
4040 m_nCurrentColl = 0;
4041 m_xCurrentItemSet.reset();
4042 m_nCharFormat = -1;
4043 m_bSpec = false;
4044 m_bPgSecBreak = false;
4045
4046 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4047 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4048
4049 WW8_CP nNext = m_xPlcxMan->Where();
4050 m_xPreviousNode.reset();
4051 sal_uInt8 nDropLines = 0;
4052 SwCharFormat* pNewSwCharFormat = nullptr;
4053 const SwCharFormat* pFormat = nullptr;
4054
4055 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4056 if (!bValidPos)
4057 return false;
4058
4059 WW8_CP l = nStartCp;
4060 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4061 if (nTextLen > nMaxPossible)
4062 {
4063 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4064 nTextLen = nMaxPossible;
4065 }
4066 WW8_CP nTextEnd = nStartCp+nTextLen;
4067 while (l < nTextEnd)
4068 {
4069 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4070 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4071
4072 if (m_pPostProcessAttrsInfo != nullptr)
4074
4075 if (l >= nTextEnd)
4076 break;
4077
4078 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4079
4080 // If the previous paragraph was a dropcap then do not
4081 // create a new txtnode and join the two paragraphs together
4082 if (bStartLine && !m_xPreviousNode) // Line end
4083 {
4084 bool bSplit = true;
4086 {
4088 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4089 bSplit = false;
4090 }
4092 {
4094 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4095 bSplit = false;
4096 }
4097 if (bSplit)
4098 {
4100 }
4101 }
4102
4103 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4104 {
4106 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4107 if (pEndNd)
4108 {
4109 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4110
4111 // Need to reset the font size and text position for the dropcap
4112 {
4113 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4114 m_xCtrlStck->Delete(aTmp);
4115 }
4116
4117 // Get the default document dropcap which we can use as our template
4118 const SwFormatDrop* defaultDrop =
4119 static_cast<const SwFormatDrop*>( GetFormatAttr(RES_PARATR_DROP));
4120 SwFormatDrop aDrop(*defaultDrop);
4121
4122 aDrop.GetLines() = nDropLines;
4123 aDrop.GetDistance() = nDistance;
4124 aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
4125 // Word has no concept of a "whole word dropcap"
4126 aDrop.GetWholeWord() = false;
4127
4128 if (pFormat)
4129 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4130 else if(pNewSwCharFormat)
4131 aDrop.SetCharFormat(pNewSwCharFormat);
4132
4133 SwPosition aStart(*pEndNd);
4134 m_xCtrlStck->NewAttr(aStart, aDrop);
4136 }
4137 m_xPreviousNode.reset();
4138 }
4139 else if (m_bDropCap)
4140 {
4141 // If we have found a dropcap store the textnode
4143
4144 SprmResult aDCS;
4145 if (m_bVer67)
4146 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4147 else
4148 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4149
4150 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4151 nDropLines = (*aDCS.pSprm) >> 3;
4152 else // There is no Drop Cap Specifier hence no dropcap
4153 m_xPreviousNode.reset();
4154
4155 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4156 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4157 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4158 else
4159 nDistance = 0;
4160
4161 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4162
4164 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4165
4166 if (pSwFormatCharFormat)
4167 pFormat = pSwFormatCharFormat->GetCharFormat();
4168
4169 if (m_xCurrentItemSet && !pFormat)
4170 {
4171 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4172 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4174 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4175 }
4176
4177 m_xCurrentItemSet.reset();
4178 m_bDropCap=false;
4179 }
4180
4181 if (bStartLine || m_bWasTabRowEnd)
4182 {
4183 // Call all 64 CRs; not for Header and the like
4184 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4185 {
4186 if (nTextLen < WW8_CP_MAX/100)
4187 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4188 else
4189 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4190 m_xProgress->Update(m_nProgress); // Update
4191 }
4192 }
4193
4194 // If we have encountered a 0x0c which indicates either section of
4195 // pagebreak then look it up to see if it is a section break, and
4196 // if it is not then insert a page break. If it is a section break
4197 // it will be handled as such in the ReadAttrs of the next loop
4198 if (m_bPgSecBreak)
4199 {
4200 // We need only to see if a section is ending at this cp,
4201 // the plcf will already be sitting on the correct location
4202 // if it is there.
4203 WW8PLCFxDesc aTemp;
4204 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4205 if (m_xPlcxMan->GetSepPLCF())
4206 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4207 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4208 {
4209 // #i39251# - insert text node for page break, if no one inserted.
4210 // #i43118# - refine condition: the anchor
4211 // control stack has to have entries, otherwise it's not needed
4212 // to insert a text node.
4213 if (!bStartLine && !m_xAnchorStck->empty())
4214 {
4216 }
4218 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4219 m_bFirstParaOfPage = true;
4220 m_bPgSecBreak = false;
4221 }
4222 }
4223 }
4224
4225 m_xPreviousNode.reset();
4226
4229
4230 if (!m_bInHyperlink)
4231 bJoined = JoinNode(*m_pPaM);
4232
4233 CloseAttrEnds();
4234
4235 m_xPlcxMan.reset();
4236 return bJoined;
4237}
4238
4240 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4241 : m_pDocShell(rD.GetDocShell())
4242 , m_pStg(pStorage)
4243 , m_pStrm(pSt)
4244 , m_pTableStream(nullptr)
4245 , m_pDataStream(nullptr)
4246 , m_rDoc(rD)
4247 , m_pPaM(nullptr)
4248 , m_aSectionManager(*this)
4249 , m_aExtraneousParas(rD)
4250 , m_aInsertedTables(rD)
4251 , m_aSectionNameGenerator(rD, "WW")
4252 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4253 , m_aParaStyleMapper(rD)
4254 , m_aCharStyleMapper(rD)
4255 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4256 , m_pPreviousNumPaM(nullptr)
4257 , m_pPrevNumRule(nullptr)
4258 , m_pCurrentColl(nullptr)
4259 , m_pDfltTextFormatColl(nullptr)
4260 , m_pStandardFormatColl(nullptr)
4261 , m_pDrawModel(nullptr)
4262 , m_pDrawPg(nullptr)
4263 , m_pNumFieldType(nullptr)
4264 , m_sBaseURL(std::move(aBaseURL))
4265 , m_nIniFlags(0)
4266 , m_nIniFlags1(0)
4267 , m_nFieldFlags(0)
4268 , m_bRegardHindiDigits( false )
4269 , m_bDrawCpOValid( false )
4270 , m_nDrawCpO(0)
4271 , m_nPicLocFc(0)
4272 , m_nObjLocFc(0)
4273 , m_nIniFlyDx(0)
4274 , m_nIniFlyDy(0)
4275 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4276 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4277 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4278 , m_nProgress(0)
4279 , m_nCurrentColl(0)
4280 , m_nFieldNum(0)
4281 , m_nLFOPosition(USHRT_MAX)
4282 , m_nCharFormat(0)
4283 , m_nDrawXOfs(0)
4284 , m_nDrawYOfs(0)
4285 , m_nDrawXOfs2(0)
4286 , m_nDrawYOfs2(0)
4287 , m_cSymbol(0)
4288 , m_nWantedVersion(nVersionPara)
4289 , m_nSwNumLevel(0xff)
4290 , m_nWwNumType(0xff)
4291 , m_pChosenWW8OutlineStyle(nullptr)
4292 , m_nListLevel(MAXLEVEL)
4293 , m_bNewDoc(bNewDoc)
4294 , m_bSkipImages(bSkipImages)
4295 , m_bReadNoTable(false)
4296 , m_bPgSecBreak(false)
4297 , m_bSpec(false)
4298 , m_bObj(false)
4299 , m_bTxbxFlySection(false)
4300 , m_bHasBorder(false)
4301 , m_bSymbol(false)
4302 , m_bIgnoreText(false)
4303 , m_nInTable(0)
4304 , m_bWasTabRowEnd(false)
4305 , m_bWasTabCellEnd(false)
4306 , m_bAnl(false)
4307 , m_bHdFtFootnoteEdn(false)
4308 , m_bFootnoteEdn(false)
4309 , m_bIsHeader(false)
4310 , m_bIsFooter(false)
4311 , m_bIsUnicode(false)
4312 , m_bCpxStyle(false)
4313 , m_bStyNormal(false)
4314 , m_bWWBugNormal(false)
4315 , m_bNoAttrImport(false)
4316 , m_bInHyperlink(false)
4317 , m_bWasParaEnd(false)
4318 , m_bVer67(false)
4319 , m_bVer6(false)
4320 , m_bVer7(false)
4321 , m_bVer8(false)
4322 , m_bEmbeddObj(false)
4323 , m_bCurrentAND_fNumberAcross(false)
4324 , m_bNoLnNumYet(true)
4325 , m_bFirstPara(true)
4326 , m_bFirstParaOfPage(false)
4327 , m_bParaAutoBefore(false)
4328 , m_bParaAutoAfter(false)
4329 , m_bDropCap(false)
4330 , m_nDropCap(0)
4331 , m_bBidi(false)
4332 , m_bReadTable(false)
4333 , m_bLoadingTOXCache(false)
4334 , m_nEmbeddedTOXLevel(0)
4335 , m_bLoadingTOXHyperlink(false)
4336 , m_bCareFirstParaEndInToc(false)
4337 , m_bCareLastParaEndInToc(false)
4338 , m_bNotifyMacroEventRead(false)
4339 , m_bFuzzing(utl::ConfigManager::IsFuzzing())
4340{
4341 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4342 m_aApos.push_back(false);
4343
4345}
4346
4348{
4349}
4350
4351void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4352{
4353 if( pStck )
4354 {
4355 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4356 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4357 }
4358 else
4359 {
4360 OSL_ENSURE( false, "WW stack already deleted" );
4361 }
4362}
4363
4365 bool bIgnoreCols)
4366{
4367 SwPageDesc &rPage = *rSection.mpPage;
4368
4369 SetNumberingType(rSection, rPage);
4370
4371 SwFrameFormat &rFormat = rPage.GetMaster();
4372
4373 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4375
4376 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4377 {
4378 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4381 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4382 {
4383 // Only handle shape if it is a background shape
4384 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4385 {
4387 aSet(rFormat.GetDoc()->GetAttrPool());
4390 if ( aSet.HasItem(RES_BACKGROUND) )
4391 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4392 else
4393 rFormat.SetFormatAttr(aSet);
4394 }
4395 }
4396 }
4397 wwULSpaceData aULData;
4398 GetPageULData(rSection, aULData);
4399 SetPageULSpaceItems(rFormat, aULData, rSection);
4400
4402
4403 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4404
4405 if (!(rSection.maSep.pgbApplyTo & 1))
4406 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4407 if (!(rSection.maSep.pgbApplyTo & 2))
4409
4410 mrReader.SetDocumentGrid(rFormat, rSection);
4411}
4412
4414{
4415 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4416 mrReader.m_xWDop->doptypography.m_f2on1;
4417
4418 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4419 UseOnPage eUse = eUseBase;
4420 if (!mrReader.m_xWDop->fFacingPages)
4422 if (!rSection.HasTitlePage())
4423 eUse |= UseOnPage::FirstShare;
4424
4425 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4426 if (rSection.mpPage)
4427 rSection.mpPage->WriteUseOn(eUse);
4428}
4429
4434static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4435 SwDoc &rDoc)
4436{
4437 /*
4438 If it's a table here, apply the pagebreak to the table
4439 properties, otherwise we add it to the para at this
4440 position
4441 */
4442 if (rIdx.GetNode().IsTableNode())
4443 {
4444 SwTable& rTable =
4445 rIdx.GetNode().GetTableNode()->GetTable();
4446 SwFrameFormat* pApply = rTable.GetFrameFormat();
4447 OSL_ENSURE(pApply, "impossible");
4448 if (pApply)
4449 pApply->SetFormatAttr(rPgDesc);
4450 }
4451 else
4452 {
4453 SwPaM aPage(rIdx);
4454 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4455 }
4456}
4457
4462 mySegIter const &rStart, bool bIgnoreCols)
4463{
4464 if (mrReader.m_bNewDoc && rIter == rStart)
4465 {
4466 rIter->mpPage =
4468 }
4469 else
4470 {
4471 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4473 nullptr, false);
4474 }
4475 OSL_ENSURE(rIter->mpPage, "no page!");
4476 if (!rIter->mpPage)
4477 return SwFormatPageDesc();
4478
4479 // Set page before hd/ft
4480 const wwSection *pPrevious = nullptr;
4481 if (rIter != rStart)
4482 pPrevious = &(*(rIter-1));
4483 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4484 SetUseOn(*rIter);
4485
4486 // Set hd/ft after set page
4487 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4488
4489 SwFormatPageDesc aRet(rIter->mpPage);
4490
4491 rIter->mpPage->SetFollow(rIter->mpPage);
4492
4493 if (rIter->PageRestartNo())
4494 aRet.SetNumOffset(rIter->PageStartAt());
4495
4496 ++mnDesc;
4497 return aRet;
4498}
4499
4501{
4502 mySegIter aEnd = maSegments.end();
4503 mySegIter aStart = maSegments.begin();
4504 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4505 {
4506 // If the section is of type "New column" (0x01), then simply insert a column break.
4507 // But only if there actually are columns on the page, otherwise a column break
4508 // seems to be handled like a page break by MSO.
4509 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4510 {
4511 SwPaM start( aIter->maStart );
4513 continue;
4514 }
4515
4516 mySegIter aNext = aIter+1;
4517 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4518
4519 // If two following sections are different in following properties, Word will interpret a continuous
4520 // section break between them as if it was a section break next page.
4521 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4522 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4523
4524 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4525 bool bInsertPageDesc = !bInsertSection;
4526 // HACK Force new pagedesc if left/right margins change, otherwise e.g. floating tables may be anchored improperly.
4527 if( aIter->maSep.dxaLeft != aPrev->maSep.dxaLeft || aIter->maSep.dxaRight != aPrev->maSep.dxaRight )
4528 bInsertPageDesc = true;
4529 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4530
4531 if (bInsertPageDesc)
4532 {
4533 /*
4534 If a cont section follows this section then we won't be
4535 creating a page desc with 2+ cols as we cannot host a one
4536 col section in a 2+ col pagedesc and make it look like
4537 word. But if the current section actually has columns then
4538 we are forced to insert a section here as well as a page
4539 descriptor.
4540 */
4541
4542 bool bIgnoreCols = bInsertSection;
4543 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4544 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4545 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4546 (aIter->IsLandScape() == aNext->IsLandScape()));
4547
4548 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4549 {
4550 bIgnoreCols = true;
4551 if ((aIter->NoCols() > 1) || bProtected)
4552 bInsertSection = true;
4553 }
4554
4555 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4556 if (!aDesc.GetPageDesc())
4557 continue;
4558
4559 // special case handling for odd/even section break
4560 // a) as before create a new page style for the section break
4561 // b) set Layout of generated page style to right/left ( according
4562 // to section break odd/even )
4563 // c) create a new style to follow the break page style
4564 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4565 {
4566 // SetSwFormatPageDesc calls some methods that could
4567 // modify aIter (e.g. wwSection ).
4568 // Since we call SetSwFormatPageDesc below to generate the
4569 // 'Following' style of the Break style, it is safer
4570 // to take a copy of the contents of aIter.
4571 wwSection aTmpSection = *aIter;
4572 // create a new following page style
4573 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4574 // restore any contents of aIter trashed by SetSwFormatPageDesc
4575 *aIter = aTmpSection;
4576
4577 // Handle the section break
4578 UseOnPage eUseOnPage = UseOnPage::Left;
4579 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4580 eUseOnPage = UseOnPage::Right;
4581
4582 // Keep the share flags.
4583 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4584 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4585 }
4586
4587 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4588 // unless it is needed to specify a page number.
4589 if (aIter != aStart || aDesc.GetNumOffset())
4590 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4591 }
4592
4593 SwTextNode* pTextNd = nullptr;
4594 if (bInsertSection)
4595 {
4596 // Start getting the bounds of this section
4597 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4598 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4599 if (aNext != aEnd)
4600 {
4601 aAnchor = aNext->maStart;
4602 aSectPaM.GetPoint()->Assign(aAnchor);
4603 aSectPaM.Move(fnMoveBackward);
4604 }
4605
4606 const SwPosition* pPos = aSectPaM.GetPoint();
4607 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4608 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4609 if (pTableNd)
4610 {
4611 pTextNd =
4614
4615 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4616 }
4617
4618 aSectPaM.SetMark();
4619
4620 aSectPaM.GetPoint()->Assign(aIter->maStart);
4621
4622 bool bHasOwnHdFt = false;
4623 /*
4624 In this nightmare scenario the continuous section has its own
4625 headers and footers so we will try and find a hard page break
4626 between here and the end of the section and put the headers and
4627 footers there.
4628 */
4629 if (!bInsertPageDesc)
4630 {
4631 bHasOwnHdFt =
4633 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4634 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4635 );
4636 }
4637 if (bHasOwnHdFt)
4638 {
4639 // #i40766# Need to cache the page descriptor in case there is
4640 // no page break in the section
4641 SwPageDesc *pOrig = aIter->mpPage;
4642 bool bFailed = true;
4643 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4644 if (aDesc.GetPageDesc())
4645 {
4646 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4647 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4648 for(; nStart <= nEnd; ++nStart)
4649 {
4650 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4651 if (!pNode)
4652 continue;
4653 if (sw::util::HasPageBreak(*pNode))
4654 {
4655 SwNodeIndex aIdx(*pNode);
4656 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4657 bFailed = false;
4658 break;
4659 }
4660 }
4661 }
4662 if(bFailed)
4663 {
4664 aIter->mpPage = pOrig;
4665 }
4666 }
4667
4668 // End getting the bounds of this section, quite a job eh?
4669 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4670 // The last section if continuous is always unbalanced
4671 if (pRet)
4672 {
4673 // Set the columns to be UnBalanced if that compatibility option is set
4674 if (mrReader.m_xWDop->fNoColumnBalance)
4676 else
4677 {
4678 // Otherwise set to unbalanced if the following section is
4679 // not continuous, (which also means that the last section
4680 // is unbalanced)
4681 if (aNext == aEnd || !aNext->IsContinuous())
4683 }
4684 }
4685 }
4686
4687 if (pTextNd)
4688 {
4689 SwPaM aTest(*pTextNd);
4691 pTextNd = nullptr;
4692 }
4693 }
4694}
4695
4697{
4698 auto aEnd = m_aTextNodes.rend();
4699 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4700 {
4701 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4702 SwTextNode* pTextNode = rListener.GetTextNode();
4703 rListener.StopListening(pTextNode);
4704
4705 SwPaM aTest(*pTextNode);
4707 }
4708 m_aTextNodes.clear();
4709}
4710
4712{
4713 m_aTextNodes.emplace(pTextNode, this);
4714}
4715
4717{
4718 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4719 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4720 if (it == m_aTextNodes.end())
4721 return;
4722 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4723 m_aTextNodes.erase(it);
4724}
4725
4727 : m_pTextNode(pTextNode)
4728{
4729 m_pTextNode->Add(this);
4730}
4731
4733{
4734 if (!m_pTextNode)
4735 return;
4737}
4738
4739void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4740{
4741 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4742 return;
4743 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4744 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4745 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4746 // indicates an underlying bug.
4747 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4748 removed(const_cast<SwModify*>(&rModify));
4749}
4750
4752{
4753 pTextNode->Remove(this);
4754 m_pTextNode = nullptr;
4755}
4756
4758{
4759 StopListening(pTextNode);
4760}
4761
4763{
4764 m_pOwner->remove_if_present(pTextNode);
4765}
4766
4768{
4769 if (!m_xWwFib->m_lcbCmds)
4770 return;
4771
4772 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4773 if (!bValidPos)
4774 return;
4775
4776 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4777
4778 if (!xRoot.is())
4779 return;
4780
4781 try
4782 {
4783 uno::Reference < io::XStream > xStream =
4784 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4785 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4786
4787 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4788 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4789 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4790 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4791 }
4792 catch (...)
4793 {
4794 }
4795}
4796
4798{
4799 std::vector<OUString> aDocVarStrings;
4800 std::vector<ww::bytes> aDocVarStringIds;
4801 std::vector<OUString> aDocValueStrings;
4802 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4803 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4804 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4805 if (m_bVer67) return;
4806
4807 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4808 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4809 uno::Reference<document::XDocumentProperties> xDocProps(
4810 xDPS->getDocumentProperties());
4811 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4812 uno::Reference<beans::XPropertyContainer> xUserDefinedProps =
4813 xDocProps->getUserDefinedProperties();
4814 OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null");
4815
4816 for(size_t i=0; i<aDocVarStrings.size(); i++)
4817 {
4818 const OUString &rName = aDocVarStrings[i];
4819 uno::Any aValue;
4820 aValue <<= rName;
4821 try {
4822 xUserDefinedProps->addProperty( rName,
4823 beans::PropertyAttribute::REMOVABLE,
4824 aValue );
4825 } catch (const uno::Exception &) {
4826 // ignore
4827 }
4828 }
4829}
4830
4835{
4836 if( !m_pStg )
4837 return;
4838
4839 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4840 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4841 uno::Reference<document::XDocumentProperties> xDocProps(
4842 xDPS->getDocumentProperties());
4843 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4844
4845 if (!xDocProps.is())
4846 return;
4847
4848 if ( m_xWwFib->m_fDot )
4849 {
4850 SfxMedium* pMedium = m_pDocShell->GetMedium();
4851 if ( pMedium )
4852 {
4853 const OUString& aName = pMedium->GetName();
4855 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4856 if ( !sTemplateURL.isEmpty() )
4857 xDocProps->setTemplateURL( sTemplateURL );
4858 }
4859 }
4860 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4861 {
4862 auto nCur = m_pTableStream->Tell();
4863 Sttb aSttb;
4864 // point at tgc record
4865 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4866 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4867 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4868 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4869 OUString aURL;
4870 // attempt to convert to url (won't work for obvious reasons on linux)
4871 if ( !sPath.isEmpty() )
4872 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4873 if (aURL.isEmpty())
4874 xDocProps->setTemplateURL( aURL );
4875 else
4876 xDocProps->setTemplateURL( sPath );
4877
4878 }
4879 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4880}
4881
4882static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4883{
4884 if ( !xPrjNameCache.is() )
4885 return;
4886
4887 INetURLObject aObj;
4888 aObj.SetURL( sTemplatePathOrURL );
4889 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4890 OUString aURL;
4891 if ( bIsURL )
4892 aURL = sTemplatePathOrURL;
4893 else
4894 {
4895 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4896 aObj.SetURL( aURL );
4897 }
4898 try
4899 {
4900 OUString templateNameWithExt = aObj.GetLastName();
4901 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4902 if ( nIndex != -1 )
4903 {
4904 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4905 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4906 }
4907 }
4908 catch( const uno::Exception& )
4909 {
4910 }
4911}
4912
4913namespace {
4914
4915class WW8Customizations
4916{
4917 SvStream* mpTableStream;
4918 WW8Fib mWw8Fib;
4919public:
4920 WW8Customizations( SvStream*, WW8Fib const & );
4921 void Import( SwDocShell* pShell );
4922};
4923
4924}
4925
4926WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4927{
4928}
4929
4930void WW8Customizations::Import( SwDocShell* pShell )
4931{
4932 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4933 return;
4934 try
4935 {
4936 Tcg aTCG;
4937 sal_uInt64 nCur = mpTableStream->Tell();
4938 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4939 {
4940 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4941 return;
4942 }
4943 bool bReadResult = aTCG.Read( *mpTableStream );
4944 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4945 if ( !bReadResult )
4946 {
4947 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4948 return;
4949 }
4950 aTCG.ImportCustomToolBar( *pShell );
4951 }
4952 catch(...)
4953 {
4954 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4955 }
4956}
4957
4958void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4959{
4960 if (m_bFuzzing)
4961 return;
4962
4963 SvtPathOptions aPathOpt;
4964 const OUString& aAddinPath = aPathOpt.GetAddinPath();
4965 uno::Sequence< OUString > sGlobalTemplates;
4966
4967 // first get the autoload addins in the directory STARTUP
4968 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
4969
4970 if( xSFA->isFolder( aAddinPath ) )
4971 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
4972
4973 for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) )
4974 {
4975 INetURLObject aObj;
4976 aObj.SetURL( rGlobalTemplate );
4977 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4978 OUString aURL;
4979 if ( bIsURL )
4980 aURL = rGlobalTemplate;
4981 else
4982 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
4983 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
4984 continue; // don't try and read the same document as ourselves
4985
4986 tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE );
4987
4988 BasicProjImportHelper aBasicImporter( *m_pDocShell );
4989 // Import vba via oox filter
4990 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
4991 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
4992 // Read toolbars & menus
4993 tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument");
4994 refMainStream->SetEndian(SvStreamEndian::LITTLE);
4995 WW8Fib aWwFib( *refMainStream, 8 );
4996 tools::SvRef<SotStorageStream> xTableStream =
4997 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? OUString(SL::a1Table) : OUString(SL::a0Table), StreamMode::STD_READ);
4998
4999 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5000 {
5001 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5002 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5003 aGblCustomisations.Import( m_pDocShell );
5004 }
5005 }
5006}
5007
5009{
5011 if (m_bNewDoc && m_pStg && !pGloss)
5012 {
5013 // Initialize RDF metadata, to be able to add statements during the import.
5014 try
5015 {
5016 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5017 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
5018 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
5019 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5020 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
5021 uno::Reference<task::XInteractionHandler> xHandler;
5022 xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5023 }
5024 catch (const uno::Exception&)
5025 {
5026 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5027 }
5028 ReadDocInfo();
5029 }
5030
5031 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5032
5033 if (m_xWwFib->m_fReadOnlyRecommended)
5034 pFibData->setReadOnlyRecommended(true);
5035 else
5036 pFibData->setReadOnlyRecommended(false);
5037
5038 if (m_xWwFib->m_fWriteReservation)
5039 pFibData->setWriteReservation(true);
5040 else
5041 pFibData->setWriteReservation(false);
5042
5043 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5044
5046 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5047
5048 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5049
5050 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5051 {
5052 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5053 if (pDocShell)
5054 pDocShell->SetReadOnlyUI();
5055 }
5056
5057 m_pPaM = mpCursor.get();
5058
5060
5062
5063 /*
5064 RefFieldStck: Keeps track of bookmarks which may be inserted as
5065 variables instead.
5066 */
5069
5070 m_xAnchorStck.reset(