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
207{
208 void lclIgnoreUString32(SvStream& rStrm)
209 {
210 sal_uInt32 nChars(0);
211 rStrm.ReadUInt32(nChars);
212 nChars *= 2;
213 rStrm.SeekRel(nChars);
214 }
215}
216
217void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
218{
219 // (0x01B8) HLINK
220 // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
221 constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001;
222 constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002;
223 constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014;
224 constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008;
225 constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080;
226 constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100;
227
228 //sal_uInt8 maGuidStdLink[ 16 ] ={
229 // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
230
231 sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
232 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
233
234 sal_uInt8 const aGuidFileMoniker[ 16 ] = {
235 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
236
237 sal_uInt8 aGuid[16];
238 sal_uInt32 nFlags(0);
239
240 rStrm.ReadBytes(aGuid, 16);
241 rStrm.SeekRel( 4 );
242 rStrm.ReadUInt32( nFlags );
243
244 std::unique_ptr< OUString > xLongName; // link / file name
245 std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
246 std::unique_ptr< OUString > xTextMark; // text mark
247
248 // description (ignore)
249 if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
250 lclIgnoreUString32( rStrm );
251
252 // target frame
253 if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
254 {
256 }
257
258 // UNC path
259 if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
260 {
261 // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the
262 // string field, including the null-terminating character.
263 sal_uInt32 nStrLen(0);
264 rStrm.ReadUInt32(nStrLen);
265 if (nStrLen)
266 {
267 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
268 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
269 lclGetAbsPath( *xLongName, 0 , pDocShell);
270 }
271 }
272 // file link or URL
273 else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
274 {
275 rStrm.ReadBytes(aGuid, 16);
276
277 if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
278 {
279 sal_uInt16 nLevel = 0; // counter for level to climb down in path
280 rStrm.ReadUInt16( nLevel );
281 // MS-OSHARED: An unsigned integer that specifies the number of
282 // ANSI characters in ansiPath, including the terminating NULL character
283 sal_uInt32 nUnits = 0;
284 rStrm.ReadUInt32(nUnits);
285 if (!nUnits)
286 xShortName.reset(new OUString);
287 else
288 {
289 OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1));
290 rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
291 xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage()));
292 }
293 rStrm.SeekRel( 24 );
294
295 sal_uInt32 nStrLen(0);
296 rStrm.ReadUInt32( nStrLen );
297 if( nStrLen )
298 {
299 nStrLen = 0;
300 rStrm.ReadUInt32( nStrLen );
301 nStrLen /= 2;
302 rStrm.SeekRel( 2 );
303 // MS-OSHARED: This array MUST not include a terminating NULL character.
304 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen)));
305 lclGetAbsPath( *xLongName, nLevel, pDocShell);
306 }
307 else
308 lclGetAbsPath( *xShortName, nLevel, pDocShell);
309 }
310 else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
311 {
312 // MS-OSHARED: An unsigned integer that specifies the size of this
313 // structure in bytes, excluding the size of the length field. The
314 // value of this field MUST be ... the byte size of the url
315 // field (including the terminating NULL character)
316 sal_uInt32 nStrLen(0);
317 rStrm.ReadUInt32( nStrLen );
318 nStrLen /= 2;
319 if (!nStrLen)
320 xLongName.reset(new OUString);
321 else
322 {
323 xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
324 rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
325 }
326 if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
327 lclGetAbsPath( *xLongName, 0 ,pDocShell);
328 }
329 else
330 {
331 SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
332 }
333 }
334
335 // text mark
336 if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
337 {
338 xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
339 }
340
341 if (!xLongName && xShortName)
342 xLongName.reset(new OUString(*xShortName));
343 else if (!xLongName && xTextMark)
344 xLongName.reset( new OUString );
345
346 if (xLongName)
347 {
348 if (xTextMark)
349 {
350 if (xLongName->isEmpty())
351 *xTextMark = xTextMark->replace('!', '.');
352 *xLongName += "#" + *xTextMark;
353 }
354 hlStr.hLinkAddr = *xLongName;
355 }
356}
357
358namespace {
359
360class BasicProjImportHelper
361{
362 SwDocShell& mrDocShell;
363 uno::Reference< uno::XComponentContext > mxCtx;
364public:
365 explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
367 {
368 }
369 bool import( const uno::Reference< io::XInputStream >& rxIn );
370 OUString getProjectName() const;
371};
372
373}
374
375bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
376{
377 bool bRet = false;
378 try
379 {
380 oox::ole::OleStorage root( mxCtx, rxIn, false );
381 oox::StorageRef vbaStg = root.openSubStorage( "Macros" , false );
382 if ( vbaStg )
383 {
384 oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
385 bRet = aVbaPrj.importVbaProject( *vbaStg );
386 }
387 }
388 catch( const uno::Exception& )
389 {
390 bRet = false;
391 }
392 return bRet;
393}
394
395OUString BasicProjImportHelper::getProjectName() const
396{
397 OUString sProjName( "Standard" );
398 uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
399 if ( xProps.is() )
400 {
401 try
402 {
403 uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW );
404 sProjName = xVBA->getProjectName();
405
406 }
407 catch( const uno::Exception& )
408 {
409 }
410 }
411 return sProjName;
412}
413
414namespace {
415
416class Sttb : public TBBase
417{
418struct SBBItem
419{
420 sal_uInt16 cchData;
421 OUString data;
422 SBBItem() : cchData(0){}
423};
424 sal_uInt16 m_fExtend;
425 sal_uInt16 m_cData;
426 sal_uInt16 m_cbExtra;
427
428 std::vector< SBBItem > m_dataItems;
429
430 Sttb(Sttb const&) = delete;
431 Sttb& operator=(Sttb const&) = delete;
432
433public:
434 Sttb();
435
436 bool Read(SvStream &rS) override;
437 OUString getStringAtIndex( sal_uInt32 );
438};
439
440}
441
442Sttb::Sttb()
443 : m_fExtend(0)
444 , m_cData(0)
445 , m_cbExtra(0)
446{
447}
448
449bool Sttb::Read( SvStream& rS )
450{
451 SAL_INFO("sw.ww8", "stream pos " << rS.Tell());
452 nOffSet = rS.Tell();
453 rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
454 if ( m_cData )
455 {
456 //if they are all going to be empty strings, how many could there be
457 const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16);
458 if (m_cData > nMaxPossibleRecords)
459 return false;
460 for ( sal_Int32 index = 0; index < m_cData; ++index )
461 {
462 SBBItem aItem;
463 rS.ReadUInt16( aItem.cchData );
464 aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
465 m_dataItems.push_back( aItem );
466 }
467 }
468 return true;
469}
470
471OUString
472Sttb::getStringAtIndex( sal_uInt32 index )
473{
474 OUString aRet;
475 if ( index < m_dataItems.size() )
476 aRet = m_dataItems[ index ].data;
477 return aRet;
478
479}
480
482 : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo,
483 rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages),
484 m_rReader(rRdr), m_pFallbackStream(nullptr)
485{
488}
489
491{
492 sal_uInt32 nFlags(0);
494 if (rOpt.IsMathType2Math())
495 nFlags |= OLE_MATHTYPE_2_STARMATH;
496 if (rOpt.IsExcel2Calc())
497 nFlags |= OLE_EXCEL_2_STARCALC;
498 if (rOpt.IsPowerPoint2Impress())
500 if (rOpt.IsWinWord2Writer())
501 nFlags |= OLE_WINWORD_2_STARWRITER;
502 return nFlags;
503}
504
505/*
506 * I would like to override the default OLE importing to add a test
507 * and conversion of OCX controls from their native OLE type into our
508 * native nonOLE Form Control Objects.
509 */
510// #i32596# - consider new parameter <_nCalledByGroup>
512 const Graphic& rGrf,
513 const tools::Rectangle& rBoundRect,
514 const tools::Rectangle& rVisArea,
515 const int _nCalledByGroup ) const
516{
517 // #i32596# - no import of OLE object, if it's inside a group.
518 // NOTE: This can be undone, if grouping of Writer fly frames is possible or
519 // if drawing OLE objects are allowed in Writer.
520 if ( _nCalledByGroup > 0 )
521 {
522 return nullptr;
523 }
524
526 OUString sStorageName;
528 uno::Reference < embed::XStorage > xDstStg;
529 if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
530 {
531 tools::SvRef<SotStorage> xSrc = xSrcStg->OpenSotStorage( sStorageName );
532 OSL_ENSURE(m_rReader.m_xFormImpl, "No Form Implementation!");
533 css::uno::Reference< css::drawing::XShape > xShape;
535 m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
536 {
538 }
539 else
540 {
541 ErrCode nError = ERRCODE_NONE;
543 *pSdrModel,
544 sStorageName,
545 xSrcStg,
546 xDstStg,
547 rGrf,
548 rBoundRect,
549 rVisArea,
550 pStData,
551 nError,
553 css::embed::Aspects::MSOLE_CONTENT,
555 }
556 }
557 return pRet;
558}
559
561{
562 OSL_ENSURE(!m_pFallbackStream,
563 "if you're recursive, you're broken");
566 aEscherBlipCache.clear();
567 pStData2 = nullptr;
568}
569
571{
574 m_aOldEscherBlipCache.clear();
575 m_pFallbackStream = nullptr;
576}
577
579{
580 return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
581}
582
584{
585 return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
586}
587
589{
590 if (m_xCtrlStck)
591 m_xCtrlStck->SetToggleAttrFlags(nFlags);
592}
593
595{
596 if (m_xCtrlStck)
597 m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
598}
599
601 DffObjData& rObjData,
602 SvxMSDffClientData& rData,
603 tools::Rectangle& rTextRect,
604 SdrObject* pObj1
605 )
606{
607 rtl::Reference<SdrObject> pObj = pObj1;
608 if( !rTextRect.IsEmpty() )
609 {
610 SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
611 std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
612
613 // fill Import Record with data
614 pImpRec->nShapeId = rObjData.nShapeId;
615 pImpRec->eShapeType = rObjData.eShapeType;
616
620 if( rObjData.bClientAnchor )
623 pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
624
628 if( rObjData.bClientData )
631 pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
632
633 pImpRec->nGroupShapeBooleanProperties = 0;
634
639 {
640 sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
641 auto nAvailableBytes = rSt.remainingSize();
642 if (nBytesLeft > nAvailableBytes)
643 {
644 SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
645 nBytesLeft = nAvailableBytes;
646 }
647 while( 5 < nBytesLeft )
648 {
649 sal_uInt16 nPID(0);
650 rSt.ReadUInt16(nPID);
651 sal_uInt32 nUDData(0);
652 rSt.ReadUInt32(nUDData);
653 if (!rSt.good())
654 break;
655 switch (nPID)
656 {
657 case 0x038F: pImpRec->nXAlign = nUDData; break;
658 case 0x0390:
659 pImpRec->nXRelTo = nUDData;
660 break;
661 case 0x0391: pImpRec->nYAlign = nUDData; break;
662 case 0x0392:
663 pImpRec->nYRelTo = nUDData;
664 break;
665 case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
666 case 0x0393:
667 // This seems to correspond to o:hrpct from .docx (even including
668 // the difference that it's in 0.1% even though the .docx spec
669 // says it's in 1%).
670 pImpRec->relativeHorizontalWidth = nUDData;
671 break;
672 case 0x0394:
673 // And this is really just a guess, but a mere presence of this
674 // flag makes a horizontal rule be as wide as the page (unless
675 // overridden by something), so it probably matches o:hr from .docx.
676 pImpRec->isHorizontalRule = true;
677 break;
678 }
679 nBytesLeft -= 6;
680 }
681 }
682
683 // Text Frame also Title or Outline
684 sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
685 if( nTextId )
686 {
688
689 // Originally anything that as a mso_sptTextBox was created as a
690 // textbox, this was changed to be created as a simple
691 // rect to keep impress happy. For the rest of us we'd like to turn
692 // it back into a textbox again.
693 bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox);
694 if (!bIsSimpleDrawingTextBox)
695 {
696 // Either
697 // a) it's a simple text object or
698 // b) it's a rectangle with text and square wrapping.
699 bIsSimpleDrawingTextBox =
700 (
701 (pImpRec->eShapeType == mso_sptTextSimple) ||
702 (
703 (pImpRec->eShapeType == mso_sptRectangle)
704 && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
705 )
706 );
707 }
708
709 // Distance of Textbox to its surrounding Autoshape
710 sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440);
711 sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 );
712 sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 );
713 sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 );
714
715 ScaleEmu( nTextLeft );
716 ScaleEmu( nTextRight );
717 ScaleEmu( nTextTop );
718 ScaleEmu( nTextBottom );
719
720 Degree100 nTextRotationAngle;
721 bool bVerticalText = false;
723 {
724 MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
725 DFF_Prop_txflTextFlow, 0) & 0xFFFF);
726 switch( eTextFlow )
727 {
728 case mso_txflBtoT:
729 nTextRotationAngle = 9000_deg100;
730 break;
731 case mso_txflVertN:
732 case mso_txflTtoBN:
733 nTextRotationAngle = 27000_deg100;
734 break;
735 case mso_txflTtoBA:
736 bVerticalText = true;
737 break;
738 case mso_txflHorzA:
739 bVerticalText = true;
740 nTextRotationAngle = 9000_deg100;
741 break;
742 case mso_txflHorzN:
743 default :
744 break;
745 }
746 }
747
748 if (nTextRotationAngle)
749 {
750 if (nTextRotationAngle == 9000_deg100)
751 {
752 tools::Long nWidth = rTextRect.GetWidth();
753 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
754 rTextRect.SetBottom( rTextRect.Top() + nWidth );
755
756 sal_Int32 nOldTextLeft = nTextLeft;
757 sal_Int32 nOldTextRight = nTextRight;
758 sal_Int32 nOldTextTop = nTextTop;
759 sal_Int32 nOldTextBottom = nTextBottom;
760
761 nTextLeft = nOldTextBottom;
762 nTextRight = nOldTextTop;
763 nTextTop = nOldTextLeft;
764 nTextBottom = nOldTextRight;
765 }
766 else if (nTextRotationAngle == 27000_deg100)
767 {
768 tools::Long nWidth = rTextRect.GetWidth();
769 rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
770 rTextRect.SetBottom( rTextRect.Top() + nWidth );
771
772 sal_Int32 nOldTextLeft = nTextLeft;
773 sal_Int32 nOldTextRight = nTextRight;
774 sal_Int32 nOldTextTop = nTextTop;
775 sal_Int32 nOldTextBottom = nTextBottom;
776
777 nTextLeft = nOldTextTop;
778 nTextRight = nOldTextBottom;
779 nTextTop = nOldTextRight;
780 nTextBottom = nOldTextLeft;
781 }
782 }
783
784 if (bIsSimpleDrawingTextBox)
785 {
786 pObj = new SdrRectObj(
787 *pSdrModel,
788 SdrObjKind::Text,
789 rTextRect);
790 }
791
792 // The vertical paragraph justification are contained within the
793 // BoundRect so calculate it here
794 tools::Rectangle aNewRect(rTextRect);
795 aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
796 aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
797
798 // Only if it's a simple Textbox, Writer can replace the Object
799 // with a Frame, else
800 if( bIsSimpleDrawingTextBox )
801 {
802 std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
803 std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
804
805 SvxMSDffShapeInfos_ById::const_iterator const it =
806 GetShapeInfos()->find(xTmpRec);
807 if (it != GetShapeInfos()->end())
808 {
809 SvxMSDffShapeInfo& rInfo = **it;
810 pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
811 }
812
813 ApplyAttributes(rSt, aSet, rObjData);
814 }
815
817 {
818 aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
820 aNewRect.Bottom() - aNewRect.Top() ) );
822 aNewRect.Right() - aNewRect.Left() ) );
823 }
824 else
825 {
826 aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
827 aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
828 }
829
831 {
832 case mso_wrapNone :
833 aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
834 pImpRec->bAutoWidth = true;
835 break;
836 case mso_wrapByPoints :
837 aSet.Put( makeSdrTextContourFrameItem( true ) );
838 break;
839 default:
840 ;
841 }
842
843 // Set distances on Textbox's margins
844 aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
845 aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
846 aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
847 aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
848 pImpRec->nDxTextLeft = nTextLeft;
849 pImpRec->nDyTextTop = nTextTop;
850 pImpRec->nDxTextRight = nTextRight;
851 pImpRec->nDyTextBottom = nTextBottom;
852
853 // Taking the correct default (which is mso_anchorTop)
854 sal_uInt32 eTextAnchor =
856
857 SdrTextVertAdjust eTVA = bVerticalText
860 SdrTextHorzAdjust eTHA = bVerticalText
863
864 switch( eTextAnchor )
865 {
866 case mso_anchorTop:
867 {
868 if ( bVerticalText )
870 else
872 }
873 break;
875 {
876 if ( bVerticalText )
878 else
880 }
881 break;
882 case mso_anchorMiddle:
883 break;
885 break;
886 case mso_anchorBottom:
887 {
888 if ( bVerticalText )
890 else
892 }
893 break;
895 {
896 if ( bVerticalText )
898 else
900 }
901 break;
902 default:
903 ;
904 }
905
906 aSet.Put( SdrTextVertAdjustItem( eTVA ) );
907 aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
908
909 if (pObj != nullptr)
910 {
911 pObj->SetMergedItemSet(aSet);
912
913 if (bVerticalText)
914 {
915 SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
916 if (pTextObj)
917 pTextObj->SetVerticalWriting(true);
918 }
919
920 if ( bIsSimpleDrawingTextBox )
921 {
922 if ( nTextRotationAngle )
923 {
924 tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
925 rTextRect.GetWidth() : rTextRect.GetHeight();
926 nMinWH /= 2;
927 Point aPivot(rTextRect.TopLeft());
928 aPivot.AdjustX(nMinWH );
929 aPivot.AdjustY(nMinWH );
930 pObj->NbcRotate(aPivot, nTextRotationAngle);
931 }
932 }
933
934 if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
935 {
936 SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
937 if (pCustomShape)
938 {
939 double fExtraTextRotation = 0.0;
941 { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
942 fExtraTextRotation = -mnFix16Angle.get();
943 }
944 if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text
945 { // remains unchanged, so we have to take back the flipping here
946 fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped.
947 }
948 fExtraTextRotation += nTextRotationAngle.get();
949 if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
950 {
951 fExtraTextRotation /= 100.0;
953 css::beans::PropertyValue aPropVal;
954 aPropVal.Name = "TextRotateAngle";
955 aPropVal.Value <<= fExtraTextRotation;
956 aGeometryItem.SetPropertyValue( aPropVal );
957 pCustomShape->SetMergedItem( aGeometryItem );
958 }
959 }
960 }
961 else if ( mnFix16Angle )
962 {
963 // rotate text with shape ?
964 pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
965 }
966 }
967 }
968 else if( !pObj )
969 {
970 // simple rectangular objects are ignored by ImportObj() :-(
971 // this is OK for Draw but not for Calc and Writer
972 // cause here these objects have a default border
973 pObj = new SdrRectObj(
974 *pSdrModel,
975 rTextRect);
976
978 ApplyAttributes( rSt, aSet, rObjData );
979
980 SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
981 if( SfxItemState::DEFAULT == eState )
982 aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
983 pObj->SetMergedItemSet(aSet);
984 }
985
986 // Means that fBehindDocument is set
987 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
988 pImpRec->bDrawHell = true;
989 else
990 pImpRec->bDrawHell = false;
991 if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
992 pImpRec->bHidden = true;
993 pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
994
995 if ( nTextId )
996 {
997 pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
998 pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
999 }
1000
1001 pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
1003 pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
1005 pImpRec->nDxWrapDistRight
1008 pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
1010 // 16.16 fraction times total image width or height, as appropriate.
1011
1013 {
1014 pImpRec->pWrapPolygon.reset();
1015
1016 sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0);
1017 rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
1018 bool bOk = false;
1019 if (nNumElemVert && (nElemSizeVert == 8 || nElemSizeVert == 4))
1020 {
1021 //check if there is enough data in the file to make the
1022 //record sane
1023 // coverity[tainted_data : FALSE] - nElemSizeVert is either 8 or 4 so it has been sanitized
1024 bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert;
1025 }
1026 if (bOk)
1027 {
1028 pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
1029 for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
1030 {
1031 sal_Int32 nX(0), nY(0);
1032 if (nElemSizeVert == 8)
1033 rSt.ReadInt32( nX ).ReadInt32( nY );
1034 else
1035 {
1036 sal_Int16 nSmallX(0), nSmallY(0);
1037 rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
1038 nX = nSmallX;
1039 nY = nSmallY;
1040 }
1041 (*(pImpRec->pWrapPolygon))[i].setX( nX );
1042 (*(pImpRec->pWrapPolygon))[i].setY( nY );
1043 }
1044 }
1045 }
1046
1047 pImpRec->nCropFromTop = GetPropertyValue(
1049 pImpRec->nCropFromBottom = GetPropertyValue(
1051 pImpRec->nCropFromLeft = GetPropertyValue(
1053 pImpRec->nCropFromRight = GetPropertyValue(
1055
1056 sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
1057
1059 pImpRec->eShapeType == mso_sptPictureFrame )
1060 {
1061 nLineFlags &= ~0x08;
1062 }
1063
1064 pImpRec->eLineStyle = (nLineFlags & 8)
1065 ? static_cast<MSO_LineStyle>(GetPropertyValue(
1068 : MSO_LineStyle(USHRT_MAX);
1069 pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
1071
1072 pImpRec->nFlags = rObjData.nSpFlags;
1073
1074 if( pImpRec->nShapeId )
1075 {
1076 auto nShapeId = pImpRec->nShapeId;
1077 auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16)
1078 + pImpRec->aTextId.nSequence;
1079 // Complement Import Record List
1080 pImpRec->pObj = pObj;
1081 rImportData.insert(std::move(pImpRec));
1082
1083 // Complement entry in Z Order List with a pointer to this Object
1084 // Only store objects which are not deep inside the tree
1085 if( ( rObjData.nCalledByGroup == 0 )
1086 ||
1087 ( (rObjData.nSpFlags & ShapeFlag::Group)
1088 && (rObjData.nCalledByGroup < 2) )
1089 )
1090 {
1091 StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
1092 }
1093 }
1094 else
1095 pImpRec.reset();
1096 }
1097
1098 sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
1099 if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
1100 {
1101 SvMemoryStream aMemStream;
1102 struct HyperLinksTable hlStr;
1103 aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
1104
1105 // copy from DFF stream to memory stream
1106 std::vector< sal_uInt8 > aBuffer( nBufferSize );
1107 if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
1108 {
1109 aMemStream.WriteBytes(aBuffer.data(), nBufferSize);
1110 sal_uInt64 nStreamSize = aMemStream.TellEnd();
1111 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
1112 bool bRet = 4 <= nStreamSize;
1113 sal_uInt16 nRawRecId,nRawRecSize;
1114 if( bRet )
1115 aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize );
1116 SwDocShell* pDocShell = m_rReader.m_pDocShell;
1117 if (pDocShell)
1118 {
1119 m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
1120 }
1121 }
1122
1123 if (pObj && !hlStr.hLinkAddr.isEmpty())
1124 {
1125 SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
1126 if( pInfo )
1127 {
1128 pInfo->SetShapeId( rObjData.nShapeId );
1129 pInfo->SetHlink( hlStr.hLinkAddr );
1130 if (!hlStr.tarFrame.isEmpty())
1131 pInfo->SetTarFrame( hlStr.tarFrame );
1132 OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt );
1133 if (!aNameStr.isEmpty())
1134 pInfo->SetName( aNameStr );
1135 }
1136 }
1137 }
1138
1139 return pObj;
1140}
1141
1145void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
1146{
1147 if (nLen < 0)
1148 {
1149 m_bCpxStyle = false;
1150 return;
1151 }
1152 sal_uInt16 nColl = 0;
1153 if (m_xWwFib->GetFIBVersion() <= ww::eWW2)
1154 nColl = *pData;
1155 else
1156 nColl = SVBT16ToUInt16(pData);
1157 if (nColl < m_vColl.size())
1158 {
1160 m_bCpxStyle = true;
1161 }
1162}
1163
1167void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
1168{
1169}
1170
1175 const SfxPoolItem& rAttr)
1176{
1177 OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put"
1178 "fields into the control stack");
1179 OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put"
1180 "input fields into the control stack");
1181 OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put"
1182 "annotations into the control stack");
1183 OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put"
1184 "redlines into the control stack");
1185 SwFltControlStack::NewAttr(rPos, rAttr);
1186}
1187
1189 bool bTstEnd, tools::Long nHand, bool )
1190{
1191 SwFltStackEntry *pRet = nullptr;
1192 // Doing a textbox, and using the control stack only as a temporary
1193 // collection point for properties which will are not to be set into
1194 // the real document
1195 if (m_rReader.m_xPlcxMan && m_rReader.m_xPlcxMan->GetDoingDrawTextBox())
1196 {
1197 size_t nCnt = size();
1198 for (size_t i=0; i < nCnt; ++i)
1199 {
1200 SwFltStackEntry& rEntry = (*this)[i];
1201 if (nAttrId == rEntry.m_pAttr->Which())
1202 {
1204 --nCnt;
1205 }
1206 }
1207 }
1208 else // Normal case, set the attribute into the document
1209 pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
1210 return pRet;
1211}
1212
1214{
1216 "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
1217
1218 SvxAdjust eAdj = rFormat.GetNumAdjust();
1219 tools::Long nReverseListIndented;
1220 if (eAdj == SvxAdjust::Right)
1221 nReverseListIndented = -rFormat.GetCharTextDistance();
1222 else if (eAdj == SvxAdjust::Center)
1223 nReverseListIndented = rFormat.GetFirstLineOffset()/2;
1224 else
1225 nReverseListIndented = rFormat.GetFirstLineOffset();
1226 return nReverseListIndented;
1227}
1228
1230 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 // rely on default for HYPHENATE_URLS=false
1895
1896 // COMPATIBILITY FLAGS END
1897
1898 // Import magic doptypography information, if it's there
1899 if (m_xWwFib->m_nFib > 105)
1900 ImportDopTypography(m_xWDop->doptypography);
1901
1902 // disable form design mode to be able to use imported controls directly
1903 // #i31239# always disable form design mode, not only in protected docs
1904 uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
1905 if (xDocProps.is())
1906 {
1907 uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
1908 if (xInfo.is())
1909 {
1910 if (xInfo->hasPropertyByName("ApplyFormDesignMode"))
1911 xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::Any(false));
1912 }
1913
1914 // for the benefit of DOCX - if this is ever saved in that format.
1915 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1916 uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
1917 { "name", uno::Any(OUString("compatibilityMode")) },
1918 { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
1919 { "val", uno::Any(OUString("11")) } //11: Use features specified in MS-DOC.
1920 }));
1921
1922 uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
1923 { "compatSetting", uno::Any(aCompatSetting) }
1924 }));
1925
1926 aGrabBag["CompatSettings"] <<= aValue;
1927 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1928 }
1929
1930 // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
1931 // Treat comments-only like read-only since Writer has no support for that.
1932 // Still allow editing of form fields, without requiring the password.
1933 // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
1934 if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
1936 else if ( xDocProps.is() )
1937 {
1938 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
1939 aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc;
1940 xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
1941 }
1942
1944 if (rOpt.IsUseEnhancedFields())
1946
1947 if (m_xWDop->iGutterPos)
1948 {
1950 }
1951}
1952
1954{
1955 switch (rTypo.m_iLevelOfKinsoku)
1956 {
1957 case 2: // custom
1958 {
1959 i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
1960 OUString(+rTypo.m_rgxchLPunct));
1961 // unary + makes sure not to accidentally call the
1962 // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
1963 // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
1964 // prefix leading up to the first NUL
1966 aForbidden);
1967 // Obviously cannot set the standard level 1 for japanese, so
1968 // bail out now while we can.
1969 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
1970 return;
1971 }
1972 break;
1973 default:
1974 break;
1975 }
1976
1977 /*
1978 This MS hack means that level 2 of japanese is not in operation, so we put
1979 in what we know are the MS defaults, there is a complementary reverse
1980 hack in the writer. Its our default as well, but we can set it anyway
1981 as a flag for later.
1982 */
1983 if (!rTypo.m_reserved2)
1984 {
1985 i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
1988 }
1989
1992}
1993
1998 mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
1999 mxOldStck(std::move(pRdr->m_xCtrlStck)),
2000 mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
2001 mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
2002 mxOldPlcxMan(pRdr->m_xPlcxMan),
2003 mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
2004 mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
2005 mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
2006 mpPrevNumRule(pRdr->m_pPrevNumRule),
2007 mxTableDesc(std::move(pRdr->m_xTableDesc)),
2008 mnInTable(pRdr->m_nInTable),
2009 mnCurrentColl(pRdr->m_nCurrentColl),
2010 mcSymbol(pRdr->m_cSymbol),
2011 mbIgnoreText(pRdr->m_bIgnoreText),
2012 mbSymbol(pRdr->m_bSymbol),
2013 mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
2014 mbTxbxFlySection(pRdr->m_bTxbxFlySection),
2015 mbAnl(pRdr->m_bAnl),
2016 mbInHyperlink(pRdr->m_bInHyperlink),
2017 mbPgSecBreak(pRdr->m_bPgSecBreak),
2018 mbWasParaEnd(pRdr->m_bWasParaEnd),
2019 mbHasBorder(pRdr->m_bHasBorder),
2020 mbFirstPara(pRdr->m_bFirstPara)
2021{
2022 pRdr->m_bSymbol = false;
2023 pRdr->m_bHdFtFootnoteEdn = true;
2024 pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
2025 = pRdr->m_bHasBorder = false;
2026 pRdr->m_bFirstPara = true;
2027 pRdr->m_nInTable = 0;
2028 pRdr->m_pPreviousNumPaM = nullptr;
2029 pRdr->m_pPrevNumRule = nullptr;
2030 pRdr->m_nCurrentColl = 0;
2031
2032 pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
2033 *pRdr));
2034
2035 pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
2036
2037 pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
2038
2039 // Save the attribute manager: we need this as the newly created PLCFx Manager
2040 // access the same FKPs as the old one and their Start-End position changes.
2041 if (pRdr->m_xPlcxMan)
2042 pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
2043
2044 if (nStartCp != -1)
2045 {
2046 pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
2047 mxOldPlcxMan->GetManType(), nStartCp);
2048 }
2049
2050 maOldApos.push_back(false);
2051 maOldApos.swap(pRdr->m_aApos);
2052 maOldFieldStack.swap(pRdr->m_aFieldStack);
2053}
2054
2056{
2057 pRdr->m_xWFlyPara = std::move(mpWFlyPara);
2058 pRdr->m_xSFlyPara = std::move(mpSFlyPara);
2061 pRdr->m_xTableDesc = std::move(mxTableDesc);
2062 pRdr->m_cSymbol = mcSymbol;
2063 pRdr->m_bSymbol = mbSymbol;
2067 pRdr->m_nInTable = mnInTable;
2068 pRdr->m_bAnl = mbAnl;
2073 pRdr->m_bHasBorder = mbHasBorder;
2074 pRdr->m_bFirstPara = mbFirstPara;
2075
2076 // Close all attributes as attributes could be created that extend the Fly
2077 pRdr->DeleteCtrlStack();
2078 pRdr->m_xCtrlStck = std::move(mxOldStck);
2079
2080 pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
2081
2082 // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2083 // place, or somewhere close if that place got destroyed
2084 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
2085 pRdr->m_oLastAnchorPos.reset();
2086
2087 pRdr->m_xRedlineStack = std::move(mxOldRedlines);
2088
2089 if (xLastAnchorCursor)
2090 pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2091
2092 pRdr->DeleteAnchorStack();
2093 pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
2094
2095 *pRdr->m_pPaM->GetPoint() = GetStartPos();
2096
2097 if (mxOldPlcxMan != pRdr->m_xPlcxMan)
2098 pRdr->m_xPlcxMan = mxOldPlcxMan;
2099 if (pRdr->m_xPlcxMan)
2100 pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
2101 pRdr->m_aApos.swap(maOldApos);
2102 pRdr->m_aFieldStack.swap(maOldFieldStack);
2103}
2104
2106 WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
2107{
2108 if (nStartCp < 0 || nLen < 0)
2109 return;
2110
2111 // Saves Flags (amongst other things) and resets them
2112 WW8ReaderSave aSave( this );
2113
2114 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2115
2116 // Read Text for Header, Footer or Footnote
2117 ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
2118 aSave.Restore( this );
2119}
2120
2125{
2126 WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
2127 if (!pSD)
2128 return 0;
2129
2130 const void* pData = pSD->GetData();
2131 if (!pData)
2132 return 0;
2133
2134 OUString sAuthor;
2135 OUString sInitials;
2136 if( m_bVer67 )
2137 {
2138 const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
2139 const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
2140 if (pA)
2141 sAuthor = *pA;
2142 else
2143 {
2144 const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
2145 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2146 sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
2147 }
2148 }
2149 else
2150 {
2151 const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
2152 {
2153 const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
2154 SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
2155 OUStringBuffer aBuf;
2156 aBuf.setLength(nLen);
2157 for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
2158 aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
2159 sInitials = aBuf.makeStringAndClear();
2160 }
2161
2162 if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
2163 sAuthor = *pA;
2164 else
2165 sAuthor = sInitials;
2166 }
2167
2168 sal_uInt32 nDateTime = 0;
2169
2170 if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
2171 {
2172 sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
2173 if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
2174 nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
2175 }
2176
2177 DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
2178
2179 OUString sText;
2181 pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
2182
2184 SwPostItField aPostIt(
2186 sText, sInitials, OUString(), aDate );
2187 aPostIt.SetTextObject(std::move(pOutliner));
2188
2189 SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
2190 m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
2192 m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
2193 // If this is a range, make sure that it ends after the just inserted character, not before it.
2195
2196 return 0;
2197}
2198
2200 SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
2201{
2202 const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
2203 OSL_ENSURE(pSttIdx, "impossible");
2204 if (!pSttIdx)
2205 return;
2206
2207 SwPosition aTmpPos(*m_pPaM->GetPoint());
2208
2209 m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
2210
2211 // tdf#122425: Explicitly remove borders and spacing
2214
2215 SwFlyFrameFormat* pFrame
2216 = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
2217
2218 SwFormatAnchor aAnch( pFrame->GetAnchor() );
2219 aAnch.SetType( RndStdIds::FLY_AT_PARA );
2220 pFrame->SetFormatAttr( aAnch );
2222 SwFrameSize eFrameSize = SwFrameSize::Minimum;
2223 if( eFrameSize != aSz.GetWidthSizeType() )
2224 aSz.SetWidthSizeType( eFrameSize );
2225 pFrame->SetFormatAttr(aSz);
2226 pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
2227 pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
2228
2229 // #i43427# - send frame for header/footer into background.
2230 pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
2231 SdrObject* pFrameObj = CreateContactObject( pFrame );
2232 OSL_ENSURE( pFrameObj,
2233 "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
2234 if ( pFrameObj )
2235 {
2236 pFrameObj->SetOrdNum( 0 );
2237 }
2238 MoveInsideFly(pFrame);
2239
2240 const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
2241
2242 Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
2243
2244 MoveOutsideFly(pFrame, aTmpPos);
2245}
2246
2247void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
2248{
2249 const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
2250 if (!pSttIdx)
2251 return;
2252
2253 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
2254
2255 Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
2256
2257 *m_pPaM->GetPoint() = aTmpPos;
2258}
2259
2261{
2262 // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
2263 return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
2264}
2265
2267 int nSect)
2268{
2269 if (m_xHdFt)
2270 {
2271 WW8_CP nStart, nLen;
2272 sal_uInt8 nNumber = 5;
2273
2274 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2275 {
2276 if (nI & nWhichItems)
2277 {
2278 bool bOk = true;
2279 if( m_bVer67 )
2280 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
2281 else
2282 {
2283 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2284 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2285 }
2286
2287 if (bOk)
2288 return true;
2289 }
2290 }
2291 }
2292 return false;
2293}
2294
2295void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
2296 const wwSection &rSection)
2297{
2298 sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
2299 SwPageDesc *pPD = rSection.mpPage;
2300
2301 if( !m_xHdFt )
2302 return;
2303
2304 WW8_CP nStart, nLen;
2305 sal_uInt8 nNumber = 5;
2306
2307 // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
2308 // corresponding to bit fields in grpfIhdt indicating which
2309 // header/footer(s) are present in this section
2310 for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
2311 {
2312 if (nI & grpfIhdt)
2313 {
2314 bool bOk = true;
2315 if( m_bVer67 )
2316 bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
2317 else
2318 {
2319 m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
2320 bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
2321 }
2322
2323 bool bUseLeft
2324 = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
2325 bool bUseFirst
2326 = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
2327
2328 // If we are loading a first-page header/footer which is not
2329 // actually enabled in this section (it still needs to be
2330 // loaded as it may be inherited by a later section)
2331 bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
2332
2333 bool bFooter
2334 = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
2335
2336 SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
2337 : bUseFirst ? pPD->GetFirstMaster()
2338 : pPD->GetMaster();
2339
2340 SwFrameFormat* pHdFtFormat;
2341 // If we have empty first page header and footer.
2342 bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
2343 if (bFooter)
2344 {
2345 m_bIsFooter = true;
2346 //#i17196# Cannot have left without right
2347 if (!bDisabledFirst
2348 && !pPD->GetMaster().GetFooter().GetFooterFormat())
2350 if (bUseLeft)
2351 pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
2352 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2354 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
2355 }
2356 else
2357 {
2358 m_bIsHeader = true;
2359 //#i17196# Cannot have left without right
2360 if (!bDisabledFirst
2361 && !pPD->GetMaster().GetHeader().GetHeaderFormat())
2363 if (bUseLeft)
2364 pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
2365 if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
2367 pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
2368 }
2369
2370 if (bOk)
2371 {
2372 bool bHackRequired = false;
2373 if (m_bIsHeader && rSection.IsFixedHeightHeader())
2374 bHackRequired = true;
2375 else if (m_bIsFooter && rSection.IsFixedHeightFooter())
2376 bHackRequired = true;
2377
2378 if (bHackRequired)
2379 {
2380 Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
2381 static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
2382 }
2383 else
2384 Read_HdFtText(nStart, nLen, pHdFtFormat);
2385 }
2386 else if (pPrev)
2387 CopyPageDescHdFt(pPrev, pPD, nI);
2388
2389 m_bIsHeader = m_bIsFooter = false;
2390 }
2391 }
2392}
2393
2395{
2396 return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
2397}
2398
2399void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
2400 const wwSection *pPrevious)
2401{
2402 // Header/Footer not present
2403 if (!rSection.maSep.grpfIhdt)
2404 return;
2405
2406 OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
2407 if (rSection.mpPage)
2408 {
2409 mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
2410 rSection);
2411 }
2412
2413 // Header/Footer - Update Index
2414 // So that the index is still valid later on
2415 if (mrReader.m_xHdFt)
2416 mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
2417
2418}
2419
2421{
2423
2424 const SwNumRule* pRule = nullptr;
2425
2426 if (pText != nullptr)
2427 pRule = sw::util::GetNumRuleFromTextNode(*pText);
2428
2429 // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
2430 // set it as a separate paragraph property, just like we do for DOCX.
2431 // This is only being used for numbering currently, so limiting to that context.
2432 if (pRule)
2433 {
2436 items(m_pPaM->GetDoc().GetAttrPool());
2437
2438 SfxWhichIter aIter(items);
2439 for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
2440 {
2441 const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
2442 if (pItem)
2443 items.Put(*pItem);
2444 }
2446 item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
2447 pText->SetAttr(item);
2448 }
2449
2450 if (
2451 pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
2453 )
2454 {
2455 // If after spacing is set to auto, set the after space to 0
2456 if (m_bParaAutoAfter)
2458
2459 // If the previous textnode had numbering and
2460 // and before spacing is set to auto, set before space to 0
2463
2464 // If the previous numbering rule was different we need
2465 // to insert a space after the previous paragraph
2466 if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
2468
2469 // cache current paragraph
2471 {
2472 delete m_pPreviousNumPaM;
2473 m_pPreviousNumPaM = nullptr;
2474 }
2475
2477 m_pPrevNumRule = pRule;
2478 }
2479 else if(!pRule && m_pPreviousNumPaM)
2480 {
2481 // If the previous paragraph has numbering but the current one does not
2482 // we need to add a space after the previous paragraph
2484 delete m_pPreviousNumPaM;
2485 m_pPreviousNumPaM = nullptr;
2486 m_pPrevNumRule = nullptr;
2487 }
2488 else
2489 {
2490 // clear paragraph cache
2492 {
2493 delete m_pPreviousNumPaM;
2494 m_pPreviousNumPaM = nullptr;
2495 }
2496 m_pPrevNumRule = pRule;
2497 }
2498
2499 // If this is the first paragraph in the document and
2500 // Auto-spacing before paragraph is set,
2501 // set the upper spacing value to 0
2502 if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
2504
2505 m_bFirstPara = false;
2506
2508
2509 // We can flush all anchored graphics at the end of a paragraph.
2510 m_xAnchorStck->Flush();
2511}
2512
2513bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
2514{
2515 bool bRet = false;
2516 const SwPosition* pSpacingPos = rMyPam.GetPoint();
2517
2518 const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
2519
2520 if(pULSpaceItem != nullptr)
2521 {
2522 SvxULSpaceItem aUL(*pULSpaceItem);
2523
2524 if(bIsUpper)
2525 aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
2526 else
2527 aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
2528
2529 const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
2530 rMyPam.GetPoint()->SetContent(0);
2531 m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
2532 rMyPam.GetPoint()->SetContent(nEnd);
2533 m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
2534 bRet = true;
2535 }
2536 return bRet;
2537}
2538
2540{
2541 return SetSpacing(rMyPam, nSpace, false);
2542}
2543
2545{
2546 return SetSpacing(rMyPam, nSpace, true);
2547}
2548
2549sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
2550{
2551 if (m_bVer67)
2552 return NS_sprm::v6::sprmPTtp;
2554}
2555
2557{
2558 // Frame/Table/Anl
2559 if (m_bAnl)
2560 StopAllAnl(); // -> bAnl = false
2561
2562 while(m_aApos.size() > 1)
2563 {
2564 StopTable();
2565 m_aApos.pop_back();
2566 --m_nInTable;
2567 if (m_aApos[m_nInTable])
2568 StopApo();
2569 }
2570
2571 if (m_aApos[0])
2572 StopApo();
2573
2574 OSL_ENSURE(!m_nInTable, "unclosed table!");
2575}
2576
2578{
2579 // This is ww8 version of the code deciding if the table needs to be
2580 // in a floating frame.
2581 // For OOXML code, see SectionPropertyMap::FloatingTableConversion in
2582 // writerfilter/source/dmapper/PropertyMap.cxx
2583 // The two should do ~same, so if you make changes here, please check
2584 // that the other is in sync.
2585
2586 // Note that this is just a list of heuristics till sw core can have a
2587 // table that is floating and can span over multiple pages at the same
2588 // time.
2589
2590 // If the floating table is in a header or footer, then it won't be a
2591 // multi-page one, so can always do the conversion.
2592 if (m_bIsHeader || m_bIsFooter)
2593 {
2594 return true;
2595 }
2596
2597 bool bResult = true;
2598
2600 if (nullptr != aRes.pSprm)
2601 {
2602 bResult = false;
2603 WW8TabBandDesc aDesc;
2604 aDesc.ReadDef(false, aRes.pSprm, aRes.nRemainingData);
2605 int nTextAreaWidth = m_aSectionManager.GetTextAreaWidth();
2606 int nTableWidth = aDesc.nCenter[aDesc.nWwCols] - aDesc.nCenter[0];
2607
2608 // It seems Word has a limit here, so that in case the table width is quite
2609 // close to the text area width, then it won't perform a wrapping, even in
2610 // case the content (e.g. an empty paragraph) would fit. The magic constant
2611 // here represents this limit.
2612 const int nMagicNumber = 469;
2613
2614 // If the table is wider than the text area, then don't create a fly
2615 // for the table: no wrapping will be performed anyway, but multi-page
2616 // tables will be broken.
2617 if ((nTableWidth + nMagicNumber) < nTextAreaWidth)
2618 bResult = true;
2619
2620 // If there are columns, do create a fly, as the flow of the columns
2621 // would otherwise restrict the table.
2622 if (!bResult && (m_aSectionManager.CurrentSectionColCount() >= 2))
2623 bResult = true;
2624 }
2625
2626 if (bResult)
2627 {
2628 WW8PLCFxSave1 aSave;
2629 pPap->Save(aSave);
2630 if (SearchTableEnd(pPap))
2631 {
2632 // Table is considered to be imported into a fly frame and we
2633 // know where the end of the table is.
2634 bool bIsUnicode;
2635 WW8_FC nFc = m_xSBase->WW8Cp2Fc(pPap->Where(), &bIsUnicode);
2636 sal_uInt64 nPos = m_pStrm->Tell();
2637 m_pStrm->Seek(nFc);
2638 sal_uInt16 nUChar = 0;
2639 if (bIsUnicode)
2640 m_pStrm->ReadUInt16(nUChar);
2641 else
2642 {
2643 sal_uInt8 nChar = 0;
2644 m_pStrm->ReadUChar(nChar);
2645 nUChar = nChar;
2646 }
2647 m_pStrm->Seek(nPos);
2648 if (nUChar == 0xc)
2649 // The pap after the table starts with a page break, so
2650 // there will be no wrapping around the float-table.
2651 // Request no fly in this case, so the table can properly
2652 // be a multi-page one if necessary.
2653 bResult = false;
2654 }
2655 pPap->Restore(aSave);
2656 }
2657
2658 return bResult;
2659}
2660
2661bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
2662{
2663 // Frame/Table/Anl
2664 if (m_bInHyperlink)
2665 return false;
2666
2667 rbReSync = false;
2668
2669 OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
2670
2671 // TabRowEnd
2672 bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
2673
2674// Unfortunately, for every paragraph we need to check first whether
2675// they contain a sprm 29 (0x261B), which starts an APO.
2676// All other sprms then refer to that APO and not to the normal text
2677// surrounding it.
2678// The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
2679
2680// WW: Table in APO is possible (Both Start-Ends occur at the same time)
2681// WW: APO in Table not possible
2682
2683// This mean that of a Table is the content of an APO, the APO start needs
2684// to be edited first, so that the Table remains in the APO and not the
2685// other way around.
2686// At the End, however, we need to edit the Table End first as the APO
2687// must end after that Table (or else we never find the APO End).
2688
2689// The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
2690
2691// If the Table is within an APO the TabRowEnd Area misses the
2692// APO settings.
2693// To not end the APO there, we do not call ProcessApo
2694
2695// KHZ: When there is a table inside the Apo the Apo-flags are also
2696// missing for the 2nd, 3rd... paragraphs of each cell.
2697
2698// 1st look for in-table flag, for 2000+ there is a subtable flag to
2699// be considered, the sprm 6649 gives the level of the table
2700 sal_uInt8 nCellLevel = 0;
2701
2702 if (m_bVer67)
2703 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
2704 else
2705 {
2706 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
2707 if (!nCellLevel)
2708 nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
2709 }
2710 do
2711 {
2712 WW8_TablePos *pTabPos=nullptr;
2713 WW8_TablePos aTabPos;
2714 if(nCellLevel && !m_bVer67)
2715 {
2716 WW8PLCFxSave1 aSave;
2717 m_xPlcxMan->GetPap()->Save( aSave );
2718 rbReSync = true;
2719 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
2720 WW8_CP nMyStartCp=nStartCp;
2721
2722 SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
2723 if (aLevel.pSprm && aLevel.nRemainingData >= 1)
2724 nCellLevel = *aLevel.pSprm;
2725
2726 bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
2727
2728 // Bad Table, remain unchanged in level, e.g. #i19667#
2729 if (!bHasRowEnd)
2730 nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
2731
2732 if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
2733 pTabPos = &aTabPos;
2734
2735 m_xPlcxMan->GetPap()->Restore( aSave );
2736 }
2737
2738 // Then look if we are in an Apo
2739
2740 ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
2741
2742 // Look to see if we are in a Table, but Table in foot/end note not allowed
2743 bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
2744
2745 bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
2746
2747 m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
2748 // WW8TabDesc::TableCellEnd() from making nonsense
2749
2750 if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
2751 bStopTab = bStartTab = true; // Required to stop and start table
2752
2753 // Test for Anl (Numbering) and process all events in the right order
2754 if( m_bAnl && !bTableRowEnd )
2755 {
2756 SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
2757 const sal_uInt8* pSprm13 = aSprm13.pSprm;
2758 if (pSprm13 && aSprm13.nRemainingData >= 1)
2759 { // Still Anl left?
2760 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
2761 if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
2762 || aApo.HasStartStop() // Forced Anl end
2763 || bStopTab || bStartTab )
2764 {
2765 StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
2766 }
2767 else
2768 {
2769 NextAnlLine( pSprm13 ); // Next Anl Line
2770 }
2771 }
2772 else
2773 { // Regular Anl end
2774 StopAllAnl(); // Actual end
2775 }
2776 }
2777 if (bStopTab)
2778 {
2779 StopTable();
2780 m_aApos.pop_back();
2781 --m_nInTable;
2782 }
2783 if (aApo.mbStopApo)
2784 {
2785 StopApo();
2786 m_aApos[m_nInTable] = false;
2787 }
2788
2789 if (aApo.mbStartApo)
2790 {
2791 m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
2792 // We need an ReSync after StartApo
2793 // (actually only if the Apo extends past a FKP border)
2794 rbReSync = true;
2795 }
2796 if (bStartTab)
2797 {
2798 WW8PLCFxSave1 aSave;
2799 m_xPlcxMan->GetPap()->Save( aSave );
2800
2801 // Numbering for cell borders causes a crash -> no Anls in Tables
2802 if (m_bAnl)
2803 StopAllAnl();
2804
2805 if(m_nInTable < nCellLevel)
2806 {
2807 if (StartTable(nStartCp))
2808 ++m_nInTable;
2809 else
2810 break;
2811 m_aApos.push_back(false);
2812 }
2813
2814 if(m_nInTable >= nCellLevel)
2815 {
2816 // We need an ReSync after StartTable
2817 // (actually only if the Apo extends past a FKP border)
2818 rbReSync = true;
2819 m_xPlcxMan->GetPap()->Restore( aSave );
2820 }
2821 }
2822 } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
2823 return bTableRowEnd;
2824}
2825
2827{
2828 /*
2829 #i22206#/#i52786#
2830 The (default) character set used for a run of text is the default
2831 character set for the version of Word that last saved the document.
2832
2833 This is a bit tentative, more might be required if the concept is correct.
2834 When later version of word write older 6/95 documents the charset is
2835 correctly set in the character runs involved, so it's hard to reproduce
2836 documents that require this to be sure of the process involved.
2837 */
2838 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
2839 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2840 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2842}
2843
2845{
2846 /*
2847 #i22206#/#i52786#
2848 The (default) character set used for a run of text is the default
2849 character set for the version of Word that last saved the document.
2850
2851 This is a bit tentative, more might be required if the concept is correct.
2852 When later version of word write older 6/95 documents the charset is
2853 correctly set in the character runs involved, so it's hard to reproduce
2854 documents that require this to be sure of the process involved.
2855 */
2856 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_CJK_LANGUAGE));
2857 LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
2858 css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
2860}
2861
2863{
2864 /*
2865 #i2015
2866 If the hard charset is set use it, if not see if there is an open
2867 character run that has set the charset, if not then fallback to the
2868 current underlying paragraph style.
2869 */
2870 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2871 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2872 {
2873 if (!m_bVer67)
2874 eSrcCharSet = GetCharSetFromLanguage();
2875 else if (!m_aFontSrcCharSets.empty())
2876 eSrcCharSet = m_aFontSrcCharSets.top();
2877 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2878 eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
2879 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2880 eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
2881 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2882 eSrcCharSet = GetCharSetFromLanguage();
2883 }
2884 return eSrcCharSet;
2885}
2886
2887//Takashi Ono for CJK
2889{
2890 /*
2891 #i2015
2892 If the hard charset is set use it, if not see if there is an open
2893 character run that has set the charset, if not then fallback to the
2894 current underlying paragraph style.
2895 */
2896 rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
2897 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2898 {
2899 if (!m_aFontSrcCJKCharSets.empty())
2900 eSrcCharSet = m_aFontSrcCJKCharSets.top();
2901 if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
2902 eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
2903 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
2904 eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
2905 if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
2906 eSrcCharSet = GetCJKCharSetFromLanguage();
2907 }
2908 return eSrcCharSet;
2909}
2910
2912{
2913 if (m_pPostProcessAttrsInfo == nullptr)
2914 return;
2915
2916 SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
2917
2918 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2919 {
2920 m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
2921 *pItem);
2922 m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
2923 pItem->Which());
2924 }
2925
2927}
2928
2929/*
2930 #i9240#
2931 It appears that some documents that are in a baltic 8 bit encoding which has
2932 some undefined characters can have use made of those characters, in which
2933 case they default to CP1252. If not then it's perhaps that the font encoding
2934 is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
2935 is always 1252.
2936
2937 So an encoding converter that on an undefined character attempts to
2938 convert from 1252 on the undefined character
2939*/
2940static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
2941 char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
2942{
2943 const sal_uInt32 nFlags =
2944 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
2945 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
2946 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2947 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2948
2949 const sal_uInt32 nFlags2 =
2950 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
2951 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
2952 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
2953 RTL_TEXTTOUNICODE_FLAGS_FLUSH;
2954
2955 std::size_t nDestChars=0;
2956 std::size_t nConverted=0;
2957
2958 do
2959 {
2960 sal_uInt32 nInfo = 0;
2961 sal_Size nThisConverted=0;
2962
2963 nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
2964 pIn+nConverted, nInLen-nConverted,
2965 pOut+nDestChars, nOutLen-nDestChars,
2966 nFlags, &nInfo, &nThisConverted);
2967
2968 OSL_ENSURE(nInfo == 0, "A character conversion failed!");
2969
2970 nConverted += nThisConverted;
2971
2972 if (
2973 nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
2974 nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
2975 )
2976 {
2977 sal_Size nOtherConverted;
2978 rtl_TextToUnicodeConverter hCP1252Converter =
2979 rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
2980 nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
2981 pIn+nConverted, 1,
2982 pOut+nDestChars, nOutLen-nDestChars,
2983 nFlags2, &nInfo, &nOtherConverted);
2984 rtl_destroyTextToUnicodeConverter(hCP1252Converter);
2985 nConverted+=1;
2986 }
2987 } while (nConverted < nInLen);
2988
2989 return nDestChars;
2990}
2991
2993{
2994 bool bResult = false;
2995
2996 switch (static_cast<sal_uInt16>(nLang))
2997 {
2998 case 0x1401: // Arabic(Algeria)
2999 case 0x3c01: // Arabic(Bahrain)
3000 case 0xc01: // Arabic(Egypt)
3001 case 0x801: // Arabic(Iraq)
3002 case 0x2c01: // Arabic (Jordan)
3003 case 0x3401: // Arabic(Kuwait)
3004 case 0x3001: // Arabic(Lebanon)
3005 case 0x1001: // Arabic(Libya)
3006 case 0x1801: // Arabic(Morocco)
3007 case 0x2001: // Arabic(Oman)
3008 case 0x4001: // Arabic(Qatar)
3009 case 0x401: // Arabic(Saudi Arabia)
3010 case 0x2801: // Arabic(Syria)
3011 case 0x1c01: // Arabic(Tunisia)
3012 case 0x3801: // Arabic(U.A.E)
3013 case 0x2401: // Arabic(Yemen)
3014 bResult = true;
3015 break;
3016 default:
3017 break;
3018 }
3019
3020 return bResult;
3021}
3022
3024{
3025 if (nChar >= 0x0030 && nChar <= 0x0039)
3026 return nChar + 0x0630;
3027
3028 return nChar;
3029}
3030
3031namespace
3032{
3033 OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
3034 {
3035 //if read len was in or around that of allocated len, just reuse pStr
3036 if (nAllocLen < pStr->length + 256)
3037 return OUString(pStr, SAL_NO_ACQUIRE);
3038 //otherwise copy the shorter used section to release extra mem
3039 OUString sRet(pStr->buffer, pStr->length);
3040 rtl_uString_release(pStr);
3041 return sRet;
3042 }
3043}
3044
3048bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
3049{
3050 sal_Int32 nRequestedStrLen = nEnd - rPos;
3051
3052 OSL_ENSURE(nRequestedStrLen, "String is 0");
3053 if (nRequestedStrLen <= 0)
3054 return true;
3055
3056 WW8_CP nCp;
3057 const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
3058 if (bFail)
3059 {
3060 rPos+=nRequestedStrLen;
3061 return true;
3062 }
3063
3064 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
3065 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3066 OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
3067 if (!bValidPos)
3068 {
3069 // Swallow missing range, e.g. #i95550#
3070 rPos+=nRequestedStrLen;
3071 return true;
3072 }
3073
3074 std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
3075 OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
3076 if (!nAvailableStrLen)
3077 {
3078 // Swallow missing range, e.g. #i95550#
3079 rPos+=nRequestedStrLen;
3080 return true;
3081 }
3082
3083 sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
3084
3085 // Reset Unicode flag and correct FilePos if needed.
3086 // Note: Seek is not expensive, as we're checking inline whether or not
3087 // the correct FilePos has already been reached.
3088 const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
3089
3090 rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
3091 RTL_TEXTENCODING_MS_1252;
3092 if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
3093 {
3094 /*
3095 fdo#82904
3096
3097 Older documents exported as word 95 that use unicode aware fonts will
3098 have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
3099 export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
3100 pain.
3101
3102 We will try and use a fallback encoding if the conversion from
3103 RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
3104 which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
3105 valid RTL_TEXTENCODING_MS_932 by chance :-(
3106
3107 We're not the only ones that struggle with this: Here's the help from
3108 MSOffice 2003 on the topic:
3109
3110 <<
3111 Earlier versions of Microsoft Word were sometimes used in conjunction with
3112 third-party language-processing add-in programs designed to support Chinese or
3113 Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
3114 results in incorrect text display in more recent versions of Word.
3115
3116 However, you can set options to convert these documents so that text is
3117 displayed correctly. On the Tools menu, click Options, and then click the
3118 General tab. In the English Word 6.0/95 documents list, select Contain Asian
3119 text (to have Word interpret the text as Asian code page data, regardless of
3120 its font) or Automatically detect Asian text (to have Word attempt to determine
3121 which parts of the text are meant to be Asian).
3122 >>
3123
3124 What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
3125 the language is not Japanese
3126 */
3127
3129 if (pItem != nullptr && LANGUAGE_JAPANESE != static_cast<const SvxLanguageItem *>(pItem)->GetLanguage())
3130 {
3131 SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
3132 eSrcCharSet = GetCharSetFromLanguage();
3133 }
3134 }
3135 const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
3136 RTL_TEXTENCODING_MS_1252;
3137
3138 // allocate unicode string data
3139 auto l = [](rtl_uString* p){rtl_uString_release(p);};
3140 std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
3141 sal_Unicode* pBuffer = xStr->buffer;
3142 sal_Unicode* pWork = pBuffer;
3143
3144 std::unique_ptr<char[]> p8Bits;
3145
3146 rtl_TextToUnicodeConverter hConverter = nullptr;
3147 if (!m_bIsUnicode || m_bVer67)
3148 hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
3149
3150 if (!m_bIsUnicode)
3151 p8Bits.reset( new char[nStrLen] );
3152
3153 // read the stream data
3154 sal_uInt8 nBCode = 0;
3155 sal_uInt16 nUCode;
3156
3157 LanguageType nCTLLang = LANGUAGE_SYSTEM;
3159 if (pItem != nullptr)
3160 nCTLLang = static_cast<const SvxLanguageItem *>(pItem)->GetLanguage();
3161
3162 sal_Int32 nL2;
3163 for (nL2 = 0; nL2 < nStrLen; ++nL2)
3164 {
3165 if (m_bIsUnicode)
3166 m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
3167 else
3168 {
3169 m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
3170 nUCode = nBCode;
3171 }
3172
3173 if (!m_pStrm->good())
3174 {
3175 rPos = WW8_CP_MAX-10; // -> eof or other error
3176 return true;
3177 }
3178
3179 if ((32 > nUCode) || (0xa0 == nUCode))
3180 {
3181 m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
3182 break; // Special character < 32, == 0xa0 found
3183 }
3184
3185 if (m_bIsUnicode)
3186 {
3187 if (!m_bVer67)
3188 *pWork++ = nUCode;
3189 else
3190 {
3191 if (nUCode >= 0x3000) //0x8000 ?
3192 {
3193 char aTest[2];
3194 aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
3195 aTest[1] = static_cast< char >(nUCode & 0x00FF);
3196 OUString aTemp(aTest, 2, eSrcCJKCharSet);
3197 OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
3198 *pWork++ = aTemp[0];
3199 }
3200 else
3201 {
3202 char cTest = static_cast< char >(nUCode & 0x00FF);
3203 pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
3204 }
3205 }
3206 }
3207 else
3208 p8Bits[nL2] = nBCode;
3209 }
3210
3211 if (nL2)
3212 {
3213 const sal_Int32 nEndUsed = !m_bIsUnicode
3214 ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
3215 : pWork - pBuffer;
3216
3218 {
3219 for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
3220 *pBuffer = TranslateToHindiNumbers(*pBuffer);
3221 }
3222
3223 xStr->buffer[nEndUsed] = 0;
3224 xStr->length = nEndUsed;
3225
3226 emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
3227 rPos += nL2;
3228 if (!m_aApos.back()) // a para end in apo doesn't count
3229 m_bWasParaEnd = false; // No CR
3230 }
3231
3232 if (hConverter)
3233 rtl_destroyTextToUnicodeConverter(hConverter);
3234 return nL2 >= nStrLen;
3235}
3236
3237#define MSASCII SAL_MAX_INT16
3238
3239namespace
3240{
3241 // We want to force weak chars inside 0x0020 to 0x007F to LATIN
3242 sal_Int16 lcl_getScriptType(
3243 const uno::Reference<i18n::XBreakIterator>& rBI,
3244 const OUString &rString, sal_Int32 nPos)
3245 {
3246 sal_Int16 nScript = rBI->getScriptType(rString, nPos);
3247 if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
3248 nScript = MSASCII;
3249 return nScript;
3250 }
3251
3252 // We want to know about WEAK segments, so endOfScript isn't
3253 // useful, and see lcl_getScriptType anyway
3254 sal_Int32 lcl_endOfScript(
3255 const uno::Reference<i18n::XBreakIterator>& rBI,
3256 const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
3257 {
3258 while (nPos < rString.getLength())
3259 {
3260 sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
3261 if (nScript != nNewScript)
3262 break;
3263 ++nPos;
3264 }
3265 return nPos;
3266 }
3267
3268 sal_Int32 lcl_getWriterScriptType(
3269 const uno::Reference<i18n::XBreakIterator>& rBI,
3270 const OUString &rString, sal_Int32 nPos)
3271 {
3272 sal_Int16 nScript = i18n::ScriptType::WEAK;
3273
3274 if (rString.isEmpty())
3275 return nScript;
3276
3277 while (nPos >= 0)
3278 {
3279 nScript = rBI->getScriptType(rString, nPos);
3280 if (nScript != i18n::ScriptType::WEAK)
3281 break;
3282 --nPos;
3283 }
3284
3285 return nScript;
3286 }
3287
3288 bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
3289 {
3290 return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
3291 }
3292
3293 bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
3294 {
3295 // Ignoring CharSet, and ignoring unknown pitch
3296 return rA.GetFamilyName() == rB.GetFamilyName() &&
3297 rA.GetStyleName() == rB.GetStyleName() &&
3298 rA.GetFamily() == rB.GetFamily() &&
3299 samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
3300 }
3301}
3302
3303// In writer we categorize text into CJK, CTL and "Western" for everything else.
3304// Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
3305// NonEastAsian/HighAnsi, with some shared characters and some properties to
3306// hint as to which way to bias those shared characters.
3307
3308// That's four categories, we however have three categories. Given that problem
3309// here we would ideally find out "what would word do" to see what font/language
3310// word would assign to characters based on the unicode range they fall into and
3311// hack the word one onto the range we use. However it's unclear what word's
3312// categorization is. So we don't do that here yet.
3313
3314// Additional to the categorization, when word encounters weak text for ambiguous
3315// chars it uses idcthint to indicate which way to bias. We don't have an idcthint
3316// feature in writer.
3317
3318// So what we currently do here then is to split our text into non-weak/weak
3319// sections and uses word's idcthint to determine what font it would use and
3320// force that on for the segment. Following what we *do* know about word's
3321// categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
3322// word, something we map to LATIN, so we consider all weak chars in that range
3323// to auto-bias to LATIN.
3324
3325// See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
3327{
3328 if (rAddString.isEmpty())
3329 return;
3330
3331 if (m_bFuzzing)
3332 {
3333 simpleAddTextToParagraph(rAddString);
3334 return;
3335 }
3336
3337 uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
3338 assert(xBI.is());
3339
3340 sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
3341 sal_Int32 nLen = rAddString.getLength();
3342
3343 OUString sParagraphText;
3344 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3345 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3346 if (pNd)
3347 sParagraphText = pNd->GetText();
3348 sal_Int32 nParaOffset = sParagraphText.getLength();
3349 sParagraphText = sParagraphText + rAddString;
3350
3351 sal_Int32 nPos = 0;
3352 while (nPos < nLen)
3353 {
3354 sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
3355 if (nEnd < 0)
3356 break;
3357
3358 OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
3359 const sal_uInt16 aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
3360 const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
3361 bool aForced[] = {false, false, false};
3362
3363 int nLclIdctHint = 0xFF;
3364 if (nScript == i18n::ScriptType::WEAK)
3365 {
3366 const SfxInt16Item *pIdctHint = static_cast<const SfxInt16Item*>(GetFormatAttr(RES_CHRATR_IDCTHINT));
3367 nLclIdctHint = pIdctHint->GetValue();
3368 }
3369 else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
3370 nLclIdctHint = 0;
3371
3372 sal_uInt16 nForceFromFontId = 0;
3373 if (nLclIdctHint != 0xFF)
3374 {
3375 switch (nLclIdctHint)
3376 {
3377 case 0:
3378 nForceFromFontId = RES_CHRATR_FONT;
3379 break;
3380 case 1:
3381 nForceFromFontId = RES_CHRATR_CJK_FONT;
3382 break;
3383 case 2:
3384 nForceFromFontId = RES_CHRATR_CTL_FONT;
3385 break;
3386 default:
3387 break;
3388 }
3389 }
3390
3391 if (nForceFromFontId != 0)
3392 {
3393 // Now we know that word would use the nForceFromFontId font for this range
3394 // Try and determine what script writer would assign this range to
3395
3396 sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
3397 nPos + nParaOffset);
3398
3399 bool bWriterWillUseSameFontAsWordAutomatically = false;
3400
3401 if (nWriterScript != i18n::ScriptType::WEAK)
3402 {
3403 if (
3404 (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
3405 (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
3406 (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
3407 )
3408 {
3409 bWriterWillUseSameFontAsWordAutomatically = true;
3410 }
3411 else
3412 {
3413 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3414 sal_uInt16 nDestId = aIds[nWriterScript-1];
3415 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(nDestId));
3416 bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
3417 }
3418 }
3419
3420 // Writer won't use the same font as word, so force the issue
3421 if (!bWriterWillUseSameFontAsWordAutomatically)
3422 {
3423 const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId));
3424
3425 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3426 {
3427 const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(aIds[i]));
3428 aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
3429 if (aForced[i])
3430 {
3431 pOverriddenItems[i] =
3432 static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
3433
3434 SvxFontItem aForceFont(*pSourceFont);
3435 aForceFont.SetWhich(aIds[i]);
3436 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
3437 }
3438 }
3439 }
3440 }
3441
3443
3444 for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
3445 {
3446 if (aForced[i])
3447 {
3448 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
3449 if (pOverriddenItems[i])
3450 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
3451 }
3452 }
3453
3454 nPos = nEnd;
3455 if (nPos < nLen)
3456 nScript = lcl_getScriptType(xBI, rAddString, nPos);
3457 }
3458}
3459
3460namespace sw {
3461
3462auto FilterControlChars(std::u16string_view aString) -> OUString
3463{
3464 OUStringBuffer buf(aString.size());
3465 for (size_t i = 0; i < aString.size(); ++i)
3466 {
3467 sal_Unicode const ch(aString[i]);
3468 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3469 {
3470 buf.append(ch);
3471 }
3472 else
3473 {
3474 SAL_INFO("sw.ww8", "filtering control character");
3475 }
3476 }
3477 return buf.makeStringAndClear();
3478}
3479
3480} // namespace sw
3481
3482void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
3483{
3484 OUString const addString(sw::FilterControlChars(aAddString));
3485
3486 if (addString.isEmpty())
3487 return;
3488
3489 const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
3490 const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
3491
3492 OSL_ENSURE(pNd, "What the hell, where's my text node");
3493
3494 if (!pNd)
3495 return;
3496
3497 const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
3498 if (nCharsLeft > 0)
3499 {
3500 if (addString.getLength() <= nCharsLeft)
3501 {
3503 }
3504 else
3505 {
3506 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
3508 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
3509 }
3510 }
3511 else
3512 {
3515 }
3516
3517 m_bReadTable = false;
3518}
3519
3523bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
3524 tools::Long nCpOfs)
3525{
3526 tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
3527
3528 if (m_bSymbol || m_bIgnoreText)
3529 {
3530 WW8_CP nRequested = nEnd - rPos;
3531 if (m_bSymbol) // Insert special chars
3532 {
3533 sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
3534 if (o3tl::make_unsigned(nRequested) > nMaxPossible)
3535 {
3536 SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
3537 nRequested = nMaxPossible;
3538 }
3539
3541 || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
3542 {
3543 for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
3544 {
3546 }
3550 }
3551 }
3552 m_pStrm->SeekRel(nRequested);
3553 rPos = nEnd; // Ignore until attribute end
3554 return false;
3555 }
3556
3557 while (true)
3558 {
3559 if (ReadPlainChars(rPos, nEnd, nCpOfs))
3560 return false; // Done
3561
3562 bool bStartLine = ReadChar(rPos, nCpOfs);
3563 rPos++;
3564 if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
3565 {
3566 return bStartLine;
3567 }
3568 }
3569}
3570
3572{
3573 bool bParaEndAdded = false;
3574 // #i1909# section/page breaks should not occur in tables, word
3575 // itself ignores them in this case.
3576 if (!m_nInTable)
3577 {
3578 bool IsTemp=true;
3580 if (pTemp && pTemp->GetText().isEmpty()
3582 {
3583 IsTemp = false;
3586 }
3587
3588 m_bPgSecBreak = true;
3589 m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
3590 /*
3591 If it's a 0x0c without a paragraph end before it, act like a
3592 paragraph end, but nevertheless, numbering (and perhaps other
3593 similar constructs) do not exist on the para.
3594 */
3595 if (!m_bWasParaEnd && IsTemp)
3596 {
3597 bParaEndAdded = true;
3598 if (0 >= m_pPaM->GetPoint()->GetContentIndex())
3599 {
3600 if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
3601 {
3602 pTextNode->SetAttr(
3604 }
3605 }
3606 }
3607 }
3608 return bParaEndAdded;
3609}
3610
3612{
3613 bool bNewParaEnd = false;
3614 // Reset Unicode flag and correct FilePos if needed.
3615 // Note: Seek is not expensive, as we're checking inline whether or not
3616 // the correct FilePos has already been reached.
3617 std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
3618 if (!checkSeek(*m_pStrm, nRequestedPos))
3619 return false;
3620
3621 sal_uInt16 nWCharVal(0);
3622 if( m_bIsUnicode )
3623 m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
3624 else
3625 {
3626 sal_uInt8 nBCode(0);
3627 m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
3628 nWCharVal = nBCode;
3629 }
3630
3631 sal_Unicode cInsert = '\x0';
3632 bool bParaMark = false;
3633
3634 if ( 0xc != nWCharVal )
3635 m_bFirstParaOfPage = false;
3636
3637 switch (nWCharVal)
3638 {
3639 case 0:
3640 if (!m_bFuzzing)
3641 {
3642 // Page number
3643 SwPageNumberField aField(
3647 }
3648 else
3649 {
3650 // extremely slow, so skip for fuzzing, and insert a space instead
3651 cInsert = ' ';
3652 }
3653 break;
3654 case 0xe:
3655 // if there is only one column word treats a column break like a pagebreak.
3657 bParaMark = HandlePageBreakChar();
3658 else if (!m_nInTable)
3659 {
3660 // Always insert a txtnode for a column break, e.g. ##
3662 if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
3665 }
3666 break;
3667 case 0x7:
3668 {
3669 bNewParaEnd = true;
3670 WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
3671 //The last paragraph of each cell is terminated by a special
3672 //paragraph mark called a cell mark. Following the cell mark
3673 //that ends the last cell of a table row, the table row is
3674 //terminated by a special paragraph mark called a row mark
3675 //
3676 //So the 0x7 should be right at the end of the previous
3677 //range to be a real cell-end.
3678 if (pPap->nOrigStartPos == nPosCp+1 ||
3679 pPap->nOrigStartPos == WW8_CP_MAX)
3680 {
3681 TabCellEnd(); // Table cell/row end
3682 }
3683 else
3684 bParaMark = true;
3685 }
3686 break;
3687 case 0xf:
3688 if( !m_bSpec ) // "Satellite"
3689 cInsert = u'\x00a4';
3690 break;
3691 case 0x14:
3692 if( !m_bSpec ) // "Para End" char
3693 cInsert = u'\x00b5';
3694 //TODO: should this be U+00B6 PILCROW SIGN rather than
3695 // U+00B5 MICRO SIGN?
3696 break;
3697 case 0x15:
3698 if( !m_bSpec ) // Juristenparagraph
3699 {
3700 cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
3701 if (aItr == m_aTOXEndCps.end())
3702 cInsert = u'\x00a7';
3703 else
3704 m_aTOXEndCps.erase(aItr);
3705 }
3706 break;
3707 case 0x9:
3708 cInsert = '\x9'; // Tab
3709 break;
3710 case 0xb:
3711 cInsert = '\xa'; // Hard NewLine
3712 break;
3713 case 0xc:
3714 bParaMark = HandlePageBreakChar();
3715 break;
3716 case 0x1e: // Non-breaking hyphen
3718 break;
3719 case 0x1f: // Non-required hyphens
3721 break;
3722 case 0xa0: // Non-breaking spaces
3724 break;
3725 case 0x1:
3726 /*
3727 Current thinking is that if bObj is set then we have a
3728 straightforward "traditional" ole object, otherwise we have a
3729 graphic preview of an associated ole2 object (or a simple
3730 graphic of course)
3731
3732 normally in the canvas field, the code is 0x8 0x1.
3733 in a special case, the code is 0x1 0x1, which yields a simple picture
3734 */
3735 {
3736 bool bReadObj = IsInlineEscherHack();
3737 if( bReadObj )
3738 {
3739 sal_uInt64 nCurPos = m_pStrm->Tell();
3740 sal_uInt16 nWordCode(0);
3741
3742 if( m_bIsUnicode )
3743 m_pStrm->ReadUInt16( nWordCode );
3744 else
3745 {
3746 sal_uInt8 nByteCode(0);
3747 m_pStrm->ReadUChar( nByteCode );
3748 nWordCode = nByteCode;
3749 }
3750 if( nWordCode == 0x1 )
3751 bReadObj = false;
3752 m_pStrm->Seek( nCurPos );
3753 }
3754 if( !bReadObj )
3755 {
3756 SwFrameFormat *pResult = nullptr;
3757 if (m_bObj)
3758 pResult = ImportOle();
3759 else if (m_bSpec)
3760 {
3761 SwFrameFormat* pAsCharFlyFormat =
3763 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
3764 pAsCharFlyFormat->SetFormatAttr(aAnchor);
3765 pResult = ImportGraf(nullptr, pAsCharFlyFormat);
3766 m_rDoc.DelFrameFormat(pAsCharFlyFormat);
3767 }
3768
3769
3770 // If we have a bad 0x1 insert a space instead.
3771 if (!pResult)
3772 {
3773 cInsert = ' ';
3774 OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
3775 "WW8: Please report this document, it may have a "
3776 "missing graphic");
3777 }
3778 else
3779 {
3780 // reset the flags.
3781 m_bObj = m_bEmbeddObj = false;
3782 m_nObjLocFc = 0;
3783 }
3784 }
3785 }
3786 break;
3787 case 0x8:
3788 if( !m_bObj )
3789 Read_GrafLayer( nPosCp );
3790 break;
3791 case 0xd:
3792 bNewParaEnd = bParaMark = true;
3793 if (m_nInTable > 1)
3794 {
3795 /*
3796 #i9666#/#i23161#
3797 Yes complex, if there is an entry in the undocumented PLCF
3798 which I believe to be a record of cell and row boundaries
3799 see if the magic bit which I believe to mean cell end is
3800 set. I also think btw that the third byte of the 4 byte
3801 value is the level of the cell
3802 */
3803 WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
3804 if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
3805 pTest->Where() == nPosCp+1+nCpOfs)
3806 {
3807 WW8_FC nPos;
3808 void *pData;
3809 sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
3810 : 0;
3811 if (nData & 0x2) // Might be how it works
3812 {
3813 TabCellEnd();
3814 bParaMark = false;
3815 }
3816 }
3817 // tdf#106799: We expect TTP marks to be also cell marks,
3818 // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
3820 {
3821 TabCellEnd();
3822 bParaMark = false;
3823 }
3824 }
3825
3826 m_bWasTabCellEnd = false;
3827
3828 break; // line end
3829 case 0x5: // Annotation reference
3830 case 0x13:
3831 break;
3832 case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
3833 if (!m_aFootnoteStack.empty())
3834 cInsert = '?';
3835 break;
3836 default:
3837 SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
3838 break;
3839 }
3840
3841 if( '\x0' != cInsert )
3842 {
3843 OUString sInsert(cInsert);
3845 }
3846 if (!m_aApos.back()) // a para end in apo doesn't count
3847 m_bWasParaEnd = bNewParaEnd;
3848 return bParaMark;
3849}
3850
3852 bool* pStartAttr, bool bCallProcessSpecial)
3853{
3854 sal_uInt16 nOldColl = m_nCurrentColl;
3855 m_nCurrentColl = m_xPlcxMan->GetColl();
3856
3857 // Invalid Style-Id
3858 if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
3859 {
3860 m_nCurrentColl = 0;
3861 m_bParaAutoBefore = false;
3862 m_bParaAutoAfter = false;
3863 }
3864 else
3865 {
3866 m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
3867 m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
3868 }
3869
3870 if (nOldColl >= m_vColl.size())
3871 nOldColl = 0; // guess! TODO make sure this is what we want
3872
3873 bool bTabRowEnd = false;
3874 if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
3875 {
3876 bool bReSync;
3877 // Frame/Table/Autonumbering List Level
3878 bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
3879 if( bReSync )
3880 *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
3881 }
3882
3883 if (!bTabRowEnd && StyleExists(m_nCurrentColl))
3884 {
3886 ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
3887 ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
3888 m_vColl[m_nCurrentColl].m_n81BiDiFlags);
3889 }
3890}
3891
3892tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
3893{
3894 tools::Long nSkipChars = 0;
3895 WW8PLCFManResult aRes;
3896
3897 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3898 bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
3899 aRes.nCurrentCp = rTextPos; // Current Cp position
3900
3901 bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
3902 if ( bNewSection ) // New Section
3903 {
3904 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3905 // Create PageDesc and fill it
3906 m_aSectionManager.CreateSep(rTextPos);
3907 // -> 0xc was a Sectionbreak, but not a Pagebreak;
3908 // Create PageDesc and fill it
3909 m_bPgSecBreak = false;
3910 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
3911 }
3912
3913 // New paragraph over Plcx.Fkp.papx
3914 if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
3915 {
3916 ProcessCurrentCollChange( aRes, &bStartAttr,
3918 !m_bIgnoreText );
3919 rbStartLine = false;
3920 }
3921
3922 // position of last CP that's to be ignored
3923 tools::Long nSkipPos = -1;
3924
3925 if( 0 < aRes.nSprmId ) // Ignore empty Attrs
3926 {
3927 if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
3928 {
3929 if( bStartAttr ) // WW attributes
3930 {
3931 if( aRes.nMemLen >= 0 )
3932 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
3933 }
3934 else
3935 EndSprm( aRes.nSprmId ); // Switch off Attr
3936 }
3937 else if( aRes.nSprmId < 0x800 ) // Own helper attributes
3938 {
3939 if (bStartAttr)
3940 {
3941 nSkipChars = ImportExtSprm(&aRes);
3942 if (
3943 (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
3944 (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
3945 )
3946 {
3947 WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
3948 // Skip Field/Footnote-/End-Note here
3949 rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
3950 nSkipPos = rTextPos-1;
3951 }
3952 }
3953 else
3954 EndExtSprm( aRes.nSprmId );
3955 }
3956 }
3957
3958 sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
3959 bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
3960 SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
3961
3962 // Find next Attr position (and Skip attributes of field contents if needed)
3963 if (nSkipChars && !m_bIgnoreText)
3964 m_xCtrlStck->MarkAllAttrsOld();
3965 bool bOldIgnoreText = m_bIgnoreText;
3966 m_bIgnoreText = true;
3967 sal_uInt16 nOldColl = m_nCurrentColl;
3968 bool bDoPlcxManPlusPLus = true;
3969 tools::Long nNext;
3970 do
3971 {
3972 if( bDoPlcxManPlusPLus )
3973 m_xPlcxMan->advance();
3974 nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
3975
3977 m_pPostProcessAttrsInfo->mnCpStart == nNext)
3978 {
3979 m_pPostProcessAttrsInfo->mbCopy = true;
3980 }
3981
3982 if( (0 <= nNext) && (nSkipPos >= nNext) )
3983 {
3984 if (nDepthGuard >= 1024)
3985 {
3986 SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
3987 nNext = nTextEnd;
3988 }
3989 else
3990 nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
3991 bDoPlcxManPlusPLus = false;
3992 m_bIgnoreText = true;
3993 }
3994
3996 nNext > m_pPostProcessAttrsInfo->mnCpEnd)
3997 {
3998 m_pPostProcessAttrsInfo->mbCopy = false;
3999 }
4000 }
4001 while( nSkipPos >= nNext );
4002 m_bIgnoreText = bOldIgnoreText;
4003 if( nSkipChars )
4004 {
4005 m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
4006 if( nOldColl != m_xPlcxMan->GetColl() )
4007 ProcessCurrentCollChange(aRes, nullptr, false);
4008 }
4009
4010 return nNext;
4011}
4012
4013void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
4014{
4015 // Do we have attributes?
4016 if( rTextPos >= rNext )
4017 {
4018 do
4019 {
4020 rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
4021 if (rTextPos == rNext && rTextPos >= nTextEnd)
4022 break;
4023 }
4024 while( rTextPos >= rNext );
4025
4026 }
4027 else if ( rbStartLine )
4028 {
4029 /* No attributes, but still a new line.
4030 * If a line ends with a line break and paragraph attributes or paragraph templates
4031 * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
4032 * is false.
4033 * Due to this we need to set the template here as a kind of special treatment.
4034 */
4035 if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
4037 rbStartLine = false;
4038 }
4039}
4040
4047{
4048 // If there are any unclosed sprms then copy them to
4049 // another stack and close the ones that must be closed
4050 std::stack<sal_uInt16> aStack;
4051 m_xPlcxMan->TransferOpenSprms(aStack);
4052
4053 while (!aStack.empty())
4054 {
4055 sal_uInt16 nSprmId = aStack.top();
4056 if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
4057 EndSprm(nSprmId);
4058 aStack.pop();
4059 }
4060
4061 EndSpecial();
4062}
4063
4064bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
4065{
4066 bool bJoined=false;
4067
4068 bool bStartLine = true;
4069 short nCrCount = 0;
4070 short nDistance = 0;
4071
4072 m_bWasParaEnd = false;
4073 m_nCurrentColl = 0;
4074 m_xCurrentItemSet.reset();
4075 m_nCharFormat = -1;
4076 m_bSpec = false;
4077 m_bPgSecBreak = false;
4078
4079 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
4080 tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
4081
4082 WW8_CP nNext = m_xPlcxMan->Where();
4083 m_xPreviousNode.reset();
4084 sal_uInt8 nDropLines = 0;
4085 SwCharFormat* pNewSwCharFormat = nullptr;
4086 const SwCharFormat* pFormat = nullptr;
4087
4088 bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
4089 if (!bValidPos)
4090 return false;
4091
4092 WW8_CP l = nStartCp;
4093 const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
4094 if (nTextLen > nMaxPossible)
4095 {
4096 SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
4097 nTextLen = nMaxPossible;
4098 }
4099 WW8_CP nTextEnd = nStartCp+nTextLen;
4100 while (l < nTextEnd)
4101 {
4102 ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
4103 OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
4104
4105 if (m_pPostProcessAttrsInfo != nullptr)
4107
4108 if (l >= nTextEnd)
4109 break;
4110
4111 bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
4112
4113 // If the previous paragraph was a dropcap then do not
4114 // create a new txtnode and join the two paragraphs together
4115 if (bStartLine && !m_xPreviousNode) // Line end
4116 {
4117 bool bSplit = true;
4119 {
4121 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4122 bSplit = false;
4123 }
4125 {
4127 if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
4128 bSplit = false;
4129 }
4130 if (bSplit)
4131 {
4133 }
4134 }
4135
4136 if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
4137 {
4139 SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
4140 if (pEndNd)
4141 {
4142 const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
4143
4144 // Need to reset the font size and text position for the dropcap
4145 {
4146 SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
4147 m_xCtrlStck->Delete(aTmp);
4148 }
4149
4150 // Get the default document dropcap which we can use as our template
4151 const SwFormatDrop* defaultDrop =
4152 static_cast<const SwFormatDrop*>( GetFormatAttr(RES_PARATR_DROP));
4153 SwFormatDrop aDrop(*defaultDrop);
4154
4155 aDrop.GetLines() = nDropLines;
4156 aDrop.GetDistance() = nDistance;
4157 aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
4158 // Word has no concept of a "whole word dropcap"
4159 aDrop.GetWholeWord() = false;
4160
4161 if (pFormat)
4162 aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
4163 else if(pNewSwCharFormat)
4164 aDrop.SetCharFormat(pNewSwCharFormat);
4165
4166 SwPosition aStart(*pEndNd);
4167 m_xCtrlStck->NewAttr(aStart, aDrop);
4169 }
4170 m_xPreviousNode.reset();
4171 }
4172 else if (m_bDropCap)
4173 {
4174 // If we have found a dropcap store the textnode
4176
4177 SprmResult aDCS;
4178 if (m_bVer67)
4179 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
4180 else
4181 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
4182
4183 if (aDCS.pSprm && aDCS.nRemainingData >= 1)
4184 nDropLines = (*aDCS.pSprm) >> 3;
4185 else // There is no Drop Cap Specifier hence no dropcap
4186 m_xPreviousNode.reset();
4187
4188 SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
4189 if (aDistance.pSprm && aDistance.nRemainingData >= 2)
4190 nDistance = SVBT16ToUInt16(aDistance.pSprm);
4191 else
4192 nDistance = 0;
4193
4194 const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
4195
4197 pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
4198
4199 if (pSwFormatCharFormat)
4200 pFormat = pSwFormatCharFormat->GetCharFormat();
4201
4202 if (m_xCurrentItemSet && !pFormat)
4203 {
4204 OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
4205 pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
4207 pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
4208 }
4209
4210 m_xCurrentItemSet.reset();
4211 m_bDropCap=false;
4212 }
4213
4214 if (bStartLine || m_bWasTabRowEnd)
4215 {
4216 // Call all 64 CRs; not for Header and the like
4217 if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
4218 {
4219 if (nTextLen < WW8_CP_MAX/100)
4220 m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
4221 else
4222 m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
4223 m_xProgress->Update(m_nProgress); // Update
4224 }
4225 }
4226
4227 // If we have encountered a 0x0c which indicates either section of
4228 // pagebreak then look it up to see if it is a section break, and
4229 // if it is not then insert a page break. If it is a section break
4230 // it will be handled as such in the ReadAttrs of the next loop
4231 if (m_bPgSecBreak)
4232 {
4233 // We need only to see if a section is ending at this cp,
4234 // the plcf will already be sitting on the correct location
4235 // if it is there.
4236 WW8PLCFxDesc aTemp;
4237 aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
4238 if (m_xPlcxMan->GetSepPLCF())
4239 m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
4240 if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
4241 {
4242 // #i39251# - insert text node for page break, if no one inserted.
4243 // #i43118# - refine condition: the anchor
4244 // control stack has to have entries, otherwise it's not needed
4245 // to insert a text node.
4246 if (!bStartLine && !m_xAnchorStck->empty())
4247 {
4249 }
4251 SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
4252 m_bFirstParaOfPage = true;
4253 m_bPgSecBreak = false;
4254 }
4255 }
4256 }
4257
4258 m_xPreviousNode.reset();
4259
4262
4263 if (!m_bInHyperlink)
4264 bJoined = JoinNode(*m_pPaM);
4265
4266 CloseAttrEnds();
4267
4268 m_xPlcxMan.reset();
4269 return bJoined;
4270}
4271
4273 SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
4274 : m_pDocShell(rD.GetDocShell())
4275 , m_pStg(pStorage)
4276 , m_pStrm(pSt)
4277 , m_pTableStream(nullptr)
4278 , m_pDataStream(nullptr)
4279 , m_rDoc(rD)
4280 , m_pPaM(nullptr)
4281 , m_aSectionManager(*this)
4282 , m_aExtraneousParas(rD)
4283 , m_aInsertedTables(rD)
4284 , m_aSectionNameGenerator(rD, "WW")
4285 , m_aGrfNameGenerator(bNewDoc, OUString('G'))
4286 , m_aParaStyleMapper(rD)
4287 , m_aCharStyleMapper(rD)
4288 , m_pFlyFormatOfJustInsertedGraphic(nullptr)
4289 , m_pPreviousNumPaM(nullptr)
4290 , m_pPrevNumRule(nullptr)
4291 , m_pCurrentColl(nullptr)
4292 , m_pDfltTextFormatColl(nullptr)
4293 , m_pStandardFormatColl(nullptr)
4294 , m_pDrawModel(nullptr)
4295 , m_pDrawPg(nullptr)
4296 , m_pNumFieldType(nullptr)
4297 , m_sBaseURL(std::move(aBaseURL))
4298 , m_nIniFlags(0)
4299 , m_nIniFlags1(0)
4300 , m_nFieldFlags(0)
4301 , m_bRegardHindiDigits( false )
4302 , m_bDrawCpOValid( false )
4303 , m_nDrawCpO(0)
4304 , m_nPicLocFc(0)
4305 , m_nObjLocFc(0)
4306 , m_nIniFlyDx(0)
4307 , m_nIniFlyDy(0)
4308 , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
4309 , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
4310 , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
4311 , m_nProgress(0)
4312 , m_nCurrentColl(0)
4313 , m_nFieldNum(0)
4314 , m_nLFOPosition(USHRT_MAX)
4315 , m_nCharFormat(0)
4316 , m_nDrawXOfs(0)
4317 , m_nDrawYOfs(0)
4318 , m_nDrawXOfs2(0)
4319 , m_nDrawYOfs2(0)
4320 , m_cSymbol(0)
4321 , m_nWantedVersion(nVersionPara)
4322 , m_nSwNumLevel(0xff)
4323 , m_nWwNumType(0xff)
4324 , m_pChosenWW8OutlineStyle(nullptr)
4325 , m_nListLevel(MAXLEVEL)
4326 , m_bNewDoc(bNewDoc)
4327 , m_bSkipImages(bSkipImages)
4328 , m_bReadNoTable(false)
4329 , m_bPgSecBreak(false)
4330 , m_bSpec(false)
4331 , m_bObj(false)
4332 , m_bTxbxFlySection(false)
4333 , m_bHasBorder(false)
4334 , m_bSymbol(false)
4335 , m_bIgnoreText(false)
4336 , m_nInTable(0)
4337 , m_bWasTabRowEnd(false)
4338 , m_bWasTabCellEnd(false)
4339 , m_bAnl(false)
4340 , m_bHdFtFootnoteEdn(false)
4341 , m_bFootnoteEdn(false)
4342 , m_bIsHeader(false)
4343 , m_bIsFooter(false)
4344 , m_bIsUnicode(false)
4345 , m_bCpxStyle(false)
4346 , m_bStyNormal(false)
4347 , m_bWWBugNormal(false)
4348 , m_bNoAttrImport(false)
4349 , m_bInHyperlink(false)
4350 , m_bWasParaEnd(false)
4351 , m_bVer67(false)
4352 , m_bVer6(false)
4353 , m_bVer7(false)
4354 , m_bVer8(false)
4355 , m_bEmbeddObj(false)
4356 , m_bCurrentAND_fNumberAcross(false)
4357 , m_bNoLnNumYet(true)
4358 , m_bFirstPara(true)
4359 , m_bFirstParaOfPage(false)
4360 , m_bParaAutoBefore(false)
4361 , m_bParaAutoAfter(false)
4362 , m_bDropCap(false)
4363 , m_nDropCap(0)
4364 , m_bBidi(false)
4365 , m_bReadTable(false)
4366 , m_bLoadingTOXCache(false)
4367 , m_nEmbeddedTOXLevel(0)
4368 , m_bLoadingTOXHyperlink(false)
4369 , m_bCareFirstParaEndInToc(false)
4370 , m_bCareLastParaEndInToc(false)
4371 , m_bNotifyMacroEventRead(false)
4372 , m_bFuzzing(utl::ConfigManager::IsFuzzing())
4373{
4374 m_pStrm->SetEndian( SvStreamEndian::LITTLE );
4375 m_aApos.push_back(false);
4376
4378}
4379
4381{
4382}
4383
4384void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
4385{
4386 if( pStck )
4387 {
4388 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4389 pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
4390 }
4391 else
4392 {
4393 OSL_ENSURE( false, "WW stack already deleted" );
4394 }
4395}
4396
4398 bool bIgnoreCols)
4399{
4400 SwPageDesc &rPage = *rSection.mpPage;
4401
4402 SetNumberingType(rSection, rPage);
4403
4404 SwFrameFormat &rFormat = rPage.GetMaster();
4405
4406 if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
4408
4409 if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
4410 {
4411 tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
4414 if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
4415 {
4416 // Only handle shape if it is a background shape
4417 if (aData.begin()->get()->nFlags & ShapeFlag::Background)
4418 {
4420 aSet(rFormat.GetDoc()->GetAttrPool());
4423 if ( aSet.HasItem(RES_BACKGROUND) )
4424 rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
4425 else
4426 rFormat.SetFormatAttr(aSet);
4427 }
4428 }
4429 }
4430 wwULSpaceData aULData;
4431 GetPageULData(rSection, aULData);
4432 SetPageULSpaceItems(rFormat, aULData, rSection);
4433
4435
4436 SetPage(rPage, rFormat, rSection, bIgnoreCols);
4437
4438 if (!(rSection.maSep.pgbApplyTo & 1))
4439 SwWW8ImplReader::SetPageBorder(rFormat, rSection);
4440 if (!(rSection.maSep.pgbApplyTo & 2))
4442
4443 mrReader.SetDocumentGrid(rFormat, rSection);
4444}
4445
4447{
4448 bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
4449 mrReader.m_xWDop->doptypography.m_f2on1;
4450
4451 UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
4452 UseOnPage eUse = eUseBase;
4453 if (!mrReader.m_xWDop->fFacingPages)
4455 if (!rSection.HasTitlePage())
4456 eUse |= UseOnPage::FirstShare;
4457
4458 OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
4459 if (rSection.mpPage)
4460 rSection.mpPage->WriteUseOn(eUse);
4461}
4462
4467static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
4468 SwDoc &rDoc)
4469{
4470 /*
4471 If it's a table here, apply the pagebreak to the table
4472 properties, otherwise we add it to the para at this
4473 position
4474 */
4475 if (rIdx.GetNode().IsTableNode())
4476 {
4477 SwTable& rTable =
4478 rIdx.GetNode().GetTableNode()->GetTable();
4479 SwFrameFormat* pApply = rTable.GetFrameFormat();
4480 OSL_ENSURE(pApply, "impossible");
4481 if (pApply)
4482 pApply->SetFormatAttr(rPgDesc);
4483 }
4484 else
4485 {
4486 SwPaM aPage(rIdx);
4487 rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
4488 }
4489}
4490
4495 mySegIter const &rStart, bool bIgnoreCols)
4496{
4497 if (mrReader.m_bNewDoc && rIter == rStart)
4498 {
4499 rIter->mpPage =
4501 }
4502 else
4503 {
4504 rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
4506 nullptr, false);
4507 }
4508 OSL_ENSURE(rIter->mpPage, "no page!");
4509 if (!rIter->mpPage)
4510 return SwFormatPageDesc();
4511
4512 // Set page before hd/ft
4513 const wwSection *pPrevious = nullptr;
4514 if (rIter != rStart)
4515 pPrevious = &(*(rIter-1));
4516 SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
4517 SetUseOn(*rIter);
4518
4519 // Set hd/ft after set page
4520 SetSegmentToPageDesc(*rIter, bIgnoreCols);
4521
4522 SwFormatPageDesc aRet(rIter->mpPage);
4523
4524 rIter->mpPage->SetFollow(rIter->mpPage);
4525
4526 if (rIter->PageRestartNo())
4527 aRet.SetNumOffset(rIter->PageStartAt());
4528
4529 ++mnDesc;
4530 return aRet;
4531}
4532
4534{
4535 mySegIter aEnd = maSegments.end();
4536 mySegIter aStart = maSegments.begin();
4537 for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
4538 {
4539 // If the section is of type "New column" (0x01), then simply insert a column break.
4540 // But only if there actually are columns on the page, otherwise a column break
4541 // seems to be handled like a page break by MSO.
4542 if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
4543 {
4544 SwPaM start( aIter->maStart );
4546 continue;
4547 }
4548
4549 mySegIter aNext = aIter+1;
4550 mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
4551
4552 // If two following sections are different in following properties, Word will interpret a continuous
4553 // section break between them as if it was a section break next page.
4554 bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
4555 (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
4556
4557 bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
4558 bool bInsertPageDesc = !bInsertSection;
4559 // HACK Force new pagedesc if left/right margins change, otherwise e.g. floating tables may be anchored improperly.
4560 if( aIter->maSep.dxaLeft != aPrev->maSep.dxaLeft || aIter->maSep.dxaRight != aPrev->maSep.dxaRight )
4561 bInsertPageDesc = true;
4562 bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
4563
4564 if (bInsertPageDesc)
4565 {
4566 /*
4567 If a cont section follows this section then we won't be
4568 creating a page desc with 2+ cols as we cannot host a one
4569 col section in a 2+ col pagedesc and make it look like
4570 word. But if the current section actually has columns then
4571 we are forced to insert a section here as well as a page
4572 descriptor.
4573 */
4574
4575 bool bIgnoreCols = bInsertSection;
4576 bool bThisAndNextAreCompatible = (aNext == aEnd) ||
4577 ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
4578 (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
4579 (aIter->IsLandScape() == aNext->IsLandScape()));
4580
4581 if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
4582 {
4583 bIgnoreCols = true;
4584 if ((aIter->NoCols() > 1) || bProtected)
4585 bInsertSection = true;
4586 }
4587
4588 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4589 if (!aDesc.GetPageDesc())
4590 continue;
4591
4592 // special case handling for odd/even section break
4593 // a) as before create a new page style for the section break
4594 // b) set Layout of generated page style to right/left ( according
4595 // to section break odd/even )
4596 // c) create a new style to follow the break page style
4597 if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
4598 {
4599 // SetSwFormatPageDesc calls some methods that could
4600 // modify aIter (e.g. wwSection ).
4601 // Since we call SetSwFormatPageDesc below to generate the
4602 // 'Following' style of the Break style, it is safer
4603 // to take a copy of the contents of aIter.
4604 wwSection aTmpSection = *aIter;
4605 // create a new following page style
4606 SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
4607 // restore any contents of aIter trashed by SetSwFormatPageDesc
4608 *aIter = aTmpSection;
4609
4610 // Handle the section break
4611 UseOnPage eUseOnPage = UseOnPage::Left;
4612 if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
4613 eUseOnPage = UseOnPage::Right;
4614
4615 // Keep the share flags.
4616 aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
4617 aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
4618 }
4619
4620 // Avoid setting the page style at the very beginning since it is always the default style anyway,
4621 // unless it is needed to specify a page number.
4622 if (aIter != aStart || aDesc.GetNumOffset())
4623 GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
4624 }
4625
4626 SwTextNode* pTextNd = nullptr;
4627 if (bInsertSection)
4628 {
4629 // Start getting the bounds of this section
4630 SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
4631 SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
4632 if (aNext != aEnd)
4633 {
4634 aAnchor = aNext->maStart;
4635 aSectPaM.GetPoint()->Assign(aAnchor);
4636 aSectPaM.Move(fnMoveBackward);
4637 }
4638
4639 const SwPosition* pPos = aSectPaM.GetPoint();
4640 SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
4641 const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
4642 if (pTableNd)
4643 {
4644 pTextNd =
4647
4648 aSectPaM.GetPoint()->Assign(*pTextNd, 0);
4649 }
4650
4651 aSectPaM.SetMark();
4652
4653 aSectPaM.GetPoint()->Assign(aIter->maStart);
4654
4655 bool bHasOwnHdFt = false;
4656 /*
4657 In this nightmare scenario the continuous section has its own
4658 headers and footers so we will try and find a hard page break
4659 between here and the end of the section and put the headers and
4660 footers there.
4661 */
4662 if (!bInsertPageDesc)
4663 {
4664 bHasOwnHdFt =
4666 aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
4667 aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
4668 );
4669 }
4670 if (bHasOwnHdFt)
4671 {
4672 // #i40766# Need to cache the page descriptor in case there is
4673 // no page break in the section
4674 SwPageDesc *pOrig = aIter->mpPage;
4675 bool bFailed = true;
4676 SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
4677 if (aDesc.GetPageDesc())
4678 {
4679 SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
4680 SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
4681 for(; nStart <= nEnd; ++nStart)
4682 {
4683 SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
4684 if (!pNode)
4685 continue;
4686 if (sw::util::HasPageBreak(*pNode))
4687 {
4688 SwNodeIndex aIdx(*pNode);
4689 GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
4690 bFailed = false;
4691 break;
4692 }
4693 }
4694 }
4695 if(bFailed)
4696 {
4697 aIter->mpPage = pOrig;
4698 }
4699 }
4700
4701 // End getting the bounds of this section, quite a job eh?
4702 SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
4703 // The last section if continuous is always unbalanced
4704 if (pRet)
4705 {
4706 // Set the columns to be UnBalanced if that compatibility option is set
4707 if (mrReader.m_xWDop->fNoColumnBalance)
4709 else
4710 {
4711 // Otherwise set to unbalanced if the following section is
4712 // not continuous, (which also means that the last section
4713 // is unbalanced)
4714 if (aNext == aEnd || !aNext->IsContinuous())
4716 }
4717 }
4718 }
4719
4720 if (pTextNd)
4721 {
4722 SwPaM aTest(*pTextNd);
4724 pTextNd = nullptr;
4725 }
4726 }
4727}
4728
4730{
4731 auto aEnd = m_aTextNodes.rend();
4732 for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
4733 {
4734 ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
4735 SwTextNode* pTextNode = rListener.GetTextNode();
4736 rListener.StopListening(pTextNode);
4737
4738 SwPaM aTest(*pTextNode);
4740 }
4741 m_aTextNodes.clear();
4742}
4743
4745{
4746 m_aTextNodes.emplace(pTextNode, this);
4747}
4748
4750{
4751 auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
4752 [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
4753 if (it == m_aTextNodes.end())
4754 return;
4755 SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
4756 m_aTextNodes.erase(it);
4757}
4758
4760 : m_pTextNode(pTextNode)
4761{
4762 m_pTextNode->Add(this);
4763}
4764
4766{
4767 if (!m_pTextNode)
4768 return;
4770}
4771
4772void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4773{
4774 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4775 return;
4776 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4777 // ofz#41398 drop a para scheduled for deletion if something else deletes it
4778 // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
4779 // indicates an underlying bug.
4780 if (pLegacy->GetWhich() == RES_OBJECTDYING)
4781 removed(const_cast<SwModify*>(&rModify));
4782}
4783
4785{
4786 pTextNode->Remove(this);
4787 m_pTextNode = nullptr;
4788}
4789
4791{
4792 StopListening(pTextNode);
4793}
4794
4796{
4797 m_pOwner->remove_if_present(pTextNode);
4798}
4799
4801{
4802 if (!m_xWwFib->m_lcbCmds)
4803 return;
4804
4805 bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
4806 if (!bValidPos)
4807 return;
4808
4809 uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
4810
4811 if (!xRoot.is())
4812 return;
4813
4814 try
4815 {
4816 uno::Reference < io::XStream > xStream =
4817 xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
4818 std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
4819
4820 sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
4821 std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
4822 m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
4823 xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
4824 }
4825 catch (...)
4826 {
4827 }
4828}
4829
4831{
4832 std::vector<OUString> aDocVarStrings;
4833 std::vector<ww::bytes> aDocVarStringIds;
4834 std::vector<OUString> aDocValueStrings;
4835 WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
4836 m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
4837 aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
4838 if (m_bVer67) return;
4839
4840 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4841 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4842 uno::Reference<document::XDocumentProperties> xDocProps(
4843 xDPS->getDocumentProperties());
4844 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4845 uno::Reference<beans::XPropertyContainer> xUserDefinedProps =
4846 xDocProps->getUserDefinedProperties();
4847 OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null");
4848
4849 for(size_t i=0; i<aDocVarStrings.size(); i++)
4850 {
4851 const OUString &rName = aDocVarStrings[i];
4852 uno::Any aValue;
4853 aValue <<= rName;
4854 try {
4855 xUserDefinedProps->addProperty( rName,
4856 beans::PropertyAttribute::REMOVABLE,
4857 aValue );
4858 } catch (const uno::Exception &) {
4859 // ignore
4860 }
4861 }
4862}
4863
4868{
4869 if( !m_pStg )
4870 return;
4871
4872 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
4873 m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
4874 uno::Reference<document::XDocumentProperties> xDocProps(
4875 xDPS->getDocumentProperties());
4876 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
4877
4878 if (!xDocProps.is())
4879 return;
4880
4881 if ( m_xWwFib->m_fDot )
4882 {
4883 SfxMedium* pMedium = m_pDocShell->GetMedium();
4884 if ( pMedium )
4885 {
4886 const OUString& aName = pMedium->GetName();
4888 OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
4889 if ( !sTemplateURL.isEmpty() )
4890 xDocProps->setTemplateURL( sTemplateURL );
4891 }
4892 }
4893 else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
4894 {
4895 auto nCur = m_pTableStream->Tell();
4896 Sttb aSttb;
4897 // point at tgc record
4898 if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
4899 SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
4900 m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
4901 OUString sPath = aSttb.getStringAtIndex( 0x1 );
4902 OUString aURL;
4903 // attempt to convert to url (won't work for obvious reasons on linux)
4904 if ( !sPath.isEmpty() )
4905 osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
4906 if (aURL.isEmpty())
4907 xDocProps->setTemplateURL( aURL );
4908 else
4909 xDocProps->setTemplateURL( sPath );
4910
4911 }
4912 sfx2::LoadOlePropertySet(xDocProps, m_pStg);
4913}
4914
4915static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
4916{
4917 if ( !xPrjNameCache.is() )
4918 return;
4919
4920 INetURLObject aObj;
4921 aObj.SetURL( sTemplatePathOrURL );
4922 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
4923 OUString aURL;
4924 if ( bIsURL )
4925 aURL = sTemplatePathOrURL;
4926 else
4927 {
4928 osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
4929 aObj.SetURL( aURL );
4930 }
4931 try
4932 {
4933 OUString templateNameWithExt = aObj.GetLastName();
4934 sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
4935 if ( nIndex != -1 )
4936 {
4937 OUString templateName = templateNameWithExt.copy( 0, nIndex );
4938 xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
4939 }
4940 }
4941 catch( const uno::Exception& )
4942 {
4943 }
4944}
4945
4946namespace {
4947
4948class WW8Customizations
4949{
4950 SvStream* mpTableStream;
4951 WW8Fib mWw8Fib;
4952public:
4953 WW8Customizations( SvStream*, WW8Fib const & );
4954 void Import( SwDocShell* pShell );
4955};
4956
4957}
4958
4959WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
4960{
4961}
4962
4963void WW8Customizations::Import( SwDocShell* pShell )
4964{
4965 if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
4966 return;
4967 try
4968 {
4969 Tcg aTCG;
4970 sal_uInt64 nCur = mpTableStream->Tell();
4971 if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
4972 {
4973 SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
4974 return;
4975 }
4976 bool bReadResult = aTCG.Read( *mpTableStream );
4977 mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
4978 if ( !bReadResult )
4979 {
4980 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
4981 return;
4982 }
4983 aTCG.ImportCustomToolBar( *pShell );
4984 }
4985 catch(...)
4986 {
4987 SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
4988 }
4989}
4990
4991void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
4992{
4993 if (m_bFuzzing)
4994 return;
4995
4996 SvtPathOptions aPathOpt;
4997 const OUString& aAddinPath = aPathOpt.GetAddinPath();
4998 uno::Sequence< OUString > sGlobalTemplates;
4999
5000 // first get the autoload addins in the directory STARTUP
5001 uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
5002
5003 if( xSFA->isFolder( aAddinPath ) )
5004 sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
5005
5006 for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) )
5007 {
5008 INetURLObject aObj;
5009 aObj.SetURL( rGlobalTemplate );
5010 bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
5011 OUString aURL;
5012 if ( bIsURL )
5013 aURL = rGlobalTemplate;
5014 else
5015 osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
5016 if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
5017 continue; // don't try and read the same document as ourselves
5018
5019 tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE );
5020
5021 BasicProjImportHelper aBasicImporter( *m_pDocShell );
5022 // Import vba via oox filter
5023 aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
5024 lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
5025 // Read toolbars & menus
5026 tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument");
5027 refMainStream->SetEndian(SvStreamEndian::LITTLE);
5028 WW8Fib aWwFib( *refMainStream, 8 );
5029 tools::SvRef<SotStorageStream> xTableStream =
5030 rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? OUString(SL::a1Table) : OUString(SL::a0Table), StreamMode::STD_READ);
5031
5032 if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
5033 {
5034 xTableStream->SetEndian(SvStreamEndian::LITTLE);
5035 WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
5036 aGblCustomisations.Import( m_pDocShell );
5037 }
5038 }
5039}
5040
5042{
5044 if (m_bNewDoc && m_pStg && !pGloss)
5045 {
5046 // Initialize RDF metadata, to be able to add statements during the import.
5047 try
5048 {
5049 uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
5050 uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
5051 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
5052 uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
5053 const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
5054 uno::Reference<task::XInteractionHandler> xHandler;
5055 xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
5056 }
5057 catch (const uno::Exception&)
5058 {
5059 TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
5060 }
5061 ReadDocInfo();
5062 }
5063
5064 auto pFibData = std::make_shared<::ww8::WW8FibData>();
5065
5066 if (m_xWwFib->m_fReadOnlyRecommended)
5067 pFibData->setReadOnlyRecommended(true);
5068 else
5069 pFibData->setReadOnlyRecommended(false);
5070
5071 if (m_xWwFib->m_fWriteReservation)
5072 pFibData->setWriteReservation(true);
5073 else
5074 pFibData->setWriteReservation(false);
5075
5076 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
5077
5079 = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
5080
5081 m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
5082
5083 if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
5084 {
5085 SwDocShell * pDocShell = m_rDoc.GetDocShell();
5086 if (pDocShell)
5087 pDocShell->SetReadOnlyUI();
5088 }
5089
5090 m_pPaM = mpCursor.get();
5091