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 }
204}
205
206namespace
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 = dynamic_cast< SdrTextObj* >(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 tools::Long &rFirstLinePos)
1231{
1233 "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1234
1235 const tools::Long nBodyIndent = rLR.GetTextLeft();
1236 const tools::Long nFirstLineDiff = rLR.GetTextFirstLineOffset();
1237 rFirstLinePos = nBodyIndent + nFirstLineDiff;
1238
1239 const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
1240 const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
1241 tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
1242
1243 return std::max<tools::Long>(nExtraListIndent, 0);
1244}
1245
1246// #i103711#
1247// #i105414#
1249 const SwNumFormat &rFormat,
1250 const bool bFirstLineOfstSet,
1251 const bool bLeftIndentSet )
1252{
1254 {
1255 tools::Long nWantedFirstLinePos;
1256 tools::Long nExtraListIndent = lcl_GetTrueMargin(rLR, rFormat, nWantedFirstLinePos);
1257 rLR.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
1259 }
1261 {
1262 if ( !bFirstLineOfstSet && bLeftIndentSet &&
1263 rFormat.GetFirstLineIndent() != 0 )
1264 {
1266 }
1267 else if ( bFirstLineOfstSet && !bLeftIndentSet &&
1268 rFormat.GetIndentAt() != 0 )
1269 {
1270 rLR.SetTextLeft( rFormat.GetIndentAt() );
1271 }
1272 else if (!bFirstLineOfstSet && !bLeftIndentSet )
1273 {
1274 if ( rFormat.GetFirstLineIndent() != 0 )
1275 {
1277 }
1278 if ( rFormat.GetIndentAt() != 0 )
1279 {
1280 rLR.SetTextLeft( rFormat.GetIndentAt() );
1281 }
1282 }
1283 }
1284}
1285
1287 const SwTextNode &rTextNode)
1288{
1289 const SwNumFormat *pRet = nullptr;
1290 const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
1291 if (pItem && rTextNode.GetNumRule())
1292 {
1293 if (rTextNode.IsCountedInList())
1294 {
1295 OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue());
1296 const SwNumRule *pRule = m_rDoc.FindNumRulePtr(sName);
1297 if (pRule)
1298 pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel());
1299 }
1300 }
1301 return pRet;
1302}
1303
1305 SwFltStackEntry& rEntry )
1306{
1307 switch( rEntry.m_pAttr->Which() )
1308 {
1309 case RES_FLTR_BOOKMARK:
1310 {
1311 // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content
1312 // and which is not referenced.
1313 bool bInsertBookmarkIntoDoc = true;
1314
1315 SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
1316 if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
1317 {
1318 const OUString& rName = pFltBookmark->GetName();
1319 std::set< OUString, SwWW8::ltstr >::const_iterator aResult = m_aReferencedTOCBookmarks.find(rName);
1320 if ( aResult == m_aReferencedTOCBookmarks.end() )
1321 {
1322 bInsertBookmarkIntoDoc = false;
1323 }
1324 }
1325 if ( bInsertBookmarkIntoDoc )
1326 {
1327 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1328 }
1329 break;
1330 }
1331 default:
1332 SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
1333 break;
1334 }
1335
1336}
1337
1339 SwFltStackEntry& rEntry)
1340{
1341 switch (rEntry.m_pAttr->Which())
1342 {
1343 case RES_LR_SPACE:
1344 {
1345 /*
1346 Loop over the affected nodes and
1347 a) convert the word style absolute indent to indent relative
1348 to any numbering indent active on the nodes
1349 b) adjust the writer style tabstops relative to the old
1350 paragraph indent to be relative to the new paragraph indent
1351 */
1352 SwPaM aRegion(rTmpPos);
1354 {
1355 SvxLRSpaceItem aNewLR( *static_cast<SvxLRSpaceItem*>(rEntry.m_pAttr.get()) );
1356 SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
1357 SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
1358 for(; nStart <= nEnd; ++nStart)
1359 {
1360 SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
1361 if (!pNode || !pNode->IsTextNode())
1362 continue;
1363
1364 SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
1365 SvxLRSpaceItem aOldLR = pNd->GetAttr(RES_LR_SPACE);
1366
1367 SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
1368
1369 const SwNumFormat* pNum
1370 = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
1371 if (!pNum)
1372 {
1373 pNum = GetNumFormatFromTextNode(*pTextNode);
1374 }
1375
1376 if ( pNum )
1377 {
1378 // #i103711#
1379 const bool bFirstLineIndentSet =
1382 // #i105414#
1383 const bool bLeftIndentSet =
1386 SyncIndentWithList( aNewLR, *pNum,
1387 bFirstLineIndentSet,
1388 bLeftIndentSet );
1389 }
1390
1391 if (aNewLR == aOldLR)
1392 continue;
1393
1394 pNd->SetAttr(aNewLR);
1395
1396 }
1397 }
1398 }
1399 break;
1400
1401 case RES_TXTATR_FIELD:
1402 OSL_ENSURE(false, "What is a field doing in the control stack,"
1403 "probably should have been in the endstack");
1404 break;
1405
1407 OSL_ENSURE(false, "What is an annotation doing in the control stack,"
1408 "probably should have been in the endstack");
1409 break;
1410
1412 OSL_ENSURE(false, "What is an input field doing in the control stack,"
1413 "probably should have been in the endstack");
1414 break;
1415
1416 case RES_TXTATR_INETFMT:
1417 {
1418 SwPaM aRegion(rTmpPos);
1420 {
1421 SwFrameFormat *pFrame;
1422 // If we have just one single inline graphic then
1423 // don't insert a field for the single frame, set
1424 // the frames hyperlink field attribute directly.
1426 if (nullptr != pFrame)
1427 {
1428 const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
1429 rEntry.m_pAttr.get());
1431 aURL.SetURL(pAttr->GetValue(), false);
1432 aURL.SetTargetFrameName(pAttr->GetTargetFrame());
1433 pFrame->SetFormatAttr(aURL);
1434 }
1435 else
1436 {
1438 }
1439 }
1440 }
1441 break;
1442 default:
1443 SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
1444 break;
1445 }
1446}
1447
1449 sal_uInt16 nWhich)
1450{
1451 const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
1452 if (!pItem)
1453 {
1454 SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
1455 if (!pNd)
1456 pItem = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
1457 else
1458 {
1459 /*
1460 If we're hunting for the indent on a paragraph and need to use the
1461 parent style indent, then return the indent in msword format, and
1462 not writer format, because that's the style that the filter works
1463 in (naturally)
1464 */
1465 if (nWhich == RES_LR_SPACE)
1466 {
1467 SfxItemState eState = SfxItemState::DEFAULT;
1468 if (const SfxItemSet *pSet = pNd->GetpSwAttrSet())
1469 eState = pSet->GetItemState(RES_LR_SPACE, false);
1470 if (eState != SfxItemState::SET && m_rReader.m_nCurrentColl < m_rReader.m_vColl.size())
1471 pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].maWordLR.get();
1472 }
1473
1474 /*
1475 If we're hunting for a character property, try and exact position
1476 within the text node for lookup
1477 */
1478 if (pNd->IsTextNode())
1479 {
1480 const sal_Int32 nPos = rPos.GetContentIndex();
1481 m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
1483 pItem = m_xScratchSet->GetItem(nWhich);
1484 }
1485
1486 if (!pItem)
1487 pItem = &pNd->GetAttr(nWhich);
1488 }
1489 }
1490 return pItem;
1491}
1492
1494 sal_uInt16 nWhich)
1495{
1496 SwFltPosition aFltPos(rPos);
1497
1498 size_t nSize = size();
1499 while (nSize)
1500 {
1501 const SwFltStackEntry& rEntry = (*this)[ --nSize ];
1502 if (rEntry.m_pAttr->Which() == nWhich)
1503 {
1504 if ( (rEntry.m_bOpen) ||
1505 (
1506 (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) &&
1507 (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) &&
1508 (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) &&
1509 (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent)
1510 )
1511 )
1512 /*
1513 * e.g. half-open range [0-3) so asking for properties at 3
1514 * means props that end at 3 are not included
1515 */
1516 {
1517 return rEntry.m_pAttr.get();
1518 }
1519 }
1520 }
1521 return nullptr;
1522}
1523
1525 const SwFormatField& rFormatField,
1526 sal_uInt16& rBkmNo)
1527{
1528 const SwField* pField = rFormatField.GetField();
1529 sal_uInt16 nSubType;
1530 if(pField && (SwFieldIds::GetRef == pField->Which())
1531 && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType))
1532 && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty())
1533 {
1534 const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
1536 pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
1537 if(ppBkmk != pMarkAccess->getAllMarksEnd())
1538 {
1539 // find Sequence No of corresponding Foot-/Endnote
1540 rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
1541 return true;
1542 }
1543 }
1544 return false;
1545}
1546
1548 SwFltStackEntry& rEntry)
1549{
1550 switch (rEntry.m_pAttr->Which())
1551 {
1552 /*
1553 Look up these in our lists of bookmarks that were changed to
1554 variables, and replace the ref field with a var field, otherwise
1555 do normal (?) strange stuff
1556 */
1557 case RES_TXTATR_FIELD:
1560 {
1561 SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
1562
1563 SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
1564 SwField* pField = rFormatField.GetField();
1565
1566 if (!RefToVar(pField, rEntry))
1567 {
1568 sal_uInt16 nBkmNo;
1569 if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
1570 {
1571 ::sw::mark::IMark const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
1572
1573 const SwPosition& rBkMrkPos = pMark->GetMarkPos();
1574
1575 SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
1576 if( pText && rBkMrkPos.GetContentIndex() )
1577 {
1578 SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
1579 rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
1580 if( pFootnote )
1581 {
1582 sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
1583
1584 static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
1585
1586 if( pFootnote->GetFootnote().IsEndNote() )
1587 static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
1588 }
1589 }
1590 }
1591 }
1592
1594 MoveAttrs(*aPaM.GetPoint());
1595 }
1596 break;
1597 case RES_FLTR_TOX:
1598 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1599 break;
1600 default:
1601 case RES_FLTR_BOOKMARK:
1602 OSL_ENSURE(false, "EndStck used with non field, not what we want");
1603 SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
1604 break;
1605 }
1606}
1607
1608/*
1609 For styles we will do our tabstop arithmetic in word style and adjust them to
1610 writer style after all the styles have been finished and the dust settles as
1611 to what affects what.
1612
1613 For explicit attributes we turn the adjusted writer tabstops back into 0 based
1614 word indexes and we'll turn them back into writer indexes when setting them
1615 into the document. If explicit left indent exist which affects them, then this
1616 is handled when the explicit left indent is set into the document
1617*/
1618void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
1619{
1620 if (nLen < 0)
1621 {
1623 return;
1624 }
1625
1626 sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
1627 const sal_uInt8* pDel = pData + 1; // Del - Array
1628
1629 sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
1630 const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
1631
1632 short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
1633 if (nRequiredLength > nLen)
1634 {
1635 // would require more data than available to describe!
1636 // discard invalid record
1637 nIns = 0;
1638 nDel = 0;
1639 }
1640
1641 WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
1642
1643 std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
1644
1645 const SwFormat * pSty = nullptr;
1646 sal_uInt16 nTabBase;
1647 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
1648 {
1649 nTabBase = m_vColl[m_nCurrentColl].m_nBase;
1650 if (nTabBase < m_vColl.size()) // Based On
1651 pSty = m_vColl[nTabBase].m_pFormat;
1652 }
1653 else
1654 { // Text
1655 nTabBase = m_nCurrentColl;
1656 if (m_nCurrentColl < m_vColl.size())
1657 pSty = m_vColl[m_nCurrentColl].m_pFormat;
1658 //TODO: figure out else here
1659 }
1660
1661 bool bFound = false;
1662 std::unordered_set<size_t> aLoopWatch;
1663 while (pSty && !bFound)
1664 {
1665 const SvxTabStopItem* pTabs;
1666 bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
1667 &pTabs) == SfxItemState::SET;
1668 if( bFound )
1669 {
1670 aAttr.reset(pTabs->Clone());
1671 }
1672 else
1673 {
1674 sal_uInt16 nOldTabBase = nTabBase;
1675 // If based on another
1676 if (nTabBase < m_vColl.size())
1677 nTabBase = m_vColl[nTabBase].m_nBase;
1678
1679 if (
1680 nTabBase < m_vColl.size() &&
1681 nOldTabBase != nTabBase &&
1682 nTabBase != ww::stiNil
1683 )
1684 {
1685 // #i61789: Stop searching when next style is the same as the
1686 // current one (prevent loop)
1687 aLoopWatch.insert(reinterpret_cast<size_t>(pSty));
1688 if (nTabBase < m_vColl.size())
1689 pSty = m_vColl[nTabBase].m_pFormat;
1690 //TODO figure out the else branch
1691
1692 if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
1693 aLoopWatch.end())
1694 pSty = nullptr;
1695 }
1696 else
1697 pSty = nullptr; // Give up on the search
1698 }
1699 }
1700
1701 SvxTabStop aTabStop;
1702 for (short i=0; i < nDel; ++i)
1703 {
1704 sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
1705 if( nPos != SVX_TAB_NOTFOUND )
1706 aAttr->Remove( nPos );
1707 }
1708
1709 for (short i=0; i < nIns; ++i)
1710 {
1711 short nPos = SVBT16ToUInt16(pIns + i*2);
1712 aTabStop.GetTabPos() = nPos;
1713 switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
1714 {
1715 case 0:
1716 aTabStop.GetAdjustment() = SvxTabAdjust::Left;
1717 break;
1718 case 1:
1719 aTabStop.GetAdjustment() = SvxTabAdjust::Center;
1720 break;
1721 case 2:
1722 aTabStop.GetAdjustment() = SvxTabAdjust::Right;
1723 break;
1724 case 3:
1725 aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
1726 break;
1727 case 4:
1728 continue; // Ignore Bar
1729 }
1730
1731 switch( pTyp[i].aBits1 >> 3 & 0x7 )
1732 {
1733 case 0:
1734 aTabStop.GetFill() = ' ';
1735 break;
1736 case 1:
1737 aTabStop.GetFill() = '.';
1738 break;
1739 case 2:
1740 aTabStop.GetFill() = '-';
1741 break;
1742 case 3:
1743 case 4:
1744 aTabStop.GetFill() = '_';
1745 break;
1746 }
1747
1748 sal_uInt16 nPos2 = aAttr->GetPos( nPos );
1749 if (nPos2 != SVX_TAB_NOTFOUND)
1750 aAttr->Remove(nPos2); // Or else Insert() refuses
1751 aAttr->Insert(aTabStop);
1752 }
1753
1754 if (nIns || nDel)
1755 NewAttr(*aAttr);
1756 else
1757 {
1758 // Here we have a tab definition which inserts no extra tabs, or deletes
1759 // no existing tabs. An older version of writer is probably the creator
1760 // of the document :-( . So if we are importing a style we can just
1761 // ignore it. But if we are importing into text we cannot as during
1762 // text SwWW8ImplReader::Read_Tab is called at the begin and end of
1763 // the range the attrib affects, and ignoring it would upset the
1764 // balance
1765 if (!m_pCurrentColl) // not importing into a style
1766 {
1767 SvxTabStopItem aOrig = pSty ?
1770 NewAttr(aOrig);
1771 }
1772 }
1773}
1774
1779{
1780 // correct the LastPrinted date in DocumentProperties
1781 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1782 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1783 uno::Reference<document::XDocumentProperties> xDocuProps(
1784 xDPS->getDocumentProperties());
1785 OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null");
1786 if (xDocuProps.is())
1787 {
1788 DateTime aLastPrinted(
1789 msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
1790 ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
1791 xDocuProps->setPrintDate(uDT);
1792 }
1793
1794 // COMPATIBILITY FLAGS START
1795
1796 // #i78951# - remember the unknown compatibility options
1797 // so as to export them out
1800
1801 // The distance between two paragraphs is the sum of the bottom distance of
1802 // the first paragraph and the top distance of the second one
1805 // move tabs on alignment
1807 // #i24363# tab stops relative to indent
1809 // tdf#117923
1814 // tdf#128195
1821
1822 // Import Default Tabs
1823 tools::Long nDefTabSiz = m_xWDop->dxaTab;
1824 if( nDefTabSiz < 56 )
1825 nDefTabSiz = 709;
1826
1827 // We want exactly one DefaultTab
1828 SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP );
1829 const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default;
1830
1832
1833 // Import zoom factor
1834 if (m_xWDop->wScaleSaved)
1835 {
1836 //Import zoom type
1837 sal_Int16 nZoomType;
1838 switch (m_xWDop->zkSaved) {
1839 case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break;
1840 case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break;
1841 case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break;
1842 default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break;
1843 }
1844 uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({
1845 { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) },
1846 { "VisibleBottom", uno::Any(sal_Int32(0)) },
1847 { "ZoomType", uno::Any(nZoomType) }
1848 }));
1849
1851 xBox->insertByIndex(sal_Int32(0), uno::Any(aViewProps));
1852 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
1853 xViewDataSupplier->setViewData(xBox);
1854 }
1855
1865
1866 // #i25901# - set new compatibility option
1867 // 'Add paragraph and table spacing at bottom of table cells'
1870
1871 // #i11860# - set new compatibility option
1872 // 'Use former object positioning' to <false>
1874
1875 // #i27767# - set new compatibility option
1876 // 'Consider Wrapping mode when positioning object' to <true>
1878
1880
1881 m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
1882
1884
1894
1895 // COMPATIBILITY FLAGS END
1896
1897 // Import magic doptypography information, if it's there
1898 if (m_xWwFib->m_nFib > 105)
1899 ImportDopTypography(m_xWDop->doptypography);
1900
1901 // disable form design mode to be able to use imported controls directly
1902 // #i31239# always disable form design mode, not only in protected docs
1903 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1904 if (xDocProps.is())
1905 {
1906 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1907 if (xInfo.is())
1908 {
1909 if (xInfo->hasPropertyByName("ApplyFormDesignMode"))
1910 xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::Any(false));
1911 }
1912
1913 // for the benefit of DOCX - if this is ever saved in that format.
1914 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1915 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1916 { "name", uno::Any(OUString("compatibilityMode")) },
1917 { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
1918 { "val", uno::Any(OUString("11")) } //11: Use features specified in MS-DOC.
1919 }));
1920
1921 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
1922 { "compatSetting", uno::Any(aCompatSetting) }
1923 }));
1924
1925 aGrabBag["CompatSettings"] <<= aValue;
1926 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1927 }
1928
1929 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
1930 // Treat comments-only like read-only since Writer has no support for that.
1931 // Still allow editing of form fields, without requiring the password.
1932 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
1933 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
1935 else if ( xDocProps.is() )
1936 {
1937 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1938 aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc;
1939 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1940 }
1941
1943 if (rOpt.IsUseEnhancedFields())
1945
1946 if (m_xWDop->iGutterPos)
1947 {
1949 }
1950}
1951
1953{
1954 switch (rTypo.m_iLevelOfKinsoku)
1955 {
1956 case 2: // custom
1957 {
1958 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
1959 OUString(+rTypo.m_rgxchLPunct));
1960 // unary + makes sure not to accidentally call the
1961 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
1962 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
1963 // prefix leading up to the first NUL
1965 aForbidden);
1966 // Obviously cannot set the standard level 1 for japanese, so
1967 // bail out now while we can.
1968 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
1969 return;
1970 }
1971 break;
1972 default:
1973 break;
1974 }
1975
1976 /*
1977 This MS hack means that level 2 of japanese is not in operation, so we put
1978 in what we know are the MS defaults, there is a complementary reverse
1979 hack in the writer. Its our default as well, but we can set it anyway
1980 as a flag for later.
1981 */
1982 if (!rTypo.m_reserved2)
1983 {
1984 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
1987 }
1988
1991}
1992
1997 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
1998 mxOldStck(std::move(pRdr->m_xCtrlStck)),
1999 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2000 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2001 mxOldPlcxMan(pRdr->m_xPlcxMan),
2002 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2003 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2004 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2005 mpPrevNumRule(pRdr->m_pPrevNumRule),
2006 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2007 mnInTable(pRdr->m_nInTable),
2008 mnCurrentColl(pRdr->m_nCurrentColl),
2009 mcSymbol(pRdr->m_cSymbol),
2010 mbIgnoreText(pRdr->m_bIgnoreText),
2011 mbSymbol(pRdr->m_bSymbol),
2012 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2013 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2014 mbAnl(pRdr->m_bAnl),
2015 mbInHyperlink(pRdr->m_bInHyperlink),
2016 mbPgSecBreak(pRdr->m_bPgSecBreak),
2017 mbWasParaEnd(pRdr->m_bWasParaEnd),
2018 mbHasBorder(pRdr->m_bHasBorder),
2019 mbFirstPara(pRdr->m_bFirstPara)
2020{
2021 pRdr->m_bSymbol = false;
2022 pRdr->m_bHdFtFootnoteEdn = true;
2023 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2024 = pRdr->m_bHasBorder = false;
2025 pRdr->m_bFirstPara = true;
2026 pRdr->m_nInTable = 0;
2027 pRdr->m_pPreviousNumPaM = nullptr;
2028 pRdr->m_pPrevNumRule = nullptr;
2029 pRdr->m_nCurrentColl = 0;
2030
2031 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2032 *pRdr));
2033
2034 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2035
2036 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2037
2038 // Save the attribute manager: we need this as the newly created PLCFx Manager
2039 // access the same FKPs as the old one and their Start-End position changes.
2040 if (pRdr->m_xPlcxMan)
2041 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2042
2043 if (nStartCp != -1)
2044 {
2045 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2046 mxOldPlcxMan->GetManType(), nStartCp);
2047 }
2048
2049 maOldApos.push_back(false);
2050 maOldApos.swap(pRdr->m_aApos);
2051 maOldFieldStack.swap(pRdr->m_aFieldStack);
2052}
2053
2055{
2056 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2057 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2060 pRdr->m_xTableDesc = std::move(mxTableDesc);
2061 pRdr->m_cSymbol = mcSymbol;
2062 pRdr->m_bSymbol = mbSymbol;
2066 pRdr->m_nInTable = mnInTable;
2067 pRdr->m_bAnl = mbAnl;
2072 pRdr->m_bHasBorder = mbHasBorder;
2073 pRdr->m_bFirstPara = mbFirstPara;
2074
2075 // Close all attributes as attributes could be created that extend the Fly
2076 pRdr->DeleteCtrlStack();
2077 pRdr->m_xCtrlStck = std::move(mxOldStck);
2078
2079 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2080
2081 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2082 // place, or somewhere close if that place got destroyed
2083 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2084 pRdr->m_oLastAnchorPos.reset();
2085
2086 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2087
2088 if (xLastAnchorCursor)
2089 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2090
2091 pRdr->DeleteAnchorStack();
2092 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2093
2094 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2095
2096 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2097 pRdr->m_xPlcxMan = mxOldPlcxMan;
2098 if (pRdr->m_xPlcxMan)
2099 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2100 pRdr->m_aApos.swap(maOldApos);
2101 pRdr->m_aFieldStack.swap(maOldFieldStack);
2102}
2103
2105 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2106{
2107 if (nStartCp < 0 || nLen < 0)
2108 return;
2109
2110 // Saves Flags (amongst other things) and resets them
2111 WW8ReaderSave aSave( this );
2112
2113 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2114
2115 // Read Text for Header, Footer or Footnote
2116 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2117 aSave.Restore( this );
2118}
2119
2124{
2125 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2126 if (!pSD)
2127 return 0;
2128
2129 const void* pData = pSD->GetData();
2130 if (!pData)
2131 return 0;
2132
2133 OUString sAuthor;
2134 OUString sInitials;
2135 if( m_bVer67 )
2136 {
2137 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2138 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2139 if (pA)
2140 sAuthor = *pA;
2141 else
2142 {
2143 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2144 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2145 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2146 }
2147 }
2148 else
2149 {
2150 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2151 {
2152 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2153 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2154 OUStringBuffer aBuf;
2155 aBuf.setLength(nLen);
2156 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2157 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2158 sInitials = aBuf.makeStringAndClear();
2159 }
2160
2161 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2162 sAuthor = *pA;
2163 else
2164 sAuthor = sInitials;
2165 }
2166
2167 sal_uInt32 nDateTime = 0;
2168
2169 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2170 {
2171 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2172 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2173 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2174 }
2175
2176 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2177
2178 OUString sText;
2180 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2181
2183 SwPostItField aPostIt(
2185 sText, sInitials, OUString(), aDate );
2186 aPostIt.SetTextObject(std::move(pOutliner));
2187
2188 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2189 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2191 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2192 // If this is a range, make sure that it ends after the just inserted character, not before it.
2194
2195 return 0;
2196}
2197
2199 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2200{
2201 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2202 OSL_ENSURE(pSttIdx, "impossible");
2203 if (!pSttIdx)
2204 return;
2205
2206 SwPosition aTmpPos(*m_pPaM->GetPoint());
2207
2208 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2209
2210 // tdf#122425: Explicitly remove borders and spacing
2213
2214 SwFlyFrameFormat* pFrame
2215 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2216
2217 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2218 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2219 pFrame->SetFormatAttr( aAnch );
2221 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2222 if( eFrameSize != aSz.GetWidthSizeType() )
2223 aSz.SetWidthSizeType( eFrameSize );
2224 pFrame->SetFormatAttr(aSz);
2225 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2226 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2227
2228 // #i43427# - send frame for header/footer into background.
2229 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2230 SdrObject* pFrameObj = CreateContactObject( pFrame );
2231 OSL_ENSURE( pFrameObj,
2232 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2233 if ( pFrameObj )
2234 {
2235 pFrameObj->SetOrdNum( 0 );
2236 }
2237 MoveInsideFly(pFrame);
2238
2239 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2240
2241 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2242
2243 MoveOutsideFly(pFrame, aTmpPos);
2244}
2245
2246void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2247{
2248 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2249 if (!pSttIdx)
2250 return;
2251
2252 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2253
2254 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2255
2256 *m_pPaM->GetPoint() = aTmpPos;
2257}
2258
2260{
2261 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2262 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2263}
2264
2266 int nSect)
2267{
2268 if (m_xHdFt)
2269 {
2270 WW8_CP nStart, nLen;
2271 sal_uInt8 nNumber = 5;
2272
2273 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2274 {
2275 if (nI & nWhichItems)
2276 {
2277 bool bOk = true;
2278 if( m_bVer67 )
2279 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2280 else
2281 {
2282 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2283 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2284 }
2285
2286 if (bOk)
2287 return true;
2288 }
2289 }
2290 }
2291 return false;
2292}
2293
2294void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2295 const wwSection &rSection)
2296{
2297 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2298 SwPageDesc *pPD = rSection.mpPage;
2299
2300 if( !m_xHdFt )
2301 return;
2302
2303 WW8_CP nStart, nLen;
2304 sal_uInt8 nNumber = 5;
2305
2306 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2307 // corresponding to bit fields in grpfIhdt indicating which
2308 // header/footer(s) are present in this section
2309 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2310 {
2311 if (nI & grpfIhdt)
2312 {
2313 bool bOk = true;
2314 if( m_bVer67 )
2315 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2316 else
2317 {
2318 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2319 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2320 }
2321
2322 bool bUseLeft
2323 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2324 bool bUseFirst
2325 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2326
2327 // If we are loading a first-page header/footer which is not
2328 // actually enabled in this section (it still needs to be
2329 // loaded as it may be inherited by a later section)
2330 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2331
2332 bool bFooter
2333 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2334
2335 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2336 : bUseFirst ? pPD->GetFirstMaster()
2337 : pPD->GetMaster();
2338
2339 SwFrameFormat* pHdFtFormat;
2340 // If we have empty first page header and footer.
2341 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2342 if (bFooter)
2343 {
2344 m_bIsFooter = true;
2345 //#i17196# Cannot have left without right
2346 if (!bDisabledFirst
2347 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2349 if (bUseLeft)
2350 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2351 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2353 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2354 }
2355 else
2356 {
2357 m_bIsHeader = true;
2358 //#i17196# Cannot have left without right
2359 if (!bDisabledFirst
2360 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2362 if (bUseLeft)
2363 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2364 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2366 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2367 }
2368
2369 if (bOk)
2370 {
2371 bool bHackRequired = false;
2372 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2373 bHackRequired = true;
2374 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2375 bHackRequired = true;
2376
2377 if (bHackRequired)
2378 {
2379 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2380 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2381 }
2382 else
2383 Read_HdFtText(nStart, nLen, pHdFtFormat);
2384 }
2385 else if (pPrev)
2386 CopyPageDescHdFt(pPrev, pPD, nI);
2387
2388 m_bIsHeader = m_bIsFooter = false;
2389 }
2390 }
2391}
2392
2394{
2395 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2396}
2397
2398void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2399 const wwSection *pPrevious)
2400{
2401 // Header/Footer not present
2402 if (!rSection.maSep.grpfIhdt)
2403 return;
2404
2405 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2406 if (rSection.mpPage)
2407 {
2408 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2409 rSection);
2410 }
2411
2412 // Header/Footer - Update Index
2413 // So that the index is still valid later on
2414 if (mrReader.m_xHdFt)
2415 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2416
2417}
2418
2420{
2422
2423 const SwNumRule* pRule = nullptr;
2424
2425 if (pText != nullptr)
2426 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2427
2428 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2429 // set it as a separate paragraph property, just like we do for DOCX.
2430 // This is only being used for numbering currently, so limiting to that context.
2431 if (pRule)
2432 {
2435 items(m_pPaM->GetDoc().GetAttrPool());
2436
2437 SfxWhichIter aIter(items);
2438 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2439 {
2440 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2441 if (pItem)
2442 items.Put(*pItem);
2443 }
2445 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2446 pText->SetAttr(item);
2447 }
2448
2449 if (
2450 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2452 )
2453 {
2454 // If after spacing is set to auto, set the after space to 0
2455 if (m_bParaAutoAfter)
2457
2458 // If the previous textnode had numbering and
2459 // and before spacing is set to auto, set before space to 0
2462
2463 // If the previous numbering rule was different we need
2464 // to insert a space after the previous paragraph
2465 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2467
2468 // cache current paragraph
2470 {
2471 delete m_pPreviousNumPaM;
2472 m_pPreviousNumPaM = nullptr;
2473 }
2474
2476 m_pPrevNumRule = pRule;
2477 }
2478 else if(!pRule && m_pPreviousNumPaM)
2479 {
2480 // If the previous paragraph has numbering but the current one does not
2481 // we need to add a space after the previous paragraph
2483 delete m_pPreviousNumPaM;
2484 m_pPreviousNumPaM = nullptr;
2485 m_pPrevNumRule = nullptr;
2486 }
2487 else
2488 {
2489 // clear paragraph cache
2491 {
2492 delete m_pPreviousNumPaM;
2493 m_pPreviousNumPaM = nullptr;
2494 }
2495 m_pPrevNumRule = pRule;
2496 }
2497
2498 // If this is the first paragraph in the document and
2499 // Auto-spacing before paragraph is set,
2500 // set the upper spacing value to 0
2501 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2503
2504 m_bFirstPara = false;
2505
2507
2508 // We can flush all anchored graphics at the end of a paragraph.
2509 m_xAnchorStck->Flush();
2510}
2511
2512bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2513{
2514 bool bRet = false;
2515 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2516
2517 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2518
2519 if(pULSpaceItem != nullptr)
2520 {
2521 SvxULSpaceItem aUL(*pULSpaceItem);
2522
2523 if(bIsUpper)
2524 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2525 else
2526 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2527
2528 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2529 rMyPam.GetPoint()->SetContent(0);
2530 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2531 rMyPam.GetPoint()->SetContent(nEnd);
2532 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2533 bRet = true;
2534 }
2535 return bRet;
2536}
2537
2539{
2540 return SetSpacing(rMyPam, nSpace, false);
2541}
2542
2544{
2545 return SetSpacing(rMyPam, nSpace, true);
2546}
2547
2548sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2549{
2550 if (m_bVer67)
2551 return NS_sprm::v6::sprmPTtp;
2553}
2554
2556{
2557 // Frame/Table/Anl
2558 if (m_bAnl)
2559 StopAllAnl(); // -> bAnl = false
2560
2561 while(m_aApos.size() > 1)
2562 {
2563 StopTable();
2564 m_aApos.pop_back();
2565 --m_nInTable;
2566 if (m_aApos[m_nInTable])
2567 StopApo();
2568 }
2569
2570 if (m_aApos[0])
2571 StopApo();
2572
2573 OSL_ENSURE(!m_nInTable, "unclosed table!");
2574}
2575
2577{
2578 // This is ww8 version of the code deciding if the table needs to be
2579 // in a floating frame.
2580 // For OOXML code, see SectionPropertyMap::FloatingTableConversion in
2581 // writerfilter/source/dmapper/PropertyMap.cxx
2582 // The two should do ~same, so if you make changes here, please check
2583 // that the other is in sync.
2584
2585 // Note that this is just a list of heuristics till sw core can have a
2586 // table that is floating and can span over multiple pages at the same
2587 // time.
2588
2589 // If the floating table is in a header or footer, then it won't be a
2590 // multi-page one, so can always do the conversion.
2591 if (m_bIsHeader || m_bIsFooter)
2592 {
2593 return true;
2594 }
2595
2596 bool bResult = true;
2597
2599 if (nullptr != aRes.pSprm)
2600 {
2601 bResult = false;
2602 WW8TabBandDesc aDesc;
2603 aDesc.ReadDef(false, aRes.pSprm, aRes.nRemainingData);
2604 int nTextAreaWidth = m_aSectionManager.GetTextAreaWidth();
2605 int nTableWidth = aDesc.nCenter[aDesc.nWwCols] - aDesc.nCenter[0];
2606
2607 // It seems Word has a limit here, so that in case the table width is quite
2608 // close to the text area width, then it won't perform a wrapping, even in
2609 // case the content (e.g. an empty paragraph) would fit. The magic constant
2610 // here represents this limit.
2611 const int nMagicNumber = 469;
2612
2613 // If the table is wider than the text area, then don't create a fly
2614 // for the table: no wrapping will be performed anyway, but multi-page
2615 // tables will be broken.
2616 if ((nTableWidth + nMagicNumber) < nTextAreaWidth)
2617 bResult = true;
2618
2619 // If there are columns, do create a fly, as the flow of the columns
2620 // would otherwise restrict the table.
2621 if (!bResult && (m_aSectionManager.CurrentSectionColCount() >= 2))
2622 bResult = true;
2623 }
2624
2625 if (bResult)
2626 {
2627 WW8PLCFxSave1 aSave;
2628 pPap->Save(aSave);
2629 if (SearchTableEnd(pPap))
2630 {
2631 // Table is considered to be imported into a fly frame and we
2632 // know where the end of the table is.
2633 bool bIsUnicode;
2634 WW8_FC nFc = m_xSBase->WW8Cp2Fc(pPap->Where(), &bIsUnicode);
2635 sal_uInt64 nPos = m_pStrm->Tell();
2636 m_pStrm->Seek(nFc);
2637 sal_uInt16 nUChar = 0;
2638 if (bIsUnicode)
2639 m_pStrm->ReadUInt16(nUChar);
2640 else
2641 {
2642 sal_uInt8 nChar = 0;
2643 m_pStrm->ReadUChar(nChar);
2644 nUChar = nChar;
2645 }
2646 m_pStrm->Seek(nPos);
2647 if (nUChar == 0xc)
2648 // The pap after the table starts with a page break, so
2649 // there will be no wrapping around the float-table.
2650 // Request no fly in this case, so the table can properly
2651 // be a multi-page one if necessary.
2652 bResult = false;
2653 }
2654 pPap->Restore(aSave);
2655 }
2656
2657 return bResult;
2658}
2659
2660bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2661{
2662 // Frame/Table/Anl
2663 if (m_bInHyperlink)
2664 return false;
2665
2666 rbReSync = false;
2667
2668 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2669
2670 // TabRowEnd
2671 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2672
2673// Unfortunately, for every paragraph we need to check first whether
2674// they contain a sprm 29 (0x261B), which starts an APO.
2675// All other sprms then refer to that APO and not to the normal text
2676// surrounding it.
2677// The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2678
2679// WW: Table in APO is possible (Both Start-Ends occur at the same time)
2680// WW: APO in Table not possible
2681
2682// This mean that of a Table is the content of an APO, the APO start needs
2683// to be edited first, so that the Table remains in the APO and not the
2684// other way around.
2685// At the End, however, we need to edit the Table End first as the APO
2686// must end after that Table (or else we never find the APO End).
2687
2688// The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2689
2690// If the Table is within an APO the TabRowEnd Area misses the
2691// APO settings.
2692// To not end the APO there, we do not call ProcessApo
2693
2694// KHZ: When there is a table inside the Apo the Apo-flags are also
2695// missing for the 2nd, 3rd... paragraphs of each cell.
2696
2697// 1st look for in-table flag, for 2000+ there is a subtable flag to
2698// be considered, the sprm 6649 gives the level of the table
2699 sal_uInt8 nCellLevel = 0;
2700
2701 if (m_bVer67)
2702 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2703 else
2704 {
2705 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2706 if (!nCellLevel)
2707 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2708 }
2709 do
2710 {
2711 WW8_TablePos *pTabPos=nullptr;
2712 WW8_TablePos aTabPos;
2713 if(nCellLevel && !m_bVer67)
2714 {
2715 WW8PLCFxSave1 aSave;
2716 m_xPlcxMan->GetPap()->Save( aSave );
2717 rbReSync = true;
2718 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2719 WW8_CP nMyStartCp=nStartCp;
2720
2721 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2722 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2723 nCellLevel = *aLevel.pSprm;
2724
2725 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2726
2727 // Bad Table, remain unchanged in level, e.g. #i19667#
2728 if (!bHasRowEnd)
2729 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2730
2731 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2732 pTabPos = &aTabPos;
2733
2734 m_xPlcxMan->GetPap()->Restore( aSave );
2735 }
2736
2737 // Then look if we are in an Apo
2738
2739 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2740
2741 // Look to see if we are in a Table, but Table in foot/end note not allowed
2742 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2743
2744 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2745
2746 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2747 // WW8TabDesc::TableCellEnd() from making nonsense
2748
2749 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2750 bStopTab = bStartTab = true; // Required to stop and start table
2751
2752 // Test for Anl (Numbering) and process all events in the right order
2753 if( m_bAnl && !bTableRowEnd )
2754 {
2755 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2756 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2757 if (pSprm13 && aSprm13.nRemainingData >= 1)
2758 { // Still Anl left?
2759 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2760 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2761 || aApo.HasStartStop() // Forced Anl end
2762 || bStopTab || bStartTab )
2763 {
2764 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2765 }
2766 else
2767 {
2768 NextAnlLine( pSprm13 ); // Next Anl Line
2769 }
2770 }
2771 else
2772 { // Regular Anl end
2773 StopAllAnl(); // Actual end
2774 }
2775 }
2776 if (bStopTab)
2777 {
2778 StopTable();
2779 m_aApos.pop_back();
2780 --m_nInTable;
2781 }
2782 if (aApo.mbStopApo)
2783 {
2784 StopApo();
2785 m_aApos[m_nInTable] = false;
2786 }
2787
2788 if (aApo.mbStartApo)
2789 {
2790 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2791 // We need an ReSync after StartApo
2792 // (actually only if the Apo extends past a FKP border)
2793 rbReSync = true;
2794 }
2795 if (bStartTab)
2796 {
2797 WW8PLCFxSave1 aSave;
2798 m_xPlcxMan->GetPap()->Save( aSave );
2799
2800 // Numbering for cell borders causes a crash -> no Anls in Tables
2801 if (m_bAnl)
2802 StopAllAnl();
2803
2804 if(m_nInTable < nCellLevel)
2805 {
2806 if (StartTable(nStartCp))
2807 ++m_nInTable;
2808 else
2809 break;
2810 m_aApos.push_back(false);
2811 }
2812
2813 if(m_nInTable >= nCellLevel)
2814 {
2815 // We need an ReSync after StartTable
2816 // (actually only if the Apo extends past a FKP border)
2817 rbReSync = true;
2818 m_xPlcxMan->GetPap()->Restore( aSave );
2819 }
2820 }
2821 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2822 return bTableRowEnd;
2823}
2824
2826{
2827 /*
2828 #i22206#/#i52786#
2829 The (default) character set used for a run of text is the default
2830 character set for the version of Word that last saved the document.
2831
2832 This is a bit tentative, more might be required if the concept is correct.
2833 When later version of word write older 6/95 documents the charset is
2834 correctly set in the character runs involved, so it's hard to reproduce
2835 documents that require this to be sure of the process involved.
2836 */
2837 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
2838 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2839 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2841}
2842
2844{
2845 /*
2846 #i22206#/#i52786#
2847 The (default) character set used for a run of text is the default
2848 character set for the version of Word that last saved the document.
2849
2850 This is a bit tentative, more might be required if the concept is correct.
2851 When later version of word write older 6/95 documents the charset is
2852 correctly set in the character runs involved, so it's hard to reproduce
2853 documents that require this to be sure of the process involved.
2854 */
2855 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_CJK_LANGUAGE));
2856 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2857 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2859}
2860
2862{
2863 /*
2864 #i2015
2865 If the hard charset is set use it, if not see if there is an open
2866 character run that has set the charset, if not then fallback to the
2867 current underlying paragraph style.
2868 */
2869 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2870 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2871 {
2872 if (!m_bVer67)
2873 eSrcCharSet = GetCharSetFromLanguage();
2874 else if (!m_aFontSrcCharSets.empty())
2875 eSrcCharSet = m_aFontSrcCharSets.top();
2876 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2877 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2878 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2879 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2880 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2881 eSrcCharSet = GetCharSetFromLanguage();
2882 }
2883 return eSrcCharSet;
2884}
2885
2886//Takashi Ono for CJK
2888{
2889 /*
2890 #i2015
2891 If the hard charset is set use it, if not see if there is an open
2892 character run that has set the charset, if not then fallback to the
2893 current underlying paragraph style.
2894 */
2895 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2896 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2897 {
2898 if (!m_aFontSrcCJKCharSets.empty())
2899 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2900 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2901 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2902 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2903 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2904 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2905 eSrcCharSet = GetCJKCharSetFromLanguage();
2906 }
2907 return eSrcCharSet;
2908}
2909
2911{
2912 if (m_pPostProcessAttrsInfo == nullptr)
2913 return;
2914
2915 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2916
2917 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2918 {
2919 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2920 *pItem);
2921 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2922 pItem->Which());
2923 }
2924
2926}
2927
2928/*
2929 #i9240#
2930 It appears that some documents that are in a baltic 8 bit encoding which has
2931 some undefined characters can have use made of those characters, in which
2932 case they default to CP1252. If not then it's perhaps that the font encoding
2933 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2934 is always 1252.
2935
2936 So an encoding converter that on an undefined character attempts to
2937 convert from 1252 on the undefined character
2938*/
2939static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2940 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2941{
2942 const sal_uInt32 nFlags =
2943 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2944 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2945 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2946 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2947
2948 const sal_uInt32 nFlags2 =
2949 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2950 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2951 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2952 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2953
2954 std::size_t nDestChars=0;
2955 std::size_t nConverted=0;
2956
2957 do
2958 {
2959 sal_uInt32 nInfo = 0;
2960 sal_Size nThisConverted=0;
2961
2962 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2963 pIn+nConverted, nInLen-nConverted,
2964 pOut+nDestChars, nOutLen-nDestChars,
2965 nFlags, &nInfo, &nThisConverted);
2966
2967 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2968
2969 nConverted += nThisConverted;
2970
2971 if (
2972 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2973 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2974 )
2975 {
2976 sal_Size nOtherConverted;
2977 rtl_TextToUnicodeConverter hCP1252Converter =
2978 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2979 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2980 pIn+nConverted, 1,
2981 pOut+nDestChars, nOutLen-nDestChars,
2982 nFlags2, &nInfo, &nOtherConverted);
2983 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2984 nConverted+=1;
2985 }
2986 } while (nConverted < nInLen);
2987
2988 return nDestChars;
2989}
2990
2992{
2993 bool bResult = false;
2994
2995 switch (static_cast<sal_uInt16>(nLang))
2996 {
2997 case 0x1401: // Arabic(Algeria)
2998 case 0x3c01: // Arabic(Bahrain)
2999 case 0xc01: // Arabic(Egypt)
3000 case 0x801: // Arabic(Iraq)
3001 case 0x2c01: // Arabic (Jordan)
3002 case 0x3401: // Arabic(Kuwait)
3003 case 0x3001: // Arabic(Lebanon)
3004 case 0x1001: // Arabic(Libya)
3005 case 0x1801: // Arabic(Morocco)
3006 case 0x2001: // Arabic(Oman)
3007 case 0x4001: // Arabic(Qatar)
3008 case 0x401: // Arabic(Saudi Arabia)
3009 case 0x2801: // Arabic(Syria)
3010 case 0x1c01: // Arabic(Tunisia)
3011 case 0x3801: // Arabic(U.A.E)
3012 case 0x2401: // Arabic(Yemen)
3013 bResult = true;
3014 break;
3015 default:
3016 break;
3017 }
3018
3019 return bResult;
3020}
3021
3023{
3024 if (nChar >= 0x0030 && nChar <= 0x0039)
3025 return nChar + 0x0630;
3026
3027 return nChar;
3028}
3029
3030namespace
3031{
3032 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3033 {
3034 //if read len was in or around that of allocated len, just reuse pStr
3035 if (nAllocLen < pStr->length + 256)
3036 return OUString(pStr, SAL_NO_ACQUIRE);
3037 //otherwise copy the shorter used section to release extra mem
3038 OUString sRet(pStr->buffer, pStr->length);
3039 rtl_uString_release(pStr);
3040 return sRet;
3041 }
3042}
3043
3047bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3048{
3049 sal_Int32 nRequestedStrLen = nEnd - rPos;
3050
3051 OSL_ENSURE(nRequestedStrLen, "String is 0");
3052 if (nRequestedStrLen <= 0)
3053 return true;
3054
3055 WW8_CP nCp;
3056 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3057 if (bFail)
3058 {
3059 rPos+=nRequestedStrLen;
3060 return true;
3061 }
3062
3063 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3064 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3065 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3066 if (!bValidPos)
3067 {
3068 // Swallow missing range, e.g. #i95550#
3069 rPos+=nRequestedStrLen;
3070 return true;
3071 }
3072
3073 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3074 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3075 if (!nAvailableStrLen)
3076 {
3077 // Swallow missing range, e.g. #i95550#
3078 rPos+=nRequestedStrLen;
3079 return true;
3080 }
3081
3082 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3083
3084 // Reset Unicode flag and correct FilePos if needed.
3085 // Note: Seek is not expensive, as we're checking inline whether or not
3086 // the correct FilePos has already been reached.
3087 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3088
3089 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3090 RTL_TEXTENCODING_MS_1252;
3091 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3092 {
3093 /*
3094 fdo#82904
3095
3096 Older documents exported as word 95 that use unicode aware fonts will
3097 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3098 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3099 pain.
3100
3101 We will try and use a fallback encoding if the conversion from
3102 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3103 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3104 valid RTL_TEXTENCODING_MS_932 by chance :-(
3105
3106 We're not the only ones that struggle with this: Here's the help from
3107 MSOffice 2003 on the topic:
3108
3109 <<
3110 Earlier versions of Microsoft Word were sometimes used in conjunction with
3111 third-party language-processing add-in programs designed to support Chinese or
3112 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3113 results in incorrect text display in more recent versions of Word.
3114
3115 However, you can set options to convert these documents so that text is
3116 displayed correctly. On the Tools menu, click Options, and then click the
3117 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3118 text (to have Word interpret the text as Asian code page data, regardless of
3119 its font) or Automatically detect Asian text (to have Word attempt to determine
3120 which parts of the text are meant to be Asian).
3121 >>
3122
3123 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3124 the language is not Japanese
3125 */
3126
3128 if (pItem != nullptr && LANGUAGE_JAPANESE != static_cast<const SvxLanguageItem *>(pItem)->GetLanguage())
3129 {
3130 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3131 eSrcCharSet = GetCharSetFromLanguage();
3132 }
3133 }
3134 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3135 RTL_TEXTENCODING_MS_1252;
3136
3137 // allocate unicode string data
3138 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3139 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3140 sal_Unicode* pBuffer = xStr->buffer;
3141 sal_Unicode* pWork = pBuffer;
3142
3143 std::unique_ptr<char[]> p8Bits;
3144
3145 rtl_TextToUnicodeConverter hConverter = nullptr;
3146 if (!m_bIsUnicode || m_bVer67)
3147 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3148
3149 if (!m_bIsUnicode)
3150 p8Bits.reset( new char[nStrLen] );
3151
3152 // read the stream data
3153 sal_uInt8 nBCode = 0;
3154 sal_uInt16 nUCode;
3155
3156 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3158 if (pItem != nullptr)
3159 nCTLLang = static_cast<const SvxLanguageItem *>(pItem)->GetLanguage();
3160
3161 sal_Int32 nL2;
3162 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3163 {
3164 if (m_bIsUnicode)
3165 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3166 else
3167 {
3168 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3169 nUCode = nBCode;
3170 }
3171
3172 if (!m_pStrm->good())
3173 {
3174 rPos = WW8_CP_MAX-10; // -> eof or other error
3175 return true;
3176 }
3177
3178 if ((32 > nUCode) || (0xa0 == nUCode))
3179 {
3180 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3181 break; // Special character < 32, == 0xa0 found
3182 }
3183
3184 if (m_bIsUnicode)
3185 {
3186 if (!m_bVer67)
3187 *pWork++ = nUCode;
3188 else
3189 {
3190 if (nUCode >= 0x3000) //0x8000 ?
3191 {
3192 char aTest[2];
3193 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3194 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3195 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3196 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3197 *pWork++ = aTemp[0];
3198 }
3199 else
3200 {
3201 char cTest = static_cast< char >(nUCode & 0x00FF);
3202 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3203 }
3204 }
3205 }
3206 else
3207 p8Bits[nL2] = nBCode;
3208 }
3209
3210 if (nL2)
3211 {
3212 const sal_Int32 nEndUsed = !m_bIsUnicode
3213 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3214 : pWork - pBuffer;
3215
3217 {
3218 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3219 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3220 }
3221
3222 xStr->buffer[nEndUsed] = 0;
3223 xStr->length = nEndUsed;
3224
3225 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3226 rPos += nL2;
3227 if (!m_aApos.back()) // a para end in apo doesn't count
3228 m_bWasParaEnd = false; // No CR
3229 }
3230
3231 if (hConverter)
3232 rtl_destroyTextToUnicodeConverter(hConverter);
3233 return nL2 >= nStrLen;
3234}
3235
3236#define MSASCII SAL_MAX_INT16
3237
3238namespace
3239{
3240 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
3241 sal_Int16 lcl_getScriptType(
3242 const uno::Reference<i18n::XBreakIterator>& rBI,
3243 const OUString &rString, sal_Int32 nPos)
3244 {
3245 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3246 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3247 nScript = MSASCII;
3248 return nScript;
3249 }
3250
3251 // We want to know about WEAK segments, so endOfScript isn't
3252 // useful, and see lcl_getScriptType anyway
3253 sal_Int32 lcl_endOfScript(
3254 const uno::Reference<i18n::XBreakIterator>& rBI,
3255 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3256 {
3257 while (nPos < rString.getLength())
3258 {
3259 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3260 if (nScript != nNewScript)
3261 break;
3262 ++nPos;
3263 }
3264 return nPos;
3265 }
3266
3267 sal_Int32 lcl_getWriterScriptType(
3268 const uno::Reference<i18n::XBreakIterator>& rBI,
3269 const OUString &rString, sal_Int32 nPos)
3270 {
3271 sal_Int16 nScript = i18n::ScriptType::WEAK;
3272
3273 if (rString.isEmpty())
3274 return nScript;
3275
3276 while (nPos >= 0)
3277 {
3278 nScript = rBI->getScriptType(rString, nPos);
3279 if (nScript != i18n::ScriptType::WEAK)
3280 break;
3281 --nPos;
3282 }
3283
3284 return nScript;
3285 }
3286
3287 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3288 {
3289 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3290 }
3291
3292 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3293 {
3294 // Ignoring CharSet, and ignoring unknown pitch
3295 return rA.GetFamilyName() == rB.GetFamilyName() &&
3296 rA.GetStyleName() == rB.GetStyleName() &&
3297 rA.GetFamily() == rB.GetFamily() &&
3298 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3299 }
3300}
3301
3302// In writer we categorize text into CJK, CTL and "Western" for everything else.
3303// Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3304// NonEastAsian/HighAnsi, with some shared characters and some properties to
3305// hint as to which way to bias those shared characters.
3306
3307// That's four categories, we however have three categories. Given that problem
3308// here we would ideally find out "what would word do" to see what font/language
3309// word would assign to characters based on the unicode range they fall into and
3310// hack the word one onto the range we use. However it's unclear what word's
3311// categorization is. So we don't do that here yet.
3312
3313// Additional to the categorization, when word encounters weak text for ambiguous
3314// chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3315// feature in writer.
3316
3317// So what we currently do here then is to split our text into non-weak/weak
3318// sections and uses word's idcthint to determine what font it would use and
3319// force that on for the segment. Following what we *do* know about word's
3320// categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3321// word, something we map to LATIN, so we consider all weak chars in that range
3322// to auto-bias to LATIN.
3323
3324// See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
3326{
3327 if (rAddString.isEmpty())
3328 return;
3329
3330 if (m_bFuzzing)
3331 {
3332 simpleAddTextToParagraph(rAddString);
3333 return;
3334 }
3335
3336 uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
3337 assert(xBI.is());
3338
3339 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3340 sal_Int32 nLen = rAddString.getLength();
3341
3342 OUString sParagraphText;
3343 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3344 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3345 if (pNd)
3346 sParagraphText = pNd->GetText();
3347 sal_Int32 nParaOffset = sParagraphText.getLength();
3348 sParagraphText = sParagraphText + rAddString;
3349
3350 sal_Int32 nPos = 0;
3351 while (nPos < nLen)
3352 {
3353 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3354 if (nEnd < 0)
3355 break;
3356
3357 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3358 const sal_uInt16 aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3359 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3360 bool aForced[] = {false, false, false};
3361
3362 int nLclIdctHint = 0xFF;
3363 if (nScript == i18n::ScriptType::WEAK)
3364 {
3365 const SfxInt16Item *pIdctHint = static_cast<const SfxInt16Item*>(GetFormatAttr(RES_CHRATR_IDCTHINT));
3366 nLclIdctHint = pIdctHint->GetValue();
3367 }
3368 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3369 nLclIdctHint = 0;
3370
3371 sal_uInt16 nForceFromFontId = 0;
3372 if (nLclIdctHint != 0xFF)
3373 {
3374 switch (nLclIdctHint)
3375 {
3376 case 0:
3377 nForceFromFontId = RES_CHRATR_FONT;
3378 break;
3379 case 1:
3380 nForceFromFontId = RES_CHRATR_CJK_FONT;
3381 break;
3382 case 2:
3383 nForceFromFontId = RES_CHRATR_CTL_FONT;
3384 break;
3385 default:
3386 break;
3387 }
3388 }
3389
3390 if (nForceFromFontId != 0)
3391 {
3392 // Now we know that word would use the nForceFromFontId font for this range
3393 // Try and determine what script writer would assign this range to
3394
3395 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3396 nPos + nParaOffset);
3397
3398 bool bWriterWillUseSameFontAsWordAutomatically = false;
3399
3400 if (nWriterScript != i18n::ScriptType::WEAK)
3401 {
3402 if (
3403 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3404 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3405 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3406 )
3407 {
3408 bWriterWillUseSameFontAsWordAutomatically = true;
3409 }
3410 else
3411 {
3412 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3413 sal_uInt16 nDestId = aIds[nWriterScript-1];
3414 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(nDestId));
3415 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3416 }
3417 }
3418
3419 // Writer won't use the same font as word, so force the issue
3420 if (!bWriterWillUseSameFontAsWordAutomatically)
3421 {
3422 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3423
3424 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3425 {
3426 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(aIds[i]));
3427 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3428 if (aForced[i])
3429 {
3430 pOverriddenItems[i] =
3431 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3432
3433 SvxFontItem aForceFont(*pSourceFont);
3434 aForceFont.SetWhich(aIds[i]);
3435 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3436 }
3437 }
3438 }
3439 }
3440
3442
3443 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3444 {
3445 if (aForced[i])
3446 {
3447 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3448 if (pOverriddenItems[i])
3449 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3450 }
3451 }
3452
3453 nPos = nEnd;
3454 if (nPos < nLen)
3455 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3456 }
3457}
3458
3459namespace sw {
3460
3461auto FilterControlChars(OUString const& rString) -> OUString
3462{
3463 OUStringBuffer buf(rString.getLength());
3464 for (sal_Int32 i = 0; i < rString.getLength(); ++i)
3465 {
3466 sal_Unicode const ch(rString[i]);
3467 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3468 {
3469 buf.append(ch);
3470 }
3471 else
3472 {
3473 SAL_INFO("sw.ww8", "filtering control character");
3474 }
3475 }
3476 return buf.makeStringAndClear();
3477}
3478
3479} // namespace sw
3480
3481void SwWW8ImplReader::simpleAddTextToParagraph(const OUString& rAddString)
3482{
3483 OUString const addString(sw::FilterControlChars(rAddString));
3484
3485 if (addString.isEmpty())
3486 return;
3487
3488 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3489 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3490
3491 OSL_ENSURE(pNd, "What the hell, where's my text node");
3492
3493 if (!pNd)
3494 return;
3495
3496 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3497 if (nCharsLeft > 0)
3498 {
3499 if (addString.getLength() <= nCharsLeft)
3500 {
3502 }
3503 else
3504 {
3505 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3507 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3508 }
3509 }
3510 else
3511 {
3514 }
3515
3516 m_bReadTable = false;
3517}
3518
3522bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3523 tools::Long nCpOfs)
3524{
3525 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3526
3527 if (m_bSymbol || m_bIgnoreText)
3528 {
3529 WW8_CP nRequested = nEnd - rPos;
3530 if (m_bSymbol) // Insert special chars
3531 {
3532 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3533 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3534 {
3535 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3536 nRequested = nMaxPossible;
3537 }
3538
3540 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3541 {
3542 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3543 {
3545 }
3549 }
3550 }
3551 m_pStrm->SeekRel(nRequested);
3552 rPos = nEnd; // Ignore until attribute end
3553 return false;
3554 }
3555
3556 while (true)
3557 {
3558 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3559 return false; // Done
3560
3561 bool bStartLine = ReadChar(rPos, nCpOfs);
3562 rPos++;
3563 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3564 {
3565 return bStartLine;
3566 }
3567 }
3568}
3569
3571{
3572 bool bParaEndAdded = false;
3573 // #i1909# section/page breaks should not occur in tables, word
3574 // itself ignores them in this case.
3575 if (!m_nInTable)
3576 {
3577 bool IsTemp=true;
3579 if (pTemp && pTemp->GetText().isEmpty()
3581 {
3582 IsTemp = false;
3585 }
3586
3587 m_bPgSecBreak = true;
3588 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3589 /*
3590 If it's a 0x0c without a paragraph end before it, act like a
3591 paragraph end, but nevertheless, numbering (and perhaps other
3592 similar constructs) do not exist on the para.
3593 */
3594 if (!m_bWasParaEnd && IsTemp)
3595 {
3596 bParaEndAdded = true;
3597 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3598 {
3599 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3600 {
3601 pTextNode->SetAttr(
3603 }
3604 }
3605 }
3606 }
3607 return bParaEndAdded;
3608}
3609
3611{
3612 bool bNewParaEnd = false;
3613 // Reset Unicode flag and correct FilePos if needed.
3614 // Note: Seek is not expensive, as we're checking inline whether or not
3615 // the correct FilePos has already been reached.
3616 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3617 if (!checkSeek(*m_pStrm, nRequestedPos))
3618 return false;
3619
3620 sal_uInt16 nWCharVal(0);
3621 if( m_bIsUnicode )
3622 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3623 else
3624 {
3625 sal_uInt8 nBCode(0);
3626 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3627 nWCharVal = nBCode;
3628 }
3629
3630 sal_Unicode cInsert = '\x0';
3631 bool bParaMark = false;
3632
3633 if ( 0xc != nWCharVal )
3634 m_bFirstParaOfPage = false;
3635
3636 switch (nWCharVal)
3637 {
3638 case 0:
3639 {
3640 // Page number
3641 SwPageNumberField aField(
3645 }
3646 break;
3647 case 0xe:
3648 // if there is only one column word treats a column break like a pagebreak.
3650 bParaMark = HandlePageBreakChar();
3651 else if (!m_nInTable)
3652 {
3653 // Always insert a txtnode for a column break, e.g. ##
3655 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3658 }
3659 break;
3660 case 0x7:
3661 {
3662 bNewParaEnd = true;
3663 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3664 //The last paragraph of each cell is terminated by a special
3665 //paragraph mark called a cell mark. Following the cell mark
3666 //that ends the last cell of a table row, the table row is
3667 //terminated by a special paragraph mark called a row mark
3668 //
3669 //So the 0x7 should be right at the end of the previous
3670 //range to be a real cell-end.
3671 if (pPap->nOrigStartPos == nPosCp+1 ||
3672 pPap->nOrigStartPos == WW8_CP_MAX)
3673 {
3674 TabCellEnd(); // Table cell/row end
3675 }
3676 else
3677 bParaMark = true;
3678 }
3679 break;
3680 case 0xf:
3681 if( !m_bSpec ) // "Satellite"
3682 cInsert = u'\x00a4';
3683 break;
3684 case 0x14:
3685 if( !m_bSpec ) // "Para End" char
3686 cInsert = u'\x00b5';
3687 //TODO: should this be U+00B6 PILCROW SIGN rather than
3688 // U+00B5 MICRO SIGN?
3689 break;
3690 case 0x15:
3691 if( !m_bSpec ) // Juristenparagraph
3692 {
3693 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3694 if (aItr == m_aTOXEndCps.end())
3695 cInsert = u'\x00a7';
3696 else
3697 m_aTOXEndCps.erase(aItr);
3698 }
3699 break;
3700 case 0x9:
3701 cInsert = '\x9'; // Tab
3702 break;
3703 case 0xb:
3704 cInsert = '\xa'; // Hard NewLine
3705 break;
3706 case 0xc:
3707 bParaMark = HandlePageBreakChar();
3708 break;
3709 case 0x1e: // Non-breaking hyphen
3711 break;
3712 case 0x1f: // Non-required hyphens
3714 break;
3715 case 0xa0: // Non-breaking spaces
3717 break;
3718 case 0x1:
3719 /*
3720 Current thinking is that if bObj is set then we have a
3721 straightforward "traditional" ole object, otherwise we have a
3722 graphic preview of an associated ole2 object (or a simple
3723 graphic of course)
3724
3725 normally in the canvas field, the code is 0x8 0x1.
3726 in a special case, the code is 0x1 0x1, which yields a simple picture
3727 */
3728 {
3729 bool bReadObj = IsInlineEscherHack();
3730 if( bReadObj )
3731 {
3732 sal_uInt64 nCurPos = m_pStrm->Tell();
3733 sal_uInt16 nWordCode(0);
3734
3735 if( m_bIsUnicode )
3736 m_pStrm->ReadUInt16( nWordCode );
3737 else
3738 {
3739 sal_uInt8 nByteCode(0);
3740 m_pStrm->ReadUChar( nByteCode );
3741 nWordCode = nByteCode;
3742 }
3743 if( nWordCode == 0x1 )
3744 bReadObj = false;
3745 m_pStrm->Seek( nCurPos );
3746 }
3747 if( !bReadObj )
3748 {
3749 SwFrameFormat *pResult = nullptr;
3750 if (m_bObj)
3751 pResult = ImportOle();
3752 else if (m_bSpec)
3753 {
3754 SwFrameFormat* pAsCharFlyFormat =
3756 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3757 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3758 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3759 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3760 }
3761
3762
3763 // If we have a bad 0x1 insert a space instead.
3764 if (!pResult)
3765 {
3766 cInsert = ' ';
3767 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3768 "WW8: Please report this document, it may have a "
3769 "missing graphic");
3770 }
3771 else
3772 {
3773 // reset the flags.
3774 m_bObj = m_bEmbeddObj = false;
3775 m_nObjLocFc = 0;
3776 }
3777 }
3778 }
3779 break;
3780 case 0x8:
3781 if( !m_bObj )
3782 Read_GrafLayer( nPosCp );
3783 break;
3784 case 0xd:
3785 bNewParaEnd = bParaMark = true;
3786 if (m_nInTable > 1)
3787 {
3788 /*
3789 #i9666#/#i23161#
3790 Yes complex, if there is an entry in the undocumented PLCF
3791 which I believe to be a record of cell and row boundaries
3792 see if the magic bit which I believe to mean cell end is
3793 set. I also think btw that the third byte of the 4 byte
3794 value is the level of the cell
3795 */
3796 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3797 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3798 pTest->Where() == nPosCp+1+nCpOfs)
3799 {
3800 WW8_FC nPos;
3801 void *pData;
3802 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3803 : 0;
3804 if (nData & 0x2) // Might be how it works
3805 {
3806 TabCellEnd();
3807 bParaMark = false;
3808 }
3809 }
3810 // tdf#106799: We expect TTP marks to be also cell marks,
3811 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3813 {
3814 TabCellEnd();
3815 bParaMark = false;
3816 }
3817 }
3818
3819 m_bWasTabCellEnd = false;
3820
3821 break; // line end
3822 case 0x5: // Annotation reference
3823 case 0x13:
3824 break;
3825 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3826 if (!m_aFootnoteStack.empty())
3827 cInsert = '?';
3828 break;
3829 default:
3830 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3831 break;
3832 }
3833
3834 if( '\x0' != cInsert )
3835 {
3836 OUString sInsert(cInsert);
3838 }
3839 if (!m_aApos.back()) // a para end in apo doesn't count
3840 m_bWasParaEnd = bNewParaEnd;
3841 return bParaMark;
3842}
3843
3845 bool* pStartAttr, bool bCallProcessSpecial)
3846{
3847 sal_uInt16 nOldColl = m_nCurrentColl;
3848 m_nCurrentColl = m_xPlcxMan->GetColl();
3849
3850 // Invalid Style-Id
3851 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3852 {
3853 m_nCurrentColl = 0;
3854 m_bParaAutoBefore = false;
3855 m_bParaAutoAfter = false;
3856 }
3857 else
3858 {
3859 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3860 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3861 }
3862
3863 if (nOldColl >= m_vColl.size())
3864 nOldColl = 0; // guess! TODO make sure this is what we want
3865
3866 bool bTabRowEnd = false;
3867 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3868 {
3869 bool bReSync;
3870 // Frame/Table/Autonumbering List Level
3871 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3872 if( bReSync )
3873 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3874 }
3875
3876 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3877 {
3879 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3880 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3881 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3882 }
3883}
3884
3885tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3886{
3887 tools::Long nSkipChars = 0;
3888 WW8PLCFManResult aRes;
3889
3890 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3891 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3892 aRes.nCurrentCp = rTextPos; // Current Cp position
3893
3894 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3895 if ( bNewSection ) // New Section
3896 {
3897 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3898 // Create PageDesc and fill it
3899 m_aSectionManager.CreateSep(rTextPos);
3900 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3901 // Create PageDesc and fill it
3902 m_bPgSecBreak = false;
3903 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3904 }
3905
3906 // New paragraph over Plcx.Fkp.papx
3907 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3908 {
3909 ProcessCurrentCollChange( aRes, &bStartAttr,
3911 !m_bIgnoreText );
3912 rbStartLine = false;
3913 }
3914
3915 // position of last CP that's to be ignored
3916 tools::Long nSkipPos = -1;
3917
3918 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3919 {
3920 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3921 {
3922 if( bStartAttr ) // WW attributes
3923 {
3924 if( aRes.nMemLen >= 0 )
3925 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3926 }
3927 else
3928 EndSprm( aRes.nSprmId ); // Switch off Attr
3929 }
3930 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3931 {
3932 if (bStartAttr)
3933 {
3934 nSkipChars = ImportExtSprm(&aRes);
3935 if (
3936 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3937 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3938 )
3939 {
3940 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3941 // Skip Field/Footnote-/End-Note here
3942 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3943 nSkipPos = rTextPos-1;
3944 }
3945 }
3946 else
3947 EndExtSprm( aRes.nSprmId );
3948 }
3949 }
3950
3951 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3952 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3953 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3954
3955 // Find next Attr position (and Skip attributes of field contents if needed)
3956 if (nSkipChars && !m_bIgnoreText)
3957 m_xCtrlStck->MarkAllAttrsOld();
3958 bool bOldIgnoreText = m_bIgnoreText;
3959 m_bIgnoreText = true;
3960 sal_uInt16 nOldColl = m_nCurrentColl;
3961 bool bDoPlcxManPlusPLus = true;
3962 tools::Long nNext;
3963 do
3964 {
3965 if( bDoPlcxManPlusPLus )
3966 m_xPlcxMan->advance();
3967 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3968
3970 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3971 {
3972 m_pPostProcessAttrsInfo->mbCopy = true;
3973 }
3974
3975 if( (0 <= nNext) && (nSkipPos >= nNext) )
3976 {
3977 if (nDepthGuard >= 1024)
3978 {
3979 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3980 nNext = nTextEnd;
3981 }
3982 else
3983 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3984 bDoPlcxManPlusPLus = false;
3985 m_bIgnoreText = true;
3986 }
3987
3989 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3990 {
3991 m_pPostProcessAttrsInfo->mbCopy = false;
3992 }
3993 }
3994 while( nSkipPos >= nNext );
3995 m_bIgnoreText = bOldIgnoreText;
3996 if( nSkipChars )
3997 {
3998 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
3999 if( nOldColl != m_xPlcxMan->GetColl() )
4000 ProcessCurrentCollChange(aRes, nullptr, false);
4001 }
4002
4003 return nNext;
4004}
4005
4006void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
4007{
4008 // Do we have attributes?
4009 if( rTextPos >= rNext )
4010 {
4011 do
4012 {
4013 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
4014 if (rTextPos == rNext && rTextPos >= nTextEnd)
4015 break;
4016 }
4017 while( rTextPos >= rNext );
4018
4019 }
4020 else if ( rbStartLine )
4021 {
4022 /* No attributes, but still a new line.
4023 * If a line ends with a line break and paragraph attributes or paragraph templates
4024 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
4025 * is false.
4026 * Due to this we need to set the template here as a kind of special treatment.
4027 */
4028 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4030 rbStartLine = false;
4031 }
4032}
4033
4040{
4041 // If there are any unclosed sprms then copy them to
4042 // another stack and close the ones that must be closed
4043 std::stack<sal_uInt16> aStack;
4044 m_xPlcxMan->TransferOpenSprms(aStack);
4045
4046 while (!aStack.empty())
4047 {
4048 sal_uInt16 nSprmId = aStack.top();
4049 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4050 EndSprm(nSprmId);
4051 aStack.pop();
4052 }
4053
4054 EndSpecial();
4055}
4056
4057bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4058{
4059 bool bJoined=false;
4060
4061 bool bStartLine = true;
4062 short nCrCount = 0;
4063 short nDistance = 0;
4064
4065 m_bWasParaEnd = false;
4066 m_nCurrentColl = 0;
4067 m_xCurrentItemSet.reset();
4068 m_nCharFormat = -1;
4069 m_bSpec = false;
4070 m_bPgSecBreak = false;
4071
4072 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4073 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4074
4075 WW8_CP nNext = m_xPlcxMan->Where();
4076 m_xPreviousNode.reset();
4077 sal_uInt8 nDropLines = 0;
4078 SwCharFormat* pNewSwCharFormat = nullptr;
4079 const SwCharFormat* pFormat = nullptr;
4080
4081 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4082 if (!bValidPos)
4083 return false;
4084
4085 WW8_CP l = nStartCp;
4086 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4087 if (nTextLen > nMaxPossible)
4088 {
4089 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4090 nTextLen = nMaxPossible;
4091 }
4092 WW8_CP nTextEnd = nStartCp+nTextLen;
4093 while (l < nTextEnd)
4094 {
4095 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4096 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4097
4098 if (m_pPostProcessAttrsInfo != nullptr)
4100
4101 if (l >= nTextEnd)
4102 break;
4103
4104 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4105
4106 // If the previous paragraph was a dropcap then do not
4107 // create a new txtnode and join the two paragraphs together
4108 if (bStartLine && !m_xPreviousNode) // Line end
4109 {
4110 bool bSplit = true;
4112 {
4114 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4115 bSplit = false;
4116 }
4118 {
4120 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4121 bSplit = false;
4122 }
4123 if (bSplit)
4124 {
4126 }
4127 }
4128
4129 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4130 {
4132 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4133 if (pEndNd)
4134 {
4135 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4136
4137 // Need to reset the font size and text position for the dropcap
4138 {
4139 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4140 m_xCtrlStck->Delete(aTmp);
4141 }
4142
4143 // Get the default document dropcap which we can use as our template
4144 const SwFormatDrop* defaultDrop =
4145 static_cast<const SwFormatDrop*>( GetFormatAttr(RES_PARATR_DROP));
4146 SwFormatDrop aDrop(*defaultDrop);
4147
4148 aDrop.GetLines() = nDropLines;
4149 aDrop.GetDistance() = nDistance;
4150 aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
4151 // Word has no concept of a "whole word dropcap"
4152 aDrop.GetWholeWord() = false;
4153
4154 if (pFormat)
4155 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4156 else if(pNewSwCharFormat)
4157 aDrop.SetCharFormat(pNewSwCharFormat);
4158
4159 SwPosition aStart(*pEndNd);
4160 m_xCtrlStck->NewAttr(aStart, aDrop);
4162 }
4163 m_xPreviousNode.reset();
4164 }
4165 else if (m_bDropCap)
4166 {
4167 // If we have found a dropcap store the textnode
4169
4170 SprmResult aDCS;
4171 if (m_bVer67)
4172 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4173 else
4174 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4175
4176 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4177 nDropLines = (*aDCS.pSprm) >> 3;
4178 else // There is no Drop Cap Specifier hence no dropcap
4179 m_xPreviousNode.reset();
4180
4181 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4182 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4183 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4184 else
4185 nDistance = 0;
4186
4187 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4188
4190 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4191
4192 if (pSwFormatCharFormat)
4193 pFormat = pSwFormatCharFormat->GetCharFormat();
4194
4195 if (m_xCurrentItemSet && !pFormat)
4196 {
4197 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4198 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4200 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4201 }
4202
4203 m_xCurrentItemSet.reset();
4204 m_bDropCap=false;
4205 }
4206
4207 if (bStartLine || m_bWasTabRowEnd)
4208 {
4209 // Call all 64 CRs; not for Header and the like
4210 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4211 {
4212 if (nTextLen < WW8_CP_MAX/100)
4213 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4214 else
4215 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4216 m_xProgress->Update(m_nProgress); // Update
4217 }
4218 }
4219
4220 // If we have encountered a 0x0c which indicates either section of
4221 // pagebreak then look it up to see if it is a section break, and
4222 // if it is not then insert a page break. If it is a section break
4223 // it will be handled as such in the ReadAttrs of the next loop
4224 if (m_bPgSecBreak)
4225 {
4226 // We need only to see if a section is ending at this cp,
4227 // the plcf will already be sitting on the correct location
4228 // if it is there.
4229 WW8PLCFxDesc aTemp;
4230 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4231 if (m_xPlcxMan->GetSepPLCF())
4232 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4233 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4234 {
4235 // #i39251# - insert text node for page break, if no one inserted.
4236 // #i43118# - refine condition: the anchor
4237 // control stack has to have entries, otherwise it's not needed
4238 // to insert a text node.
4239 if (!bStartLine && !m_xAnchorStck->empty())
4240 {
4242 }
4244 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4245 m_bFirstParaOfPage = true;
4246 m_bPgSecBreak = false;
4247 }
4248 }
4249 }
4250
4251 m_xPreviousNode.reset();
4252
4255
4256 if (!m_bInHyperlink)
4257 bJoined = JoinNode(*m_pPaM);
4258
4259 CloseAttrEnds();
4260
4261 m_xPlcxMan.reset();
4262 return bJoined;
4263}
4264
4266 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4267 : m_pDocShell(rD.GetDocShell())
4268 , m_pStg(pStorage)
4269 , m_pStrm(pSt)
4270 , m_pTableStream(nullptr)
4271 , m_pDataStream(nullptr)
4272 , m_rDoc(rD)
4273 , m_pPaM(nullptr)
4274 , m_aSectionManager(*this)
4275 , m_aExtraneousParas(rD)
4276 , m_aInsertedTables(rD)
4277 , m_aSectionNameGenerator(rD, "WW")
4278 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4279 , m_aParaStyleMapper(rD)
4280 , m_aCharStyleMapper(rD)
4281 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4282 , m_pPreviousNumPaM(nullptr)
4283 , m_pPrevNumRule(nullptr)
4284 , m_pCurrentColl(nullptr)
4285 , m_pDfltTextFormatColl(nullptr)
4286 , m_pStandardFormatColl(nullptr)
4287 , m_pDrawModel(nullptr)
4288 , m_pDrawPg(nullptr)
4289 , m_pNumFieldType(nullptr)
4290 , m_sBaseURL(std::move(aBaseURL))
4291 , m_nIniFlags(0)
4292 , m_nIniFlags1(0)
4293 , m_nFieldFlags(0)
4294 , m_bRegardHindiDigits( false )
4295 , m_bDrawCpOValid( false )
4296 , m_nDrawCpO(0)
4297 , m_nPicLocFc(0)
4298 , m_nObjLocFc(0)
4299 , m_nIniFlyDx(0)
4300 , m_nIniFlyDy(0)
4301 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4302 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4303 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4304 , m_nProgress(0)
4305 , m_nCurrentColl(0)
4306 , m_nFieldNum(0)
4307 , m_nLFOPosition(USHRT_MAX)
4308 , m_nCharFormat(0)
4309 , m_nDrawXOfs(0)
4310 , m_nDrawYOfs(0)
4311 , m_nDrawXOfs2(0)
4312 , m_nDrawYOfs2(0)
4313 , m_cSymbol(0)
4314 , m_nWantedVersion(nVersionPara)
4315 , m_nSwNumLevel(0xff)
4316 , m_nWwNumType(0xff)
4317 , m_pChosenWW8OutlineStyle(nullptr)
4318 , m_nListLevel(MAXLEVEL)
4319 , m_bNewDoc(bNewDoc)
4320 , m_bSkipImages(bSkipImages)
4321 , m_bReadNoTable(false)
4322 , m_bPgSecBreak(false)
4323 , m_bSpec(false)
4324 , m_bObj(false)
4325 , m_bTxbxFlySection(false)
4326 , m_bHasBorder(false)
4327 , m_bSymbol(false)
4328 , m_bIgnoreText(false)
4329 , m_nInTable(0)
4330 , m_bWasTabRowEnd(false)
4331 , m_bWasTabCellEnd(false)
4332 , m_bAnl(false)
4333 , m_bHdFtFootnoteEdn(false)
4334 , m_bFootnoteEdn(false)
4335 , m_bIsHeader(false)
4336 , m_bIsFooter(false)
4337 , m_bIsUnicode(false)
4338 , m_bCpxStyle(false)
4339 , m_bStyNormal(false)
4340 , m_bWWBugNormal(false)
4341 , m_bNoAttrImport(false)
4342 , m_bInHyperlink(false)
4343 , m_bWasParaEnd(false)
4344 , m_bVer67(false)
4345 , m_bVer6(false)
4346 , m_bVer7(false)
4347 , m_bVer8(false)
4348 , m_bEmbeddObj(false)
4349 , m_bCurrentAND_fNumberAcross(false)
4350 , m_bNoLnNumYet(true)
4351 , m_bFirstPara(true)
4352 , m_bFirstParaOfPage(false)
4353 , m_bParaAutoBefore(false)
4354 , m_bParaAutoAfter(false)
4355 , m_bDropCap(false)
4356 , m_nDropCap(0)
4357 , m_bBidi(false)
4358 , m_bReadTable(false)
4359 , m_bLoadingTOXCache(false)
4360 , m_nEmbeddedTOXLevel(0)
4361 , m_bLoadingTOXHyperlink(false)
4362 , m_bCareFirstParaEndInToc(false)
4363 , m_bCareLastParaEndInToc(false)
4364 , m_bNotifyMacroEventRead(false)
4365 , m_bFuzzing(utl::ConfigManager::IsFuzzing())
4366{
4367 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4368 m_aApos.push_back(false);
4369
4371}
4372
4374{
4375}
4376
4377void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4378{
4379 if( pStck )
4380 {
4381 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4382 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4383 }
4384 else
4385 {
4386 OSL_ENSURE( false, "WW stack already deleted" );
4387 }
4388}
4389
4391 bool bIgnoreCols)
4392{
4393 SwPageDesc &rPage = *rSection.mpPage;
4394
4395 SetNumberingType(rSection, rPage);
4396
4397 SwFrameFormat &rFormat = rPage.GetMaster();
4398
4399 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4401
4402 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4403 {
4404 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4407 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4408 {
4409 // Only handle shape if it is a background shape
4410 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4411 {
4413 aSet(rFormat.GetDoc()->GetAttrPool());
4416 if ( aSet.HasItem(RES_BACKGROUND) )
4417 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4418 else
4419 rFormat.SetFormatAttr(aSet);
4420 }
4421 }
4422 }
4423 wwULSpaceData aULData;
4424 GetPageULData(rSection, aULData);
4425 SetPageULSpaceItems(rFormat, aULData, rSection);
4426
4428
4429 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4430
4431 if (!(rSection.maSep.pgbApplyTo & 1))
4432 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4433 if (!(rSection.maSep.pgbApplyTo & 2))
4435
4436 mrReader.SetDocumentGrid(rFormat, rSection);
4437}
4438
4440{
4441 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4442 mrReader.m_xWDop->doptypography.m_f2on1;
4443
4444 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4445 UseOnPage eUse = eUseBase;
4446 if (!mrReader.m_xWDop->fFacingPages)
4448 if (!rSection.HasTitlePage())
4449 eUse |= UseOnPage::FirstShare;
4450
4451 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4452 if (rSection.mpPage)
4453 rSection.mpPage->WriteUseOn(eUse);
4454}
4455
4460static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4461 SwDoc &rDoc)
4462{
4463 /*
4464 If it's a table here, apply the pagebreak to the table
4465 properties, otherwise we add it to the para at this
4466 position
4467 */
4468 if (rIdx.GetNode().IsTableNode())
4469 {
4470 SwTable& rTable =
4471 rIdx.GetNode().GetTableNode()->GetTable();
4472 SwFrameFormat* pApply = rTable.GetFrameFormat();
4473 OSL_ENSURE(pApply, "impossible");
4474 if (pApply)
4475 pApply->SetFormatAttr(rPgDesc);
4476 }
4477 else
4478 {
4479 SwPaM aPage(rIdx);
4480 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4481 }
4482}
4483
4488 mySegIter const &rStart, bool bIgnoreCols)
4489{
4490 if (mrReader.m_bNewDoc && rIter == rStart)
4491 {
4492 rIter->mpPage =
4494 }
4495 else
4496 {
4497 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4499 nullptr, false);
4500 }
4501 OSL_ENSURE(rIter->mpPage, "no page!");
4502 if (!rIter->mpPage)
4503 return SwFormatPageDesc();
4504
4505 // Set page before hd/ft
4506 const wwSection *pPrevious = nullptr;
4507 if (rIter != rStart)
4508 pPrevious = &(*(rIter-1));
4509 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4510 SetUseOn(*rIter);
4511
4512 // Set hd/ft after set page
4513 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4514
4515 SwFormatPageDesc aRet(rIter->mpPage);
4516
4517 rIter->mpPage->SetFollow(rIter->mpPage);
4518
4519 if (rIter->PageRestartNo())
4520 aRet.SetNumOffset(rIter->PageStartAt());
4521
4522 ++mnDesc;
4523 return aRet;
4524}
4525
4527{
4528 mySegIter aEnd = maSegments.end();
4529 mySegIter aStart = maSegments.begin();
4530 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4531 {
4532 // If the section is of type "New column" (0x01), then simply insert a column break.
4533 // But only if there actually are columns on the page, otherwise a column break
4534 // seems to be handled like a page break by MSO.
4535 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4536 {
4537 SwPaM start( aIter->maStart );
4539 continue;
4540 }
4541
4542 mySegIter aNext = aIter+1;
4543 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4544
4545 // If two following sections are different in following properties, Word will interpret a continuous
4546 // section break between them as if it was a section break next page.
4547 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4548 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4549
4550 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4551 bool bInsertPageDesc = !bInsertSection;
4552 // HACK Force new pagedesc if left/right margins change, otherwise e.g. floating tables may be anchored improperly.
4553 if( aIter->maSep.dxaLeft != aPrev->maSep.dxaLeft || aIter->maSep.dxaRight != aPrev->maSep.dxaRight )
4554 bInsertPageDesc = true;
4555 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4556
4557 if (bInsertPageDesc)
4558 {
4559 /*
4560 If a cont section follows this section then we won't be
4561 creating a page desc with 2+ cols as we cannot host a one
4562 col section in a 2+ col pagedesc and make it look like
4563 word. But if the current section actually has columns then
4564 we are forced to insert a section here as well as a page
4565 descriptor.
4566 */
4567
4568 bool bIgnoreCols = bInsertSection;
4569 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4570 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4571 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4572 (aIter->IsLandScape() == aNext->IsLandScape()));
4573
4574 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4575 {
4576 bIgnoreCols = true;
4577 if ((aIter->NoCols() > 1) || bProtected)
4578 bInsertSection = true;
4579 }
4580
4581 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4582 if (!aDesc.GetPageDesc())
4583 continue;
4584
4585 // special case handling for odd/even section break
4586 // a) as before create a new page style for the section break
4587 // b) set Layout of generated page style to right/left ( according
4588 // to section break odd/even )
4589 // c) create a new style to follow the break page style
4590 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4591 {
4592 // SetSwFormatPageDesc calls some methods that could
4593 // modify aIter (e.g. wwSection ).
4594 // Since we call SetSwFormatPageDesc below to generate the
4595 // 'Following' style of the Break style, it is safer
4596 // to take a copy of the contents of aIter.
4597 wwSection aTmpSection = *aIter;
4598 // create a new following page style
4599 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4600 // restore any contents of aIter trashed by SetSwFormatPageDesc
4601 *aIter = aTmpSection;
4602
4603 // Handle the section break
4604 UseOnPage eUseOnPage = UseOnPage::Left;
4605 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4606 eUseOnPage = UseOnPage::Right;
4607
4608 // Keep the share flags.
4609 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4610 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4611 }
4612
4613 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4614 // unless it is needed to specify a page number.
4615 if (aIter != aStart || aDesc.GetNumOffset())
4616 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4617 }
4618
4619 SwTextNode* pTextNd = nullptr;
4620 if (bInsertSection)
4621 {
4622 // Start getting the bounds of this section
4623 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4624 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4625 if (aNext != aEnd)
4626 {
4627 aAnchor = aNext->maStart;
4628 aSectPaM.GetPoint()->Assign(aAnchor);
4629 aSectPaM.Move(fnMoveBackward);
4630 }
4631
4632 const SwPosition* pPos = aSectPaM.GetPoint();
4633 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4634 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4635 if (pTableNd)
4636 {
4637 pTextNd =
4640
4641 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4642 }
4643
4644 aSectPaM.SetMark();
4645
4646 aSectPaM.GetPoint()->Assign(aIter->maStart);
4647
4648 bool bHasOwnHdFt = false;
4649 /*
4650 In this nightmare scenario the continuous section has its own
4651 headers and footers so we will try and find a hard page break
4652 between here and the end of the section and put the headers and
4653 footers there.
4654 */
4655 if (!bInsertPageDesc)
4656 {
4657 bHasOwnHdFt =
4659 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4660 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4661 );
4662 }
4663 if (bHasOwnHdFt)
4664 {
4665 // #i40766# Need to cache the page descriptor in case there is
4666 // no page break in the section
4667 SwPageDesc *pOrig = aIter->mpPage;
4668 bool bFailed = true;
4669 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4670 if (aDesc.GetPageDesc())
4671 {
4672 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4673 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4674 for(; nStart <= nEnd; ++nStart)
4675 {
4676 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4677 if (!pNode)
4678 continue;
4679 if (sw::util::HasPageBreak(*pNode))
4680 {
4681 SwNodeIndex aIdx(*pNode);
4682 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4683 bFailed = false;
4684 break;
4685 }
4686 }
4687 }
4688 if(bFailed)
4689 {
4690 aIter->mpPage = pOrig;
4691 }
4692 }
4693
4694 // End getting the bounds of this section, quite a job eh?
4695 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4696 // The last section if continuous is always unbalanced
4697 if (pRet)
4698 {
4699 // Set the columns to be UnBalanced if that compatibility option is set
4700 if (mrReader.m_xWDop->fNoColumnBalance)
4702 else
4703 {
4704 // Otherwise set to unbalanced if the following section is
4705 // not continuous, (which also means that the last section
4706 // is unbalanced)
4707 if (aNext == aEnd || !aNext->IsContinuous())
4709 }
4710 }
4711 }
4712
4713 if (pTextNd)
4714 {
4715 SwPaM aTest(*pTextNd);
4717 pTextNd = nullptr;
4718 }
4719 }
4720}
4721
4723{
4724 auto aEnd = m_aTextNodes.rend();
4725 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4726 {
4727 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4728 SwTextNode* pTextNode = rListener.GetTextNode();
4729 rListener.StopListening(pTextNode);
4730
4731 SwPaM aTest(*pTextNode);
4733 }
4734 m_aTextNodes.clear();
4735}
4736
4738{
4739 m_aTextNodes.emplace(pTextNode, this);
4740}
4741
4743{
4744 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4745 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4746 if (it == m_aTextNodes.end())
4747 return;
4748 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4749 m_aTextNodes.erase(it);
4750}
4751
4753 : m_pTextNode(pTextNode)
4754{
4755 m_pTextNode->Add(this);
4756}
4757
4759{
4760 if (!m_pTextNode)
4761 return;
4763}
4764
4765void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4766{
4767 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4768 return;
4769 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4770 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4771 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4772 // indicates an underlying bug.
4773 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4774 removed(const_cast<SwModify*>(&rModify));
4775}
4776
4778{
4779 pTextNode->Remove(this);
4780 m_pTextNode = nullptr;
4781}
4782
4784{
4785 StopListening(pTextNode);
4786}
4787
4789{
4790 m_pOwner->remove_if_present(pTextNode);
4791}
4792
4794{
4795 if (!m_xWwFib->m_lcbCmds)
4796 return;
4797
4798 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4799 if (!bValidPos)
4800 return;
4801
4802 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4803
4804 if (!xRoot.is())
4805 return;
4806
4807 try
4808 {
4809 uno::Reference < io::XStream > xStream =
4810 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4811 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4812
4813 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4814 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4815 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4816 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4817 }
4818 catch (...)
4819 {
4820 }
4821}
4822
4824{
4825 std::vector<OUString> aDocVarStrings;
4826 std::vector<ww::bytes> aDocVarStringIds;
4827 std::vector<OUString> aDocValueStrings;
4828 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4829 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4830 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4831 if (m_bVer67) return;
4832
4833 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4834 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4835 uno::Reference<document::XDocumentProperties> xDocProps(
4836 xDPS->getDocumentProperties());
4837 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4838 uno::Reference<beans::XPropertyContainer> xUserDefinedProps =
4839 xDocProps->getUserDefinedProperties();
4840 OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null");
4841
4842 for(size_t i=0; i<aDocVarStrings.size(); i++)
4843 {
4844 const OUString &rName = aDocVarStrings[i];
4845 uno::Any aValue;
4846 aValue <<= rName;
4847 try {
4848 xUserDefinedProps->addProperty( rName,
4849 beans::PropertyAttribute::REMOVABLE,
4850 aValue );
4851 } catch (const uno::Exception &) {
4852 // ignore
4853 }
4854 }
4855}
4856
4861{
4862 if( !m_pStg )
4863 return;
4864
4865 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4866 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4867 uno::Reference<document::XDocumentProperties> xDocProps(
4868 xDPS->getDocumentProperties());
4869 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4870
4871 if (!xDocProps.is())
4872 return;
4873
4874 if ( m_xWwFib->m_fDot )
4875 {
4876 SfxMedium* pMedium = m_pDocShell->GetMedium();
4877 if ( pMedium )
4878 {
4879 const OUString& aName = pMedium->GetName();
4881 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4882 if ( !sTemplateURL.isEmpty() )
4883 xDocProps->setTemplateURL( sTemplateURL );
4884 }
4885 }
4886 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4887 {
4888 auto nCur = m_pTableStream->Tell();
4889 Sttb aSttb;
4890 // point at tgc record
4891 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4892 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4893 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4894 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4895 OUString aURL;
4896 // attempt to convert to url (won't work for obvious reasons on linux)
4897 if ( !sPath.isEmpty() )
4898 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4899 if (aURL.isEmpty())
4900 xDocProps->setTemplateURL( aURL );
4901 else
4902 xDocProps->setTemplateURL( sPath );
4903
4904 }
4905 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4906}
4907
4908static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4909{
4910 if ( !xPrjNameCache.is() )
4911 return;
4912
4913 INetURLObject aObj;
4914 aObj.SetURL( sTemplatePathOrURL );
4915 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4916 OUString aURL;
4917 if ( bIsURL )
4918 aURL = sTemplatePathOrURL;
4919 else
4920 {
4921 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4922 aObj.SetURL( aURL );
4923 }
4924 try
4925 {
4926 OUString templateNameWithExt = aObj.GetLastName();
4927 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4928 if ( nIndex != -1 )
4929 {
4930 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4931 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4932 }
4933 }
4934 catch( const uno::Exception& )
4935 {
4936 }
4937}
4938
4939namespace {
4940
4941class WW8Customizations
4942{
4943 SvStream* mpTableStream;
4944 WW8Fib mWw8Fib;
4945public:
4946 WW8Customizations( SvStream*, WW8Fib const & );
4947 void Import( SwDocShell* pShell );
4948};
4949
4950}
4951
4952WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4953{
4954}
4955
4956void WW8Customizations::Import( SwDocShell* pShell )
4957{
4958 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4959 return;
4960 try
4961 {
4962 Tcg aTCG;
4963 sal_uInt64 nCur = mpTableStream->Tell();
4964 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4965 {
4966 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4967 return;
4968 }
4969 bool bReadResult = aTCG.Read( *mpTableStream );
4970 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4971 if ( !bReadResult )
4972 {
4973 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4974 return;
4975 }
4976 aTCG.ImportCustomToolBar( *pShell );
4977 }
4978 catch(...)
4979 {
4980 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4981 }
4982}
4983
4984void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4985{
4986 if (m_bFuzzing)
4987 return;
4988
4989 SvtPathOptions aPathOpt;
4990 const OUString& aAddinPath = aPathOpt.GetAddinPath();
4991 uno::Sequence< OUString > sGlobalTemplates;
4992
4993 // first get the autoload addins in the directory STARTUP
4994 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
4995
4996 if( xSFA->isFolder( aAddinPath ) )
4997 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
4998
4999 for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) )
5000 {
5001 INetURLObject aObj;
5002 aObj.SetURL( rGlobalTemplate );
5003 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
5004 OUString aURL;
5005 if ( bIsURL )
5006 aURL = rGlobalTemplate;
5007 else
5008 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
5009 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
5010 continue; // don't try and read the same document as ourselves
5011
5012 tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE );
5013
5014 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5015 // Import vba via oox filter
5016 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5017 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
5018 // Read toolbars & menus
5019 tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument");
5020 refMainStream->SetEndian(SvStreamEndian::LITTLE);
5021 WW8Fib aWwFib( *refMainStream, 8 );
5022 tools::SvRef<SotStorageStream> xTableStream =
5023 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? OUString(SL::a1Table) : OUString(SL::a0Table), StreamMode::STD_READ);
5024
5025 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5026 {
5027 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5028 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5029 aGblCustomisations.Import( m_pDocShell );
5030 }
5031 }
5032}
5033
5035{
5037 if (m_bNewDoc && m_pStg && !pGloss)
5038 {
5039 // Initialize RDF metadata, to be able to add statements during the import.
5040 try
5041 {
5042 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5043 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
5044 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
5045 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5046 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
5047 uno::Reference<task::XInteractionHandler> xHandler;
5048 xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5049 }
5050 catch (const uno::Exception&)
5051 {
5052 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5053 }
5054 ReadDocInfo();
5055 }
5056
5057 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5058
5059 if (m_xWwFib->m_fReadOnlyRecommended)
5060 pFibData->setReadOnlyRecommended(true);
5061 else
5062 pFibData->setReadOnlyRecommended(false);
5063
5064 if (m_xWwFib->m_fWriteReservation)
5065 pFibData->setWriteReservation(true);
5066 else
5067 pFibData->setWriteReservation(false);
5068
5069 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5070
5072 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5073
5074 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5075
5076 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5077 {
5078 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5079 if (pDocShell)
5080 pDocShell->SetReadOnlyUI();
5081 }
5082
5083 m_pPaM = mpCursor.get();
5084
5086
5088
5089