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