LibreOffice Module sw (master) 1
ww8par6.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 <stdlib.h>
21#include <o3tl/safeint.hxx>
23#include <svl/itemiter.hxx>
24#include <svl/grabbagitem.hxx>
25#include <rtl/tencinfo.h>
26#include <sal/log.hxx>
27
28#include <hintids.hxx>
29#include <editeng/lspcitem.hxx>
30#include <editeng/wrlmitem.hxx>
31#include <editeng/udlnitem.hxx>
32#include <editeng/kernitem.hxx>
33#include <editeng/langitem.hxx>
34#include <editeng/cmapitem.hxx>
35#include <editeng/shdditem.hxx>
38#include <editeng/postitem.hxx>
39#include <editeng/wghtitem.hxx>
40#include <editeng/colritem.hxx>
41#include <editeng/brushitem.hxx>
42#include <editeng/spltitem.hxx>
43#include <editeng/keepitem.hxx>
44#include <editeng/orphitem.hxx>
45#include <editeng/widwitem.hxx>
48#include <editeng/fhgtitem.hxx>
49#include <editeng/fontitem.hxx>
50#include <editeng/shaditem.hxx>
51#include <editeng/boxitem.hxx>
52#include <editeng/ulspitem.hxx>
53#include <editeng/lrspitem.hxx>
54#include <editeng/tstpitem.hxx>
56#include <editeng/paperinf.hxx>
62#include <editeng/blinkitem.hxx>
65#include <editeng/pgrditem.hxx>
69#include <svx/xfillit0.hxx>
70#include <svx/xflclit.hxx>
71#include <officecfg/Office/Writer.hxx>
72#include "sortedarray.hxx"
73#include "sprmids.hxx"
74#include <node.hxx>
75#include <ndtxt.hxx>
76#include <pam.hxx>
77#include <doc.hxx>
79#include <pagedesc.hxx>
80#include <fmtanchr.hxx>
81#include <fmtcntnt.hxx>
82#include <fchrfmt.hxx>
83#include <fmthdft.hxx>
84#include <fmtclds.hxx>
85#include <fmtftntx.hxx>
86#include <frmatr.hxx>
87#include <section.hxx>
88#include <lineinfo.hxx>
89#include <fmtline.hxx>
90#include <txatbase.hxx>
91#include <fmtflcnt.hxx>
92#include <tgrditem.hxx>
93#include <hfspacingitem.hxx>
94#include <swtable.hxx>
95#include <fltini.hxx>
96#include "writerhelper.hxx"
97#include "writerwordglue.hxx"
98#include "ww8scan.hxx"
99#include "ww8par2.hxx"
100#include "ww8graf.hxx"
101
103#include <formatflysplit.hxx>
104
105using namespace sw::util;
106using namespace sw::types;
107using namespace ::com::sun::star;
108using namespace nsHdFtFlags;
109
110// various
111
112// WW default for horizontal borders: 2.5 cm
114// WW default for lower border: 2.0 cm
116
117
118static sal_uInt8 lcl_ReadBorders(bool bVer67, WW8_BRCVer9* brc, WW8PLCFx_Cp_FKP* pPap,
119 const WW8RStyle* pSty = nullptr, const WW8PLCFx_SEPX* pSep = nullptr);
120
122{
123 static const Color eSwWW8ColA[] =
124 {
129 };
131 nIco >= SAL_N_ELEMENTS(eSwWW8ColA), "sw.ww8",
132 "ico " << sal_uInt32(nIco) << " >= " << SAL_N_ELEMENTS(eSwWW8ColA));
133 return nIco < SAL_N_ELEMENTS(eSwWW8ColA) ? eSwWW8ColA[nIco] : COL_AUTO;
134}
135
136static sal_uInt32 MSRoundTweak(sal_uInt32 x)
137{
138 return x;
139}
140
141// page attribute which are not handled via the attribute management but
142// using ...->HasSprm
143// (except OLST which stays a normal attribute)
144static short ReadSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal )
145{
146 SprmResult aRes = pSep->HasSprm(nId); // sprm here?
147 const sal_uInt8* pS = aRes.pSprm;
148 short nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToInt16(pS) : nDefaultVal;
149 return nVal;
150}
151
152static sal_uInt16 ReadUSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal )
153{
154 SprmResult aRes = pSep->HasSprm(nId); // sprm here?
155 const sal_uInt8* pS = aRes.pSprm;
156 sal_uInt16 nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToUInt16(pS) : nDefaultVal;
157 return nVal;
158}
159
160static sal_uInt8 ReadBSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, sal_uInt8 nDefaultVal )
161{
162 SprmResult aRes = pSep->HasSprm(nId); // sprm here?
163 const sal_uInt8* pS = aRes.pSprm;
164 sal_uInt8 nVal = (pS && aRes.nRemainingData >= 1) ? *pS : nDefaultVal;
165 return nVal;
166}
167
169{
170 //sprmSTextFlow
171 switch (maSep.wTextFlow)
172 {
173 default:
174 OSL_ENSURE(false, "Unknown layout type");
175 [[fallthrough]];
176 case 0:
177 meDir=SvxFrameDirection::Horizontal_LR_TB;
178 break;
179 case 1:
180 meDir=SvxFrameDirection::Vertical_RL_TB;
181 break;
182 case 2:
183 //asian letters are not rotated, western are. We can't import
184 //bottom to top going left to right, we can't do this in
185 //pages, (in drawboxes we could partly hack it with a rotated
186 //drawing box, though not frame)
187 meDir=SvxFrameDirection::Vertical_RL_TB;
188 break;
189 case 3:
190 //asian letters are not rotated, western are. We can't import
191 meDir=SvxFrameDirection::Vertical_RL_TB;
192 break;
193 case 4:
194 //asian letters are rotated, western not. We can't import
195 meDir=SvxFrameDirection::Horizontal_LR_TB;
196 break;
197 }
198
199 sal_uInt8 bRTLPgn = maSep.fBiDi;
200 if ((meDir == SvxFrameDirection::Horizontal_LR_TB) && bRTLPgn)
201 meDir = SvxFrameDirection::Horizontal_RL_TB;
202}
203
205{
206 return meDir == SvxFrameDirection::Vertical_RL_TB || meDir == SvxFrameDirection::Vertical_LR_TB;
207}
208
209/*
210 This is something of festering mapping, I'm open to better ways of doing it,
211 but primarily the grid in writer is different to that in word. In writer the
212 grid elements are squares with ruby rows inbetween. While in word there is no
213 ruby stuff, and the elements are rectangles. By misusing the ruby row I can
214 handle distortions in one direction, but its all a bit of a mess:
215*/
217{
218 if (m_bVer67)
219 return;
220
222
223 SwTwips nTextareaHeight = rFormat.GetFrameSize().GetHeight();
224 const SvxULSpaceItem &rUL = rFormat.GetFormatAttr(RES_UL_SPACE);
225 nTextareaHeight -= rUL.GetUpper();
226 nTextareaHeight -= rUL.GetLower();
227
228 SwTwips nTextareaWidth = rFormat.GetFrameSize().GetWidth();
229 const SvxLRSpaceItem &rLR = rFormat.GetFormatAttr(RES_LR_SPACE);
230 nTextareaWidth -= rLR.GetLeft();
231 nTextareaWidth -= rLR.GetRight();
232
233 if (rSection.IsVertical())
234 std::swap(nTextareaHeight, nTextareaWidth);
235
236 SwTextGridItem aGrid;
237 aGrid.SetDisplayGrid(false);
238 aGrid.SetPrintGrid(false);
240
241 switch (rSection.maSep.clm)
242 {
243 case 0:
245 break;
246 default:
247 OSL_ENSURE(false, "Unknown grid type");
248 [[fallthrough]];
249 case 3:
251 aGrid.SetSnapToChars(true);
252 break;
253 case 1:
255 aGrid.SetSnapToChars(false);
256 break;
257 case 2:
259 break;
260 }
261
262 aGrid.SetGridType(eType);
263
264 // seem to not add external leading in word, or the character would run across
265 // two line in some cases.
266 // TODO: tdf#85435 suggests that this is wrong
267 if (eType != GRID_NONE)
269
270 //force to set document as standard page mode
271 bool bSquaredMode = false;
272 m_rDoc.SetDefaultPageMode( bSquaredMode );
273 aGrid.SetSquaredMode( bSquaredMode );
274
275 //Get the size of word's default styles font
276 sal_uInt32 nCharWidth=240;
277 for (sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); ++nI)
278 {
279 if (m_vColl[nI].m_bValid && m_vColl[nI].m_pFormat &&
280 m_vColl[nI].IsWW8BuiltInDefaultStyle())
281 {
282 const SvxFontHeightItem& rFontHeightItem =
283 m_vColl[nI].m_pFormat->GetFormatAttr(RES_CHRATR_CJK_FONTSIZE);
284 nCharWidth = rFontHeightItem.GetHeight();
285 break;
286 }
287 }
288
289 //dxtCharSpace
290 if (rSection.maSep.dxtCharSpace)
291 {
292 sal_uInt32 nCharSpace = rSection.maSep.dxtCharSpace;
293 //main lives in top 20 bits, and is signed.
294 sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
295 nMain/=0x1000;
296 nCharWidth += nMain*20;
297
298 int nFraction = (nCharSpace & 0x00000FFF);
299 nFraction = (nFraction*20)/0xFFF;
300 nCharWidth += nFraction;
301 }
302
303 aGrid.SetBaseWidth( writer_cast<sal_uInt16>(nCharWidth));
304
305 //sep.dyaLinePitch
306 sal_Int32 nLinePitch = rSection.maSep.dyaLinePitch;
307 if (nLinePitch >= 1 && nLinePitch <= 31680)
308 {
309 aGrid.SetLines(writer_cast<sal_uInt16>(nTextareaHeight/nLinePitch));
310 aGrid.SetBaseHeight(writer_cast<sal_uInt16>(nLinePitch));
311 }
312
313 aGrid.SetRubyHeight(0);
314
315 rFormat.SetFormatAttr(aGrid);
316}
317
319{
320 if ( m_pCurrentColl && StyleExists(m_nCurrentColl) ) // importing style
321 m_vColl[m_nCurrentColl].m_nRelativeJustify = bRel ? 1 : 0;
322 else if ( m_xPlcxMan && m_xPlcxMan->GetPap() ) // importing paragraph
323 m_xPlcxMan->GetPap()->nRelativeJustify = bRel ? 1 : 0;
324}
325
327{
328 bool bRet = m_xWwFib->GetFIBVersion() >= ww::eWW8;
329 if ( bRet )
330 {
331 // if relativeJustify is undefined (-1), then check the parent style.
333 {
334 sal_Int16 nRelative = m_vColl[m_nCurrentColl].m_nRelativeJustify;
335 if ( nRelative < 0 && m_nCurrentColl )
336 {
337 o3tl::sorted_vector<sal_uInt16> aVisitedStyles;
338 bRet = IsRelativeJustify(m_vColl[m_nCurrentColl].m_nBase, aVisitedStyles);
339 }
340 else
341 bRet = nRelative > 0;
342 }
343 else if ( m_xPlcxMan && m_xPlcxMan->GetPap() )
344 {
345 sal_Int16 nRelative = m_xPlcxMan->GetPap()->nRelativeJustify;
346 if ( nRelative < 0 )
347 {
348 o3tl::sorted_vector<sal_uInt16> aVisitedStyles;
349 bRet = IsRelativeJustify(m_nCurrentColl, aVisitedStyles);
350 }
351 else
352 bRet = nRelative > 0;
353 }
354 }
355
356 return bRet;
357}
358
360{
361 assert( m_xWwFib->GetFIBVersion() >= ww::eWW8
362 && "pointless to search styles if relative justify is impossible");
363 bool bRet = true;
364 if ( StyleExists(nColl) )
365 {
366 rVisitedStyles.insert(nColl);
367 // if relativeJustify is undefined (-1), then check the parent style.
368 sal_Int16 nRelative = m_vColl[nColl].m_nRelativeJustify;
369 if ( nColl == 0 || nRelative >= 0 )
370 bRet = nRelative > 0;
371 else if (rVisitedStyles.find(m_vColl[nColl].m_nBase) == rVisitedStyles.end()) // detect loop in chain
372 bRet = IsRelativeJustify(m_vColl[nColl].m_nBase, rVisitedStyles);
373 }
374
375 return bRet;
376}
377
378void SwWW8ImplReader::Read_ParaBiDi(sal_uInt16, const sal_uInt8* pData, short nLen)
379{
380 if (nLen < 1)
382 else
383 {
384 SvxFrameDirection eDir =
385 *pData ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB;
386
387 // In eWW8+, justify can be absolute, or relative to BiDi
388 bool bBiDiSwap = IsRelativeJustify();
389 if ( bBiDiSwap )
390 {
391 // Only change if ParaBiDi doesn't match previous setting.
392 const bool bParentRTL = IsRightToLeft();
393 bBiDiSwap = (eDir == SvxFrameDirection::Horizontal_RL_TB && !bParentRTL)
394 || (eDir == SvxFrameDirection::Horizontal_LR_TB && bParentRTL);
395 }
396
397 if ( bBiDiSwap )
398 {
399 const SvxAdjustItem* pItem = static_cast<const SvxAdjustItem*>(GetFormatAttr(RES_PARATR_ADJUST));
400 if ( !pItem )
401 {
402 // no previous adjust: set appropriate default
403 if ( eDir == SvxFrameDirection::Horizontal_LR_TB )
404 NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
405 else
406 NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) );
407 }
408 else
409 {
410 // previous adjust and bidi has changed: swap Left/Right
411 const SvxAdjust eJustify = pItem->GetAdjust();
412 if ( eJustify == SvxAdjust::Left )
413 NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) );
414 else if ( eJustify == SvxAdjust::Right )
415 NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
416 }
417 }
418
420
421 if ( m_pCurrentColl && m_xStyles ) // in style definition
422 m_xStyles->mbBidiChanged = true;
423 }
424}
425
426bool wwSectionManager::SetCols(SwFrameFormat &rFormat, const wwSection &rSection,
427 sal_uInt32 nNetWidth)
428{
429 //sprmSCcolumns - number of columns - 1
430 const sal_Int16 nCols = rSection.NoCols();
431
432 if (nCols < 2) //check for no columns or other weird state
433 return false;
434
435 const sal_uInt16 nNetWriterWidth = writer_cast<sal_uInt16>(nNetWidth);
436 if (nNetWriterWidth == 0)
437 return false;
438
439 SwFormatCol aCol; // Create SwFormatCol
440
441 //sprmSDxaColumns - Default distance is 1.25 cm
442 sal_Int32 nColSpace = rSection.StandardColSeparation();
443
444 const SEPr& rSep = rSection.maSep;
445
446 // sprmSLBetween
447 if (rSep.fLBetween)
448 {
449 aCol.SetLineAdj(COLADJ_TOP); // Line
450 aCol.SetLineHeight(100);
452 aCol.SetLineWidth(1);
453 }
454
455 aCol.Init(nCols, writer_cast<sal_uInt16>(nColSpace), nNetWriterWidth);
456
457 // sprmSFEvenlySpaced
458 if (!rSep.fEvenlySpaced)
459 {
460 aCol.SetOrtho_(false);
461 const sal_uInt16 maxIdx = SAL_N_ELEMENTS(rSep.rgdxaColumnWidthSpacing);
462 for (sal_uInt16 i = 0, nIdx = 1; i < nCols && nIdx < maxIdx; i++, nIdx+=2 )
463 {
464 SwColumn* pCol = &aCol.GetColumns()[i];
465 const sal_Int32 nLeft = rSep.rgdxaColumnWidthSpacing[nIdx-1]/2;
466 const sal_Int32 nRight = rSep.rgdxaColumnWidthSpacing[nIdx+1]/2;
467 const sal_Int32 nWishWidth = rSep.rgdxaColumnWidthSpacing[nIdx]
468 + nLeft + nRight;
469 pCol->SetWishWidth(writer_cast<sal_uInt16>(nWishWidth));
470 pCol->SetLeft(writer_cast<sal_uInt16>(nLeft));
471 pCol->SetRight(writer_cast<sal_uInt16>(nRight));
472 }
473 aCol.SetWishWidth(nNetWriterWidth);
474 }
475 rFormat.SetFormatAttr(aCol);
476 return true;
477}
478
480{
481 // 3. LR-Margin
482 sal_uInt32 nWWLe = MSRoundTweak(rSection.maSep.dxaLeft);
483 sal_uInt32 nWWRi = MSRoundTweak(rSection.maSep.dxaRight);
484 sal_uInt32 nWWGu = rSection.maSep.dzaGutter;
485
486 /*
487 fRTLGutter is set if the gutter is on the right, the gutter is otherwise
488 placed on the left unless the global dop options are to put it on top, that
489 case is handled in GetPageULData.
490 */
491 if (rSection.maSep.fRTLGutter)
492 {
493 rSection.m_bRtlGutter = true;
494 }
495
496 // Left / Right
497 if ((rSection.m_nPgWidth - nWWLe - nWWRi) < MINLAY)
498 {
499 /*
500 There are some label templates which are "broken", they specify
501 margins which make no sense e.g. Left 16.10cm, Right 16.10cm. So the
502 space left between the margins is less than 0 In word the left margin
503 is honoured and if the right margin would be past the left margin is
504 left at the left margin position.
505
506 Now this will work fine for importing, layout and exporting, *but* the
507 page layout dialog has a hardcoded minimum page width of 0.5cm so it
508 will report a different value than what is actually being used. i.e.
509 it will add up the values to give a wider page than is actually being
510 used.
511 */
512 nWWRi = rSection.m_nPgWidth - nWWLe - MINLAY;
513 }
514
515 rSection.m_nPgLeft = nWWLe;
516 rSection.m_nPgRight = nWWRi;
517 rSection.m_nPgGutter = nWWGu;
518}
519
521 const wwSection &rSection, bool bIgnoreCols)
522{
523 // 1. orientation
524 rInPageDesc.SetLandscape(rSection.IsLandScape());
525
526 // 2. paper size
527 SwFormatFrameSize aSz( rFormat.GetFrameSize() );
528 aSz.SetWidth(rSection.GetPageWidth());
530 rFormat.SetFormatAttr(aSz);
531
532 SvxLRSpaceItem aLR(rSection.GetPageLeft(), rSection.GetPageRight(), 0, RES_LR_SPACE);
533 aLR.SetGutterMargin(rSection.m_nPgGutter);
534 rFormat.SetFormatAttr(aLR);
535
536 SfxBoolItem aRtlGutter(RES_RTL_GUTTER, rSection.m_bRtlGutter);
537 rFormat.SetFormatAttr(aRtlGutter);
538
539 if (!bIgnoreCols)
540 SetCols(rFormat, rSection, rSection.GetTextAreaWidth());
541}
542
543namespace {
544// Returns corrected (ODF) margin size
545tools::Long SetBorderDistance(bool bFromEdge, SvxBoxItem& aBox, SvxBoxItemLine eLine, tools::Long nMSMargin)
546{
547 const editeng::SvxBorderLine* pLine = aBox.GetLine(eLine);
548 if (!pLine)
549 return nMSMargin;
550 sal_Int32 nNewMargin = nMSMargin;
551 sal_Int32 nNewDist = aBox.GetDistance(eLine);
552 sal_Int32 nLineWidth = pLine->GetScaledWidth();
553
554 editeng::BorderDistanceFromWord(bFromEdge, nNewMargin, nNewDist, nLineWidth);
555 aBox.SetDistance(nNewDist, eLine);
556
557 return nNewMargin;
558}
559}
560
562{
563 if (!IsBorder(rSection.m_brc))
564 return;
565
566 SfxItemSet aSet(rFormat.GetAttrSet());
567 short aSizeArray[5]={0};
568 SetFlyBordersShadow(aSet, rSection.m_brc, &aSizeArray[0]);
571 SvxBoxItem aBox(aSet.Get(RES_BOX));
572 bool bFromEdge = rSection.maSep.pgbOffsetFrom == 1;
573
574 aLR.SetLeft(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::LEFT, aLR.GetLeft()));
575 aLR.SetRight(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::RIGHT, aLR.GetRight()));
576 aUL.SetUpper(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::TOP, aUL.GetUpper()));
577 aUL.SetLower(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::BOTTOM, aUL.GetLower()));
578
579 aSet.Put(aBox);
580 aSet.Put(aLR);
581 aSet.Put(aUL);
582 rFormat.SetFormatAttr(aSet);
583}
584
587{
588 sal_Int32 nWWUp = rSection.maSep.dyaTop;
589 sal_Int32 nWWLo = rSection.maSep.dyaBottom;
590 sal_uInt32 nWWHTop = rSection.maSep.dyaHdrTop;
591 sal_uInt32 nWWFBot = rSection.maSep.dyaHdrBottom;
592
593 /*
594 If there is gutter in 97+ and the dop says put it on top then get the
595 gutter distance and set it to the top margin. When we are "two pages
596 in one" the gutter is put at the top of odd pages, and bottom of
597 even pages, something we cannot do. So we will put it on top of all
598 pages, that way the pages are at least the right size.
599 */
600 if (!mrReader.m_bVer67 && mrReader.m_xWDop->iGutterPos &&
601 rSection.maSep.fRTLGutter)
602 {
603 nWWUp += rSection.maSep.dzaGutter;
604 }
605
606 /* Check whether this section has headers / footers */
607 sal_uInt16 nHeaderMask = WW8_HEADER_EVEN | WW8_HEADER_ODD;
608 sal_uInt16 nFooterMask = WW8_FOOTER_EVEN | WW8_FOOTER_ODD;
609 /* Ignore the presence of a first-page header/footer unless it is enabled */
610 if( rSection.HasTitlePage() )
611 {
612 nHeaderMask |= WW8_HEADER_FIRST;
613 nFooterMask |= WW8_FOOTER_FIRST;
614 }
615 rData.bHasHeader = (rSection.maSep.grpfIhdt & nHeaderMask) != 0;
616 rData.bHasFooter = (rSection.maSep.grpfIhdt & nFooterMask) != 0;
617
618 if( rData.bHasHeader )
619 {
620 rData.nSwUp = nWWHTop; // Header -> convert
621 // #i19922# - correction:
622 // consider that <nWWUp> can be negative, compare only if it's positive
623 if ( nWWUp > 0 &&
624 o3tl::make_unsigned(nWWUp) >= nWWHTop )
625 rData.nSwHLo = nWWUp - nWWHTop;
626 else
627 rData.nSwHLo = 0;
628
629 // #i19922# - minimum page header height is now 1mm
630 // use new constant <cMinHdFtHeight>
631 if (rData.nSwHLo < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight))
632 rData.nSwHLo = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight);
633 }
634 else // no header -> just use Up as-is
635 rData.nSwUp = std::abs(nWWUp);
636
637 if( rData.bHasFooter )
638 {
639 rData.nSwLo = nWWFBot; // footer -> convert
640 // #i19922# - correction: consider that <nWWLo> can be negative, compare only if it's positive
641 if ( nWWLo > 0 &&
642 o3tl::make_unsigned(nWWLo) >= nWWFBot )
643 rData.nSwFUp = nWWLo - nWWFBot;
644 else
645 rData.nSwFUp = 0;
646
647 // #i19922# - minimum page header height is now 1mm
648 // use new constant <cMinHdFtHeight>
649 if (rData.nSwFUp < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight))
650 rData.nSwFUp = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight);
651 }
652 else // no footer -> just use Lo as-is
653 rData.nSwLo = std::abs(nWWLo);
654}
655
657 wwSectionManager::wwULSpaceData const & rData, const wwSection &rSection)
658{
659 if (rData.bHasHeader) // ... and set Header-Lower
660 {
661 // set header height to minimum
662 if (SwFrameFormat* pHdFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat()))
663 {
664 SvxULSpaceItem aHdUL(pHdFormat->GetULSpace());
665 if (!rSection.IsFixedHeightHeader()) //normal
666 {
667 pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwHLo));
668 // #i19922# - minimum page header height is now 1mm
669 // use new constant <cMinHdFtHeight>
670 aHdUL.SetLower( writer_cast<sal_uInt16>(rData.nSwHLo - cMinHdFtHeight) );
671 pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
673 }
674 else
675 {
676 // Hack alert: these calculations are based on
677 // #112727# import negative height headers/footers as floating frames inside fixed height headers/footer
678 // #i48832# - set correct spacing between header and body.
679 const sal_Int32 nHdLowerSpace(std::max<sal_Int32>(0, std::abs(rSection.maSep.dyaTop) - rData.nSwUp - rData.nSwHLo));
680 pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwHLo + nHdLowerSpace));
681 aHdUL.SetLower( static_cast< sal_uInt16 >(nHdLowerSpace) );
682 pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
684 }
685 pHdFormat->SetFormatAttr(aHdUL);
686 }
687 }
688
689 if (rData.bHasFooter) // ... and set footer-upper
690 {
691 if (SwFrameFormat* pFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat()))
692 {
693 SvxULSpaceItem aFtUL(pFtFormat->GetULSpace());
694 if (!rSection.IsFixedHeightFooter()) //normal
695 {
696 pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwFUp));
697 // #i19922# - minimum page header height is now 1mm
698 // use new constant <cMinHdFtHeight>
699 aFtUL.SetUpper( writer_cast<sal_uInt16>(rData.nSwFUp - cMinHdFtHeight) );
700 pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
702 }
703 else
704 {
705 // #i48832# - set correct spacing between footer and body.
706 const sal_Int32 nFtUpperSpace(std::max<sal_Int32>(0, std::abs(rSection.maSep.dyaBottom) - rData.nSwLo - rData.nSwFUp));
707 pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwFUp + nFtUpperSpace));
708 aFtUL.SetUpper( static_cast< sal_uInt16 >(nFtUpperSpace) );
709 pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
711 }
712 pFtFormat->SetFormatAttr(aFtUL);
713 }
714 }
715
716 SvxULSpaceItem aUL(writer_cast<sal_uInt16>(rData.nSwUp),
717 writer_cast<sal_uInt16>(rData.nSwLo), RES_UL_SPACE);
718 rFormat.SetFormatAttr(aUL);
719}
720
722 SwPaM const & rMyPaM, wwSection &rSection)
723{
726
728
729 bool bRTLPgn = !maSegments.empty() && maSegments.back().IsBiDi();
731 bRTLPgn ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
732
733 if (2 == mrReader.m_xWDop->fpc)
735 if (0 == mrReader.m_xWDop->epc)
737
738 aSection.SetProtectFlag(SectionIsProtected(rSection));
739
740 rSection.mpSection =
741 mrReader.m_rDoc.InsertSwSection( rMyPaM, aSection, nullptr, & aSet );
742 OSL_ENSURE(rSection.mpSection, "section not inserted!");
743 if (!rSection.mpSection)
744 return nullptr;
745
746 SwPageDesc *pPage = nullptr;
747 auto aIter = std::find_if(maSegments.rbegin(), maSegments.rend(),
748 [](const wwSection& rSegment) { return rSegment.mpPage != nullptr; });
749 if (aIter != maSegments.rend())
750 pPage = aIter->mpPage;
751
752 OSL_ENSURE(pPage, "no page outside this section!");
753
754 if (!pPage)
755 pPage = &mrReader.m_rDoc.GetPageDesc(0);
756
757 SwSectionFormat *pFormat = rSection.mpSection->GetFormat();
758 OSL_ENSURE(pFormat, "impossible");
759 if (!pFormat)
760 return nullptr;
761
762 SwFrameFormat& rFormat = pPage->GetMaster();
763 const SvxLRSpaceItem& rLR = rFormat.GetLRSpace();
764 tools::Long nPageLeft = rLR.GetLeft();
765 tools::Long nPageRight = rLR.GetRight();
766 tools::Long nSectionLeft = rSection.GetPageLeft() - nPageLeft;
767 tools::Long nSectionRight = rSection.GetPageRight() - nPageRight;
768 if ((nSectionLeft != 0) || (nSectionRight != 0))
769 {
770 SvxLRSpaceItem aLR(nSectionLeft, nSectionRight, 0, RES_LR_SPACE);
771 pFormat->SetFormatAttr(aLR);
772 }
773
774 SetCols(*pFormat, rSection, rSection.GetTextAreaWidth());
775 return pFormat;
776}
777
779{
780 // check if Line Numbering must be activated or reset
781 if (!(m_bNewDoc && rSection.maSep.nLnnMod))
782 return;
783
784 // restart-numbering-mode: 0 per page, 1 per section, 2 never restart
785 bool bRestartLnNumPerSection = (1 == rSection.maSep.lnc);
786
787 if (m_bNoLnNumYet)
788 {
790
791 aInfo.SetPaintLineNumbers(true);
792
793 aInfo.SetRestartEachPage(rSection.maSep.lnc == 0);
794
795 // A value of 0 (auto) indicates that the application MUST automatically determine positioning.
796 if ( rSection.maSep.dxaLnn )
797 aInfo.SetPosFromLeft(writer_cast<sal_uInt16>(rSection.maSep.dxaLnn));
798
799 //Paint only for every n line
800 aInfo.SetCountBy(rSection.maSep.nLnnMod);
801
802 // to be defaulted features ( HARDCODED in MS Word 6,7,8,9 )
803 aInfo.SetCountBlankLines(true);
804 aInfo.SetCountInFlys(false);
806 SvxNumberType aNumType; // this sets SVX_NUM_ARABIC per default
807 aInfo.SetNumType( aNumType );
808
809 m_rDoc.SetLineNumberInfo( aInfo );
810 m_bNoLnNumYet = false;
811 }
812
813 if ((0 < rSection.maSep.lnnMin) || bRestartLnNumPerSection)
814 {
816 if (const SwFormatLineNumber* pLN
817 = static_cast<const SwFormatLineNumber*>(GetFormatAttr(RES_LINENUMBER)))
818 {
819 aLN.SetCountLines( pLN->IsCount() );
820 }
821 aLN.SetStartValue(1 + rSection.maSep.lnnMin);
822 NewAttr(aLN);
824 }
825}
826
828 , mpSection(nullptr)
829 , mpPage(nullptr)
831 , m_nPgWidth(SvxPaperInfo::GetPaperSize(PAPER_A4).Width())
832 , m_nPgLeft(MM_250)
833 , m_nPgRight(MM_250)
834 , m_nPgGutter(0)
835 , mnVerticalAdjustment(drawing::TextVerticalAdjust_TOP)
836 , mnBorders(0)
837 , mbHasFootnote(false)
838{
839}
840
842 SwPageDesc &rPageDesc)
843{
844 // save page number format
845 static const SvxNumType aNumTyp[5] =
846 {
849 };
850
851 SvxNumberType aType;
852 aType.SetNumberingType( aNumTyp[rNewSection.maSep.nfcPgn] );
853 rPageDesc.SetNumType(aType);
854}
855
856// CreateSep is called for every section change (even at the start of
857// the document. CreateSep also creates the pagedesc(s) and
858// fills it/them with attributes and KF texts.
859// This has become necessary because the translation of the various
860// page attributes is interconnected too much.
862{
863 /*
864 #i1909# section/page breaks should not occur in tables or subpage
865 elements like frames. Word itself ignores them in this case. The bug is
866 more likely that this filter created such documents in the past!
867 */
869 return;
870
871 WW8PLCFx_SEPX* pSep = mrReader.m_xPlcxMan->GetSepPLCF();
872 OSL_ENSURE(pSep, "impossible!");
873 if (!pSep)
874 return;
875
877 {
878 bool insert = true;
880 if( pam.Move(fnMoveBackward, GoInNode))
881 if( SwTextNode* txtNode = pam.GetPoint()->GetNode().GetTextNode())
882 if( txtNode->Len() == 0 )
883 insert = false;
884 if( insert )
886 }
887
889
890 // M.M. Create a linked section if the WkbPLCF
891 // has an entry for one at this cp
892 WW8PLCFspecial* pWkb = mrReader.m_xPlcxMan->GetWkbPLCF();
893 if (pWkb && pWkb->SeekPosExact(nTextPos) &&
894 pWkb->Where() == nTextPos)
895 {
896 void* pData;
897 WW8_CP nTest;
898 bool bSuccess = pWkb->Get(nTest, pData);
899 if (!bSuccess)
900 return;
901 OUString sSectionName = mrReader.m_aLinkStringMap[SVBT16ToUInt16( static_cast<WW8_WKB*>(pData)->nLinkId) ];
902 sSectionName = mrReader.ConvertFFileName(sSectionName);
903 SwSectionData aSection(SectionType::FileLink, sSectionName);
904 aSection.SetLinkFileName( sSectionName );
905 aSection.SetProtectFlag(true);
906 // #i19922# - improvement: return value of method <Insert> not used.
907 mrReader.m_rDoc.InsertSwSection(*mrReader.m_pPaM, aSection, nullptr, nullptr, false);
908 }
909
910 wwSection aLastSection(*mrReader.m_pPaM->GetPoint());
911 if (!maSegments.empty())
912 aLastSection = maSegments.back();
913
914 //Here
915 sal_uInt16 nLIdx = ( ( static_cast<sal_uInt16>(mrReader.m_xWwFib->m_lid) & 0xff ) == 0x9 ) ? 1 : 0;
916
917 //BEGIN read section values
918 wwSection aNewSection(*mrReader.m_pPaM->GetPoint());
919
920 static const sal_uInt16 aVer2Ids0[] =
921 {
922 /*sprmSBkc*/ 117,
923 /*sprmSFTitlePage*/ 118,
924 /*sprmSNfcPgn*/ 122,
925 /*sprmSCcolumns*/ 119,
926 /*sprmSDxaColumns*/ 120,
927 /*sprmSLBetween*/ 133
928 };
929
930 static const sal_uInt16 aVer67Ids0[] =
931 {
938 };
939
940 static const sal_uInt16 aVer8Ids0[] =
941 {
948 };
949
950 const sal_uInt16* pIds = eVer <= ww::eWW2 ? aVer2Ids0 : eVer <= ww::eWW7 ? aVer67Ids0 : aVer8Ids0;
951
952 SprmResult aRes = pSep->HasSprm(pIds[0]);
953 const sal_uInt8* pSprmBkc = aRes.pSprm;
954 if (!maSegments.empty())
955 {
956 // Type of break: break codes are:
957 // 0 No break
958 // 1 New column
959 // 2 New page
960 // 3 Even page
961 // 4 Odd page
962 if (pSprmBkc && aRes.nRemainingData >= 1)
963 aNewSection.maSep.bkc = *pSprmBkc;
964 }
965
966 // Has a table page
967 aNewSection.maSep.fTitlePage =
968 sal_uInt8(0 != ReadBSprm( pSep, pIds[1], 0 ));
969
970 // sprmSNfcPgn
971 aNewSection.maSep.nfcPgn = ReadBSprm( pSep, pIds[2], 0 );
972 if (aNewSection.maSep.nfcPgn > 4)
973 aNewSection.maSep.nfcPgn = 0;
974
975 aNewSection.maSep.fUnlocked = eVer > ww::eWW2 ? ReadBSprm(pSep, (eVer <= ww::eWW7 ? NS_sprm::v6::sprmSFProtected : NS_sprm::SFProtected::val), 0 ) : 0;
976
977 // sprmSFBiDi
978 aNewSection.maSep.fBiDi = eVer >= ww::eWW8 ? ReadBSprm(pSep, NS_sprm::SFBiDi::val, 0) : 0;
979
980 // Reading section property sprmSCcolumns - one less than the number of columns in the section.
981 // It must be less than MAX_NO_OF_SEP_COLUMNS according the WW8 specification.
982 aNewSection.maSep.ccolM1 = ReadSprm(pSep, pIds[3], 0 );
983 if ( aNewSection.maSep.ccolM1 >= MAX_NO_OF_SEP_COLUMNS )
984 {
985 // clip to max
986 aNewSection.maSep.ccolM1 = MAX_NO_OF_SEP_COLUMNS-1;
987 }
988
989 //sprmSDxaColumns - default distance 1.25 cm
990 aNewSection.maSep.dxaColumns = ReadUSprm( pSep, pIds[4], 708 );
991
992 // sprmSLBetween
993 aNewSection.maSep.fLBetween = ReadBSprm(pSep, pIds[5], 0 );
994
995 if (eVer >= ww::eWW6)
996 {
997 // sprmSFEvenlySpaced
998 aNewSection.maSep.fEvenlySpaced =
1000
1001 if (aNewSection.maSep.ccolM1 > 0 && !aNewSection.maSep.fEvenlySpaced)
1002 {
1003 int nColumnDataIdx = 0;
1004 aNewSection.maSep.rgdxaColumnWidthSpacing[nColumnDataIdx] = 0;
1005
1006 const sal_uInt16 nColumnWidthSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColWidth : NS_sprm::SDxaColWidth::val);
1007 const sal_uInt16 nColumnSpacingSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColSpacing : NS_sprm::SDxaColSpacing::val);
1008 const sal_uInt8 nColumnCount = static_cast< sal_uInt8 >(aNewSection.maSep.ccolM1 + 1);
1009 for ( sal_uInt8 nColumn = 0; nColumn < nColumnCount; ++nColumn )
1010 {
1011 //sprmSDxaColWidth
1012 SprmResult aSWRes = pSep->HasSprm(nColumnWidthSprmId, nColumn);
1013 const sal_uInt8* pSW = aSWRes.pSprm;
1014
1015 OSL_ENSURE( pSW, "+Sprm 136 (resp. 0xF203) (ColWidth) missing" );
1016 sal_uInt16 nWidth = (pSW && aSWRes.nRemainingData >= 3) ? SVBT16ToUInt16(pSW + 1) : 1440;
1017
1018 aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth;
1019
1020 if ( nColumn < nColumnCount - 1 )
1021 {
1022 //sprmSDxaColSpacing
1023 SprmResult aSDRes = pSep->HasSprm(nColumnSpacingSprmId, nColumn);
1024 const sal_uInt8* pSD = aSDRes.pSprm;
1025
1026 OSL_ENSURE( pSD, "+Sprm 137 (resp. 0xF204) (Colspacing) missing" );
1027 if (pSD && aSDRes.nRemainingData >= 3)
1028 {
1029 nWidth = SVBT16ToUInt16(pSD + 1);
1030 aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth;
1031 }
1032 }
1033 }
1034 }
1035 }
1036
1037 static const sal_uInt16 aVer2Ids1[] =
1038 {
1039 /*sprmSBOrientation*/ 137,
1040 /*sprmSXaPage*/ 139,
1041 /*sprmSYaPage*/ 140,
1042 /*sprmSDxaLeft*/ 141,
1043 /*sprmSDxaRight*/ 142,
1044 /*sprmSDzaGutter*/ 145,
1045 /*sprmSFPgnRestart*/ 125,
1046 /*sprmSPgnStart*/ 136,
1047 /*sprmSDmBinFirst*/ 115,
1048 /*sprmSDmBinOther*/ 116
1049 };
1050
1051 static const sal_uInt16 aVer67Ids1[] =
1052 {
1063 };
1064
1065 static const sal_uInt16 aVer8Ids1[] =
1066 {
1077 };
1078
1079 pIds = eVer <= ww::eWW2 ? aVer2Ids1 : eVer <= ww::eWW7 ? aVer67Ids1 : aVer8Ids1;
1080
1081 // 1. orientation
1082 aNewSection.maSep.dmOrientPage = ReadBSprm(pSep, pIds[0], 0);
1083
1084 // 2. paper size
1085 aNewSection.maSep.xaPage = ReadUSprm(pSep, pIds[1], lLetterWidth);
1087
1088 aNewSection.maSep.yaPage = ReadUSprm(pSep, pIds[2], lLetterHeight);
1089
1090 // 3. LR borders
1091 static const sal_uInt16 nLef[] = { MM_250, 1800 };
1092 static const sal_uInt16 nRig[] = { MM_250, 1800 };
1093
1094 aNewSection.maSep.dxaLeft = ReadUSprm( pSep, pIds[3], nLef[nLIdx]);
1095 aNewSection.maSep.dxaRight = ReadUSprm( pSep, pIds[4], nRig[nLIdx]);
1096
1097 // 2pages in 1sheet hackery ?
1098 // #i31806# but only swap if 2page in 1sheet is enabled.
1099 // it's not clear if dmOrientPage is the correct member to
1100 // decide on this.
1101 if(mrReader.m_xWDop->doptypography.m_f2on1 &&
1102 aNewSection.maSep.dmOrientPage == 2)
1103 std::swap(aNewSection.maSep.dxaLeft, aNewSection.maSep.dxaRight);
1104
1105 aNewSection.maSep.dzaGutter = ReadUSprm( pSep, pIds[5], 0);
1106
1107 aNewSection.maSep.fRTLGutter = static_cast<sal_uInt8>(
1108 eVer >= ww::eWW8 ? ReadBSprm(pSep, NS_sprm::SFRTLGutter::val, 0) : 0);
1109
1110 // Page Number Restarts - sprmSFPgnRestart
1111 aNewSection.maSep.fPgnRestart = ReadBSprm(pSep, pIds[6], 0);
1112
1113 aNewSection.maSep.pgnStart = ReadUSprm( pSep, pIds[7], 0 );
1114
1115 // if the document's first page number is unspecified, but it starts with an even page break,
1116 // then set the first page number to two
1117 if ( maSegments.empty() && !aNewSection.maSep.fPgnRestart && pSprmBkc && *pSprmBkc == 3 )
1118 {
1119 aNewSection.maSep.pgnStart = 2;
1120 aNewSection.maSep.fPgnRestart = 1;
1121 }
1122
1123 if (eVer >= ww::eWW6)
1124 {
1126 if (aRes.pSprm && aRes.nRemainingData >= 1)
1127 aNewSection.maSep.iHeadingPgn = *aRes.pSprm;
1128
1130 if (aRes.pSprm && aRes.nRemainingData >= 1)
1131 aNewSection.maSep.cnsPgn = *aRes.pSprm;
1132 }
1133
1134 aRes = pSep->HasSprm(pIds[8]);
1135 const sal_uInt8* pSprmSDmBinFirst = aRes.pSprm;
1136 if (pSprmSDmBinFirst && aRes.nRemainingData >= 1)
1137 aNewSection.maSep.dmBinFirst = *pSprmSDmBinFirst;
1138
1139 aRes = pSep->HasSprm(pIds[9]);
1140 const sal_uInt8* pSprmSDmBinOther = aRes.pSprm;
1141 if (pSprmSDmBinOther && aRes.nRemainingData >= 1)
1142 aNewSection.maSep.dmBinOther = *pSprmSDmBinOther;
1143
1144 static const sal_uInt16 nTop[] = { MM_250, 1440 };
1145 static const sal_uInt16 nBot[] = { MM_200, 1440 };
1146
1147 static const sal_uInt16 aVer2Ids2[] =
1148 {
1149 /*sprmSDyaTop*/ 143,
1150 /*sprmSDyaBottom*/ 144,
1151 /*sprmSDyaHdrTop*/ 131,
1152 /*sprmSDyaHdrBottom*/ 132,
1153 /*sprmSNLnnMod*/ 129,
1154 /*sprmSLnc*/ 127,
1155 /*sprmSDxaLnn*/ 130,
1156 /*sprmSLnnMin*/ 135
1157 };
1158
1159 static const sal_uInt16 aVer67Ids2[] =
1160 {
1169 };
1170 static const sal_uInt16 aVer8Ids2[] =
1171 {
1180 };
1181
1182 pIds = eVer <= ww::eWW2 ? aVer2Ids2 : eVer <= ww::eWW7 ? aVer67Ids2 : aVer8Ids2;
1183
1184 aNewSection.maSep.dyaTop = ReadSprm( pSep, pIds[0], nTop[nLIdx] );
1185 aNewSection.maSep.dyaBottom = ReadSprm( pSep, pIds[1], nBot[nLIdx] );
1186 aNewSection.maSep.dyaHdrTop = ReadUSprm( pSep, pIds[2], 720 );
1187 aNewSection.maSep.dyaHdrBottom = ReadUSprm( pSep, pIds[3], 720 );
1188
1189 if (eVer >= ww::eWW8)
1190 {
1191 aNewSection.maSep.wTextFlow = ReadUSprm(pSep, NS_sprm::STextFlow::val, 0);
1192 aNewSection.maSep.clm = ReadUSprm( pSep, NS_sprm::SClm::val, 0 );
1193 aNewSection.maSep.dyaLinePitch = ReadUSprm(pSep, NS_sprm::SDyaLinePitch::val, 360);
1195 if (aRes.pSprm && aRes.nRemainingData >= 4)
1196 aNewSection.maSep.dxtCharSpace = SVBT32ToUInt32(aRes.pSprm);
1197
1198 //sprmSPgbProp
1199 sal_uInt16 pgbProp = ReadSprm( pSep, NS_sprm::SPgbProp::val, 0 );
1200 aNewSection.maSep.pgbApplyTo = pgbProp & 0x0007;
1201 aNewSection.maSep.pgbPageDepth = (pgbProp & 0x0018) >> 3;
1202 aNewSection.maSep.pgbOffsetFrom = (pgbProp & 0x00E0) >> 5;
1203
1204 aNewSection.mnBorders = ::lcl_ReadBorders(false, aNewSection.m_brc, nullptr, nullptr, pSep);
1205 }
1206
1207 // check if Line Numbering must be activated or reset
1208 SprmResult aSprmSNLnnMod = pSep->HasSprm(pIds[4]);
1209 if (aSprmSNLnnMod.pSprm && aSprmSNLnnMod.nRemainingData >= 1)
1210 aNewSection.maSep.nLnnMod = *aSprmSNLnnMod.pSprm;
1211
1212 SprmResult aSprmSLnc = pSep->HasSprm(pIds[5]);
1213 if (aSprmSLnc.pSprm && aSprmSLnc.nRemainingData >= 1)
1214 aNewSection.maSep.lnc = *aSprmSLnc.pSprm;
1215
1216 SprmResult aSprmSDxaLnn = pSep->HasSprm(pIds[6]);
1217 if (aSprmSDxaLnn.pSprm && aSprmSDxaLnn.nRemainingData >= 2)
1218 aNewSection.maSep.dxaLnn = SVBT16ToUInt16(aSprmSDxaLnn.pSprm);
1219
1220 SprmResult aSprmSLnnMin = pSep->HasSprm(pIds[7]);
1221 if (aSprmSLnnMin.pSprm && aSprmSLnnMin.nRemainingData >= 1)
1222 aNewSection.maSep.lnnMin = *aSprmSLnnMin.pSprm;
1223
1224 if (eVer <= ww::eWW7)
1225 aNewSection.maSep.grpfIhdt = ReadBSprm(pSep, eVer <= ww::eWW2 ? 128 : 153, 0);
1226 else if (mrReader.m_xHdFt)
1227 {
1230
1231 // It is possible for a first page header to be provided
1232 // for this section, but not actually shown in this section. In this
1233 // case (aNewSection.maSep.grpfIhdt & WW8_HEADER_FIRST) will be nonzero
1234 // but aNewSection.HasTitlePage() will be false.
1235 // Likewise for first page footer.
1236
1237 if (mrReader.m_xWDop->fFacingPages)
1239
1240 //See if we have a header or footer for each enabled possibility
1241 //if we do not then we inherit the previous sections header/footer,
1242 for (int nI = 0, nMask = 1; nI < 6; ++nI, nMask <<= 1)
1243 {
1244 if (aNewSection.maSep.grpfIhdt & nMask)
1245 {
1246 WW8_CP nStart, nLen;
1247 mrReader.m_xHdFt->GetTextPosExact( static_cast< short >(nI + ( maSegments.size() + 1) * 6), nStart, nLen);
1248 //No header or footer, inherit previous one, or set to zero
1249 //if no previous one
1250 if (!nLen)
1251 {
1252 if (
1253 maSegments.empty() ||
1254 !(maSegments.back().maSep.grpfIhdt & nMask)
1255 )
1256 {
1257 aNewSection.maSep.grpfIhdt &= ~nMask;
1258 }
1259 }
1260 }
1261 }
1262 }
1263
1264 SetLeftRight(aNewSection);
1265 //END read section values
1266
1267 if (eVer >= ww::eWW8)
1268 aNewSection.SetDirection();
1269
1270 mrReader.HandleLineNumbering(aNewSection);
1271 maSegments.push_back(aNewSection);
1272}
1273
1275 SwPageDesc* pNewPageDesc, sal_uInt8 nCode )
1276{
1277 // copy odd header content section
1278 if( nCode & WW8_HEADER_ODD )
1279 {
1280 m_rDoc.CopyHeader(pOrgPageDesc->GetMaster(),
1281 pNewPageDesc->GetMaster() );
1282 }
1283 // copy odd footer content section
1284 if( nCode & WW8_FOOTER_ODD )
1285 {
1286 m_rDoc.CopyFooter(pOrgPageDesc->GetMaster(),
1287 pNewPageDesc->GetMaster());
1288 }
1289 // copy even header content section
1290 if( nCode & WW8_HEADER_EVEN )
1291 {
1292 m_rDoc.CopyHeader(pOrgPageDesc->GetLeft(),
1293 pNewPageDesc->GetLeft());
1294 }
1295 // copy even footer content section
1296 if( nCode & WW8_FOOTER_EVEN )
1297 {
1298 m_rDoc.CopyFooter(pOrgPageDesc->GetLeft(),
1299 pNewPageDesc->GetLeft());
1300 }
1301 // copy first page header content section
1302 if( nCode & WW8_HEADER_FIRST )
1303 {
1304 m_rDoc.CopyHeader(pOrgPageDesc->GetFirstMaster(),
1305 pNewPageDesc->GetFirstMaster());
1306 }
1307 // copy first page footer content section
1308 if( nCode & WW8_FOOTER_FIRST )
1309 {
1310 m_rDoc.CopyFooter(pOrgPageDesc->GetFirstMaster(),
1311 pNewPageDesc->GetFirstMaster());
1312 }
1313}
1314
1315// helper functions for graphics, Apos and tables
1316
1317// Read BoRder Control structure
1318// nBrcVer should be set to the version of the BRC record being read (6, 8 or 9)
1319// This will be converted to the latest format (9).
1320static bool SetWW8_BRC(int nBrcVer, WW8_BRCVer9& rVar, const sal_uInt8* pS, size_t nLen)
1321{
1322
1323 if( pS )
1324 {
1325 if (nBrcVer == 9 && nLen >= sizeof(WW8_BRCVer9))
1326 rVar = *reinterpret_cast<const WW8_BRCVer9*>(pS);
1327 else if (nBrcVer == 8 && nLen >= sizeof(WW8_BRC))
1328 rVar = WW8_BRCVer9(*reinterpret_cast<const WW8_BRC*>(pS));
1329 else if (nLen >= sizeof(WW8_BRCVer6)) // nBrcVer == 6
1330 rVar = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<const WW8_BRCVer6*>(pS)));
1331 }
1332
1333 return nullptr != pS;
1334}
1335
1337 const WW8RStyle* pSty, const WW8PLCFx_SEPX* pSep)
1338{
1339
1340//returns a sal_uInt8 filled with a bit for each position that had a sprm
1341//setting that border
1342
1343 sal_uInt8 nBorder = 0;
1344 if( pSep )
1345 {
1346 if( !bVer67 )
1347 {
1348 SprmResult a8Sprm[4];
1349 if (pSep->Find4Sprms(
1352 a8Sprm[0], a8Sprm[1], a8Sprm[2], a8Sprm[3]))
1353 {
1354 for( int i = 0; i < 4; ++i )
1355 nBorder |= int(SetWW8_BRC(8, brc[i], a8Sprm[i].pSprm, a8Sprm[i].nRemainingData))<<i;
1356 }
1357
1358 // Version 9 BRCs if present will override version 8
1359 SprmResult a9Sprm[4];
1360 if (pSep->Find4Sprms(
1363 a9Sprm[0], a9Sprm[1], a9Sprm[2], a9Sprm[3]))
1364 {
1365 for( int i = 0; i < 4; ++i )
1366 nBorder |= int(SetWW8_BRC(9, brc[i], a9Sprm[i].pSprm, a9Sprm[i].nRemainingData))<<i;
1367 }
1368 }
1369 }
1370 else
1371 {
1372
1373 static const sal_uInt16 aVer67Ids[5] = {
1379 };
1380
1381 static const sal_uInt16 aVer8Ids[5] = {
1387 };
1388
1389 static const sal_uInt16 aVer9Ids[5] = {
1395 };
1396
1397 if( pPap )
1398 {
1399 if (bVer67)
1400 {
1401 for( int i = 0; i < 5; ++i )
1402 {
1403 SprmResult aRes(pPap->HasSprm(aVer67Ids[i]));
1404 nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1405 }
1406 }
1407 else
1408 {
1409 for( int i = 0; i < 5; ++i )
1410 {
1411 SprmResult aRes(pPap->HasSprm(aVer8Ids[i]));
1412 nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1413 }
1414 // Version 9 BRCs if present will override version 8
1415 for( int i = 0; i < 5; ++i )
1416 {
1417 SprmResult aRes(pPap->HasSprm(aVer9Ids[i]));
1418 nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1419 }
1420 }
1421 }
1422 else if( pSty )
1423 {
1424 if (bVer67)
1425 {
1426 for( int i = 0; i < 5; ++i )
1427 {
1428 SprmResult aRes(pSty->HasParaSprm(aVer67Ids[i]));
1429 nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1430 }
1431 }
1432 else
1433 {
1434 for( int i = 0; i < 5; ++i )
1435 {
1436 SprmResult aRes(pSty->HasParaSprm(aVer8Ids[i]));
1437 nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1438 }
1439 // Version 9 BRCs if present will override version 8
1440 for( int i = 0; i < 5; ++i )
1441 {
1442 SprmResult aRes(pSty->HasParaSprm(aVer9Ids[i]));
1443 nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
1444 }
1445 }
1446 }
1447 else {
1448 OSL_ENSURE( pSty || pPap, "WW8PLCFx_Cp_FKP and WW8RStyle "
1449 "and WW8PLCFx_SEPX is 0" );
1450 }
1451 }
1452
1453 return nBorder;
1454}
1455
1456static void GetLineIndex(SvxBoxItem &rBox, short nLineThickness, short nSpace,
1457 sal_uInt32 cv, short nIdx, SvxBoxItemLine nOOIndex, sal_uInt16 nWWIndex,
1458 short *pSize)
1459{
1460 // LO cannot handle outset/inset (new in WW9 BRC) so fall back same as WW8
1461 if ( nIdx == 0x1A || nIdx == 0x1B )
1462 {
1463 nIdx = (nIdx == 0x1A) ? 0x12 : 0x11;
1464 cv = 0xc0c0c0;
1465 }
1466
1467 SvxBorderLineStyle const eStyle(
1468 ::editeng::ConvertBorderStyleFromWord(nIdx));
1469
1471 aLine.SetBorderLineStyle( eStyle );
1472 double const fConverted( (SvxBorderLineStyle::NONE == eStyle) ? 0.0 :
1473 ::editeng::ConvertBorderWidthFromWord(eStyle, nLineThickness, nIdx));
1474 aLine.SetWidth(fConverted);
1475
1476 //No AUTO for borders as yet, so if AUTO, use BLACK
1477 Color col = (cv==0xff000000) ? COL_BLACK : msfilter::util::BGRToRGB(cv);
1478
1479 aLine.SetColor(col);
1480
1481 if (pSize)
1482 pSize[nWWIndex] = fConverted + nSpace;
1483
1484 rBox.SetLine(&aLine, nOOIndex);
1485 rBox.SetDistance(nSpace, nOOIndex);
1486
1487}
1488
1489static void Set1Border(SvxBoxItem &rBox, const WW8_BRCVer9& rBor, SvxBoxItemLine nOOIndex,
1490 sal_uInt16 nWWIndex, short *pSize, const bool bIgnoreSpace)
1491{
1492 short nSpace;
1493 short nLineThickness = rBor.DetermineBorderProperties(&nSpace);
1494
1495 GetLineIndex(rBox, nLineThickness, bIgnoreSpace ? 0 : nSpace,
1496 rBor.cv(), rBor.brcType(), nOOIndex, nWWIndex, pSize );
1497
1498}
1499
1500static bool lcl_IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn = false)
1501{
1502 return pbrc[WW8_TOP ].brcType() || // brcType != 0
1503 pbrc[WW8_LEFT ].brcType() ||
1504 pbrc[WW8_BOT ].brcType() ||
1505 pbrc[WW8_RIGHT].brcType() ||
1506 (bChkBtwn && pbrc[WW8_BETW ].brcType());
1507}
1508
1509bool SwWW8ImplReader::IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn)
1510{
1511 return lcl_IsBorder(pbrc, bChkBtwn);
1512}
1513
1515 short *pSizeArray, sal_uInt8 nSetBorders)
1516{
1517 bool bChange = false;
1518 static const std::pair<sal_uInt16, SvxBoxItemLine> aIdArr[] =
1519 {
1520 { WW8_TOP, SvxBoxItemLine::TOP },
1521 { WW8_LEFT, SvxBoxItemLine::LEFT },
1522 { WW8_RIGHT, SvxBoxItemLine::RIGHT },
1523 { WW8_BOT, SvxBoxItemLine::BOTTOM },
1524 { WW8_BETW, SvxBoxItemLine::BOTTOM }
1525 };
1526
1527 for( int i = 0; i < 4; ++i )
1528 {
1529 // filter out the invalid borders
1530 const WW8_BRCVer9& rB = pbrc[ aIdArr[ i ].first ];
1531 if( !rB.isNil() && rB.brcType() )
1532 {
1533 Set1Border(rBox, rB, aIdArr[i].second, aIdArr[i].first, pSizeArray, false);
1534 bChange = true;
1535 }
1536 else if ( nSetBorders & (1 << aIdArr[i].first) )
1537 {
1538 /*
1539 ##826##, ##653##
1540
1541 If a style has borders set,and the para attributes attempt remove
1542 the borders, then this is perfectly acceptable, so we shouldn't
1543 ignore this blank entry
1544
1545 nSetBorders has a bit set for each location that a sprm set a
1546 border, so with a sprm set, but no border, then disable the
1547 appropriate border
1548 */
1549 rBox.SetLine( nullptr, aIdArr[ i ].second );
1550 }
1551 }
1552 return bChange;
1553}
1554
1555bool SwWW8ImplReader::SetShadow(SvxShadowItem& rShadow, const short *pSizeArray,
1556 const WW8_BRCVer9& aRightBrc)
1557{
1558 bool bRet = aRightBrc.fShadow() && pSizeArray && pSizeArray[WW8_RIGHT];
1559 if (bRet)
1560 {
1561 rShadow.SetColor(COL_BLACK);
1562 //i120718
1563 short nVal = aRightBrc.DetermineBorderProperties();
1564 //End
1565 if (nVal < 0x10)
1566 nVal = 0x10;
1567 rShadow.SetWidth(nVal);
1568 rShadow.SetLocation(SvxShadowLocation::BottomRight);
1569 bRet = true;
1570 }
1571 return bRet;
1572}
1573
1575 tools::Rectangle& rInnerDist)
1576{
1577 rInnerDist = tools::Rectangle( pbrc[ 1 ].dptSpace() * 20,
1578 pbrc[ 0 ].dptSpace() * 20,
1579 pbrc[ 3 ].dptSpace() * 20,
1580 pbrc[ 2 ].dptSpace() * 20 );
1581}
1582
1584 const WW8_BRCVer9 *pbrc, short *pSizeArray)
1585{
1586 bool bShadowed = false;
1587 if (IsBorder(pbrc))
1588 {
1589 SvxBoxItem aBox( RES_BOX );
1590 SetBorder(aBox, pbrc, pSizeArray);
1591
1592 rFlySet.Put( aBox );
1593
1594 // fShadow
1595 SvxShadowItem aShadow( RES_SHADOW );
1596 if( SetShadow( aShadow, pSizeArray, pbrc[WW8_RIGHT] ))
1597 {
1598 bShadowed = true;
1599 rFlySet.Put( aShadow );
1600 }
1601 }
1602 return bShadowed;
1603}
1604
1605// APOs
1606
1607 // for computing the minimal FrameSize
1608#define MAX_BORDER_SIZE 210 // max. size of border
1609#define MAX_EMPTY_BORDER 10 // for off-by-one errors, at least 1
1610
1611static void FlySecur1(short& rSize, const bool bBorder)
1612{
1613 short nMin = MINFLY +
1614 (bBorder ? MAX_BORDER_SIZE : MAX_EMPTY_BORDER);
1615
1616 if ( rSize < nMin )
1617 rSize = nMin;
1618}
1619
1620static bool SetValSprm( sal_Int16* pVar, WW8PLCFx_Cp_FKP* pPap, sal_uInt16 nId )
1621{
1622 SprmResult aS = pPap->HasSprm(nId);
1623 if (aS.pSprm && aS.nRemainingData >= 2)
1624 *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm));
1625 return aS.pSprm != nullptr;
1626}
1627
1628static bool SetValSprm( sal_Int16* pVar, const WW8RStyle* pStyle, sal_uInt16 nId )
1629{
1630 SprmResult aS = pStyle->HasParaSprm(nId);
1631 if (aS.pSprm && aS.nRemainingData >= 2)
1632 *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm));
1633 return aS.pSprm != nullptr;
1634}
1635
1636/*
1637#i1930 revealed that sprm 0x360D (sprmTPc) as used in tables can affect the frame
1638around the table. Its full structure is not fully understood as yet.
1639*/
1641{
1642 if (pTabPos)
1643 {
1644 nTDxaAbs = pTabPos->nTDxaAbs;
1645 nTDyaAbs = pTabPos->nTDyaAbs;
1646 nTPc = pTabPos->nTPc;
1647 nLeftMargin = pTabPos->nLeftMargin;
1648 nRightMargin = pTabPos->nRightMargin;
1649 nUpperMargin = pTabPos->nUpperMargin;
1650 nLowerMargin = pTabPos->nLowerMargin;
1651 nPWr = pTabPos->nPWr;
1652 }
1653}
1654
1655WW8FlyPara::WW8FlyPara(bool bIsVer67, const WW8FlyPara* pSrc /* = 0 */)
1656{
1657 if ( pSrc )
1658 memcpy( this, pSrc, sizeof( WW8FlyPara ) ); // Copy-Ctor
1659 else
1660 {
1661 nTDxaAbs = 0;
1662 nTDyaAbs = 0;
1663 nSp45 = 0;
1664 nSp28 = 0;
1665 nLeftMargin = 0;
1666 nRightMargin = 0;
1667 nUpperMargin = 0;
1668 nLowerMargin = 0;
1669 nTPc = 0;
1670 nPWr = 2; // Default: wrapping
1671 bBorderLines = false;
1672 bGrafApo = false;
1673 mbVertSet = false;
1674 }
1675 bVer67 = bIsVer67;
1676}
1677
1678bool WW8FlyPara::operator==(const WW8FlyPara& rSrc) const
1679{
1680 /*
1681 Compare the parts that word seems to compare for equivalence.
1682 Interestingly being autoheight or absolute height (the & 0x7fff) doesn't
1683 matter to word
1684 */
1685 return
1686 (
1687 (nTDxaAbs == rSrc.nTDxaAbs) &&
1688 (nTDyaAbs == rSrc.nTDyaAbs) &&
1689 ((nSp45 & 0x7fff) == (rSrc.nSp45 & 0x7fff)) &&
1690 (nSp28 == rSrc.nSp28) &&
1691 (nLeftMargin == rSrc.nLeftMargin) &&
1692 (nRightMargin == rSrc.nRightMargin) &&
1693 (nUpperMargin == rSrc.nUpperMargin) &&
1694 (nLowerMargin == rSrc.nLowerMargin) &&
1695 (nTPc == rSrc.nTPc) &&
1696 (nPWr == rSrc.nPWr)
1697 );
1698}
1699
1700// Read for normal text
1702{
1703 if( bVer67 )
1704 {
1705 SetValSprm( &nTDxaAbs, pPap, 26 ); // X-position //sprmPDxaAbs
1706 //set in me or in parent style
1707 mbVertSet |= SetValSprm( &nTDyaAbs, pPap, 27 ); // Y-position //sprmPDyaAbs
1708 SetValSprm( &nSp45, pPap, 45 ); // height //sprmPWHeightAbs
1709 SetValSprm( &nSp28, pPap, 28 ); // width //sprmPDxaWidth
1710 SetValSprm( &nLeftMargin, pPap, 49 ); // L-border //sprmPDxaFromText
1711 SetValSprm( &nRightMargin, pPap, 49 ); // R-border //sprmPDxaFromText
1712 SetValSprm( &nUpperMargin, pPap, 48 ); // U-border //sprmPDyaFromText
1713 SetValSprm( &nLowerMargin, pPap, 48 ); // D-border //sprmPDyaFromText
1714
1716 if (aS.pSprm && aS.nRemainingData >= 1)
1717 nPWr = *aS.pSprm;
1718 }
1719 else
1720 {
1721 SetValSprm( &nTDxaAbs, pPap, NS_sprm::PDxaAbs::val ); // X-position
1722 //set in me or in parent style
1723 mbVertSet |= SetValSprm( &nTDyaAbs, pPap, NS_sprm::PDyaAbs::val ); // Y-position
1724 SetValSprm( &nSp45, pPap, NS_sprm::PWHeightAbs::val ); // height
1725 SetValSprm( &nSp28, pPap, NS_sprm::PDxaWidth::val ); // width
1726 SetValSprm( &nLeftMargin, pPap, NS_sprm::PDxaFromText::val ); // L-border
1727 SetValSprm( &nRightMargin, pPap, NS_sprm::PDxaFromText::val ); // R-border
1728 SetValSprm( &nUpperMargin, pPap, NS_sprm::PDyaFromText::val ); // U-border
1729 SetValSprm( &nLowerMargin, pPap, NS_sprm::PDyaFromText::val ); // D-border
1730
1731 SprmResult aS = pPap->HasSprm(NS_sprm::PWr::val); // wrapping
1732 if (aS.pSprm && aS.nRemainingData >= 1)
1733 nPWr = *aS.pSprm;
1734 }
1735
1736 if( ::lcl_ReadBorders( bVer67, brc, pPap )) // borders
1738
1739 /*
1740 #i8798#
1741 Appears that with no dyaAbs set then the actual vert anchoring set is
1742 ignored and we remain relative to text, so if that is the case we are 0
1743 from para anchor, so we update the frame to have explicitly this type of
1744 anchoring
1745 */
1746 if (!mbVertSet)
1747 nTPc = (nOrigSprmTPc & 0xCF) | 0x20;
1748 else
1749 nTPc = nOrigSprmTPc;
1750}
1751
1753{
1754 std::shared_ptr<WW8PLCFMan> xPlcxMan = pIo->m_xPlcxMan;
1755 WW8PLCFx_Cp_FKP* pPap = xPlcxMan->GetPapPLCF();
1756
1757 Read(nOrigSprmTPc, pPap); // read Apo parameter
1758
1759 do{ // block for quick exit
1760 if( nSp45 != 0 /* || nSp28 != 0 */ )
1761 break; // bGrafApo only automatic for height
1762 if( pIo->m_xWwFib->m_fComplex )
1763 break; // (*pPap)++ does not work for FastSave
1764 // -> for FastSave, no test for graphics APO
1765 SvStream* pIoStrm = pIo->m_pStrm;
1766 sal_uInt64 nPos = pIoStrm->Tell();
1767 WW8PLCFxSave1 aSave;
1768 xPlcxMan->GetPap()->Save( aSave );
1769 bGrafApo = false;
1770
1771 do{ // block for quick exit
1772 sal_uInt8 nText[2];
1773
1774 if (!checkRead(*pIoStrm, nText, 2)) // read text
1775 break;
1776
1777 if( nText[0] != 0x01 || nText[1] != 0x0d )// only graphics + CR?
1778 break; // no
1779
1780 pPap->advance(); // next line
1781
1782 // in APO ?
1783 //sprmPPc
1785
1786 // no -> graphics Apo
1787 if (!aS.pSprm || aS.nRemainingData < 1)
1788 {
1789 bGrafApo = true;
1790 break; // end of APO
1791 }
1792
1793 ww::WordVersion eVer = pIo->GetFib().GetFIBVersion();
1794 WW8FlyPara *pNowStyleApo=nullptr;
1795 sal_uInt16 nColl = pPap->GetIstd();
1796
1798 ww::sti eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc(nColl) : static_cast<ww::sti>(nColl);
1799 while (eSti != ww::stiNil && static_cast<size_t>(nColl) < pIo->m_vColl.size() && nullptr == (pNowStyleApo = pIo->m_vColl[nColl].m_xWWFly.get()))
1800 {
1801 aSeenStyles.insert(nColl);
1802
1803 nColl = pIo->m_vColl[nColl].m_nBase;
1804
1805 if (aSeenStyles.find(nColl) != aSeenStyles.end())
1806 {
1807 SAL_WARN("sw.ww8", "loop in style chain");
1808 break;
1809 }
1810
1811 eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc(nColl) : static_cast<ww::sti>(nColl);
1812 }
1813
1814 WW8FlyPara aF(bVer67, pNowStyleApo);
1815 // new FlaPara for comparison
1816 aF.Read(*aS.pSprm, pPap); // WWPara for new Para
1817 if( !( aF == *this ) ) // same APO? (or a new one?)
1818 bGrafApo = true; // no -> 1-line APO
1819 // -> graphics APO
1820 }
1821 while( false ); // block for quick exit
1822
1823 xPlcxMan->GetPap()->Restore( aSave );
1824 pIoStrm->Seek( nPos );
1825 }while( false ); // block for quick exit
1826}
1827
1828// read for Apo definitions in Styledefs
1829void WW8FlyPara::Read(sal_uInt8 nOrigSprmTPc, WW8RStyle const * pStyle)
1830{
1831 if (bVer67)
1832 {
1833 SetValSprm( &nTDxaAbs, pStyle, NS_sprm::v6::sprmPDxaAbs ); // X-position
1834 //set in me or in parent style
1835 mbVertSet |= SetValSprm(&nTDyaAbs, pStyle, NS_sprm::v6::sprmPDyaAbs); // Y-position
1836 SetValSprm( &nSp45, pStyle, NS_sprm::v6::sprmPWHeightAbs ); // height
1837 SetValSprm( &nSp28, pStyle, NS_sprm::v6::sprmPDxaWidth ); // width
1838 SetValSprm( &nLeftMargin, pStyle, NS_sprm::v6::sprmPDxaFromText ); // L-border
1839 SetValSprm( &nRightMargin, pStyle, NS_sprm::v6::sprmPDxaFromText ); // R-border
1840 SetValSprm( &nUpperMargin, pStyle, NS_sprm::v6::sprmPDyaFromText ); // U-border
1841 SetValSprm( &nLowerMargin, pStyle, NS_sprm::v6::sprmPDyaFromText ); // D-border
1842
1843 SprmResult aS = pStyle->HasParaSprm( NS_sprm::v6::sprmPWr ); // wrapping
1844 if (aS.pSprm && aS.nRemainingData >= 1)
1845 nPWr = *aS.pSprm;
1846 }
1847 else
1848 {
1849 SetValSprm( &nTDxaAbs, pStyle, NS_sprm::PDxaAbs::val ); // X-position
1850 //set in me or in parent style
1851 mbVertSet |= SetValSprm(&nTDyaAbs, pStyle, NS_sprm::PDyaAbs::val); // Y-position
1852 SetValSprm( &nSp45, pStyle, NS_sprm::PWHeightAbs::val ); // height
1853 SetValSprm( &nSp28, pStyle, NS_sprm::PDxaWidth::val ); // width
1854 SetValSprm( &nLeftMargin, pStyle, NS_sprm::PDxaFromText::val ); // L-border
1855 SetValSprm( &nRightMargin, pStyle, NS_sprm::PDxaFromText::val ); // R-border
1856 SetValSprm( &nUpperMargin, pStyle, NS_sprm::PDyaFromText::val ); // U-border
1857 SetValSprm( &nLowerMargin, pStyle, NS_sprm::PDyaFromText::val ); // D-border
1858
1859 SprmResult aS = pStyle->HasParaSprm( NS_sprm::PWr::val ); // wrapping
1860 if (aS.pSprm && aS.nRemainingData >= 1)
1861 nPWr = *aS.pSprm;
1862 }
1863
1864 if (::lcl_ReadBorders(bVer67, brc, nullptr, pStyle)) // border
1866
1867 /*
1868 #i8798#
1869 Appears that with no dyaAbs set then the actual vert anchoring set is
1870 ignored and we remain relative to text, so if that is the case we are 0
1871 from para anchor, so we update the frame to have explicitly this type of
1872 anchoring
1873 */
1874 if (!mbVertSet)
1875 nTPc = (nOrigSprmTPc & 0xCF) | 0x20;
1876 else
1877 nTPc = nOrigSprmTPc;
1878}
1879
1881{
1882 WW8FlyPara aEmpty(bVer67);
1883 /*
1884 wr of 0 like 2 appears to me to be equivalent for checking here. See
1885 #107103# if wrong, so given that the empty is 2, if we are 0 then set
1886 empty to 0 to make 0 equiv to 2 for empty checking
1887 */
1888 OSL_ENSURE(aEmpty.nPWr == 2, "this is not what we expect for nPWr");
1889 if (this->nPWr == 0)
1890 aEmpty.nPWr = 0;
1891 return aEmpty == *this;
1892}
1893
1894// #i18732# - changes made on behalf of CMC
1896 SwWW8ImplReader& rIo,
1897 WW8FlyPara& rWW,
1898 const sal_uInt32 nWWPgTop,
1899 const sal_uInt32 nPgWidth,
1900 const sal_Int32 nIniFlyDx,
1901 const sal_Int32 nIniFlyDy ):
1902nXPos(0),
1903nYPos(0),
1906nUpperMargin(rWW.nUpperMargin),
1907nLowerMargin(rWW.nLowerMargin),
1908nWidth(rWW.nSp28),
1909nHeight(rWW.nSp45),
1910nNetWidth(rWW.nSp28),
1911eHeightFix(SwFrameSize::Fixed),
1912eHRel(text::RelOrientation::PAGE_FRAME),
1913eVRel(text::RelOrientation::FRAME),
1914eVAlign(text::VertOrientation::NONE),
1915eHAlign(text::HoriOrientation::NONE),
1916nXBind(( rWW.nTPc & 0xc0 ) >> 6),
1917nYBind(( rWW.nTPc & 0x30 ) >> 4),
1918nNewNetWidth(MINFLY),
1919nLineSpace(0),
1920bAutoWidth(false),
1921bTogglePos(false)
1922{
1923 switch(rWW.nPWr)
1924 {
1925 case 0: // ST_Wrap: auto
1926 eSurround = css::text::WrapTextMode_DYNAMIC;
1927 break;
1928 case 1: // ST_Wrap: notBeside
1929 case 3: // ST_Wrap: none
1930 eSurround = css::text::WrapTextMode_NONE;
1931 break;
1932 case 2: // ST_Wrap: around
1933 case 4: // ST_Wrap: tight
1934 eSurround = css::text::WrapTextMode_PARALLEL;
1935 break;
1936 case 5: // St_Wrap: through
1937 eSurround = css::text::WrapTextMode_THROUGH;
1938 break;
1939 default:
1940 eSurround = css::text::WrapTextMode_DYNAMIC;
1941 }
1942
1943 /*
1944 #95905#, #83307# seems to have gone away now, so re-enable parallel
1945 wrapping support for frames in headers/footers. I don't know if we truly
1946 have an explicitly specified behaviour for these circumstances.
1947 */
1948
1949 if( nHeight & 0x8000 )
1950 {
1951 nHeight &= 0x7fff;
1953 }
1954
1955 if( nHeight <= MINFLY )
1956 { // no data, or bad data
1958 nHeight = MINFLY;
1959 }
1960
1961 if( nWidth <= 10 ) // auto width
1962 {
1963 bAutoWidth = true;
1964 nWidth = nNetWidth =
1965 msword_cast<sal_Int16>(nPgWidth ? nPgWidth : 2268); // 4 cm
1966 }
1967 if( nWidth <= MINFLY )
1968 nWidth = nNetWidth = MINFLY; // minimum width
1969
1970 /*
1971 See issue #i9178# for the 9 anchoring options, and make sure they stay
1972 working if you modify the anchoring logic here.
1973 */
1974
1975 // If the Fly is aligned left, right, up, or down,
1976 // the outer text distance will be ignored, because otherwise
1977 // the Fly will end up in the wrong position.
1978 // The only problem is with inside/outside.
1979
1980 //#i53725# - absolute positioned objects have to be
1981 // anchored at-paragraph to assure its correct anchor position.
1982 rIo.m_oLastAnchorPos.emplace(*rPaM.GetPoint());
1983
1984 switch (nYBind)
1985 {
1986 case 0: //relative to margin
1987 eVRel = text::RelOrientation::PAGE_PRINT_AREA;
1988 break;
1989 case 1: //relative to page
1990 eVRel = text::RelOrientation::PAGE_FRAME;
1991 break;
1992 default: //relative to text
1993 // put in initialization part eVRel = text::RelOrientation::FRAME;
1994 break;
1995 }
1996
1997// #i18732#
1998 switch( rWW.nTDyaAbs ) // particular Y-positions ?
1999 {
2000 case 0: // inline
2001 // Specifies that the parent object shall be vertically aligned in line
2002 // with the surrounding text (i.e. shall not allow any text wrapping around it)
2003 eVRel = text::RelOrientation::FRAME;
2004 break;
2005 case -4:
2006 eVAlign = text::VertOrientation::TOP;
2007 if (nYBind < 2)
2008 nUpperMargin = 0;
2009 break; // up
2010 case -8:
2011 eVAlign = text::VertOrientation::CENTER;
2012 break; // centered
2013 case -12:
2014 eVAlign = text::VertOrientation::BOTTOM;
2015 if (nYBind < 2)
2016 nLowerMargin = 0;
2017 break; // down
2018 default:
2019 nYPos = rWW.nTDyaAbs + static_cast<short>(nIniFlyDy);
2020 break; // corrections from ini file
2021 }
2022
2023 switch( rWW.nTDxaAbs ) // particular X-positions ?
2024 {
2025 case 0:
2026 eHAlign = text::HoriOrientation::LEFT;
2027 nLeftMargin = 0;
2028 break; // left
2029 case -4:
2030 eHAlign = text::HoriOrientation::CENTER;
2031 break; // centered
2032 case -8:
2033 eHAlign = text::HoriOrientation::RIGHT;
2034 nRightMargin = 0;
2035 break; // right
2036 case -12:
2037 eHAlign = text::HoriOrientation::LEFT;
2038 bTogglePos = true;
2039 break; // inside
2040 case -16:
2041 eHAlign = text::HoriOrientation::RIGHT;
2042 bTogglePos = true;
2043 break; // outside
2044 default:
2045 nXPos = rWW.nTDxaAbs + static_cast<short>(nIniFlyDx);
2046 break; // corrections from ini file
2047 }
2048
2049// #i18732#
2050 switch (nXBind) // X - binding -> transform coordinates
2051 {
2052 case 0: //relative to column
2053 eHRel = text::RelOrientation::FRAME;
2054 break;
2055 case 1: //relative to margin
2056 eHRel = text::RelOrientation::PAGE_PRINT_AREA;
2057 break;
2058 default: //relative to page
2059 // put in initialization part eHRel= text::RelOrientation::PAGE_FRAME;
2060 break;
2061 }
2062
2063 // #i36649# - adjustments for certain horizontal alignments
2064 // Note: These special adjustments found by an investigation of documents
2065 // containing frames with different left/right border distances and
2066 // distances to text. The outcome is somehow strange.
2067 // Note: These adjustments causes wrong horizontal positions for frames,
2068 // which are aligned inside|outside to page|margin on even pages,
2069 // the left and right border distances are different.
2070 // no adjustments possible, if frame has automatic width.
2071 // determine left border distance
2072 sal_Int16 nLeBorderMgn( 0 );
2073 if ( !bAutoWidth )
2074 {
2075 WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT];
2076 sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeBorderMgn);
2077 nLeBorderMgn = nLeBorderMgn + nTemp;
2078 }
2079 // determine right border distance
2080 sal_Int16 nRiBorderMgn( 0 );
2081 if ( !bAutoWidth )
2082 {
2083 WW8_BRCVer9 &rBrc = rWW.brc[WW8_RIGHT];
2084 sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nRiBorderMgn);
2085 nRiBorderMgn = nRiBorderMgn + nTemp;
2086 }
2087 if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_FRAME )
2088 {
2089 // convert 'left to page' to
2090 // 'from left -<width>-<2*left border distance>-<right wrap distance>
2091 // to page text area'
2093 eHRel = text::RelOrientation::PAGE_PRINT_AREA;
2094 nXPos = -nWidth - (2*nLeBorderMgn) - rWW.nRightMargin;
2095 // re-set left wrap distance
2097 }
2098 else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_FRAME )
2099 {
2100 // convert 'right to page' to
2101 // 'from left <right border distance-left border distance>+<left wrap distance>
2102 // to right page border'
2104 eHRel = text::RelOrientation::PAGE_RIGHT;
2105 nXPos = ( nRiBorderMgn - nLeBorderMgn ) + rWW.nLeftMargin;
2106 // re-set right wrap distance
2108 }
2109 else if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_PRINT_AREA )
2110 {
2111 // convert 'left to margin' to
2112 // 'from left -<left border distance> to page text area'
2114 eHRel = text::RelOrientation::PAGE_PRINT_AREA;
2115 nXPos = -nLeBorderMgn;
2116 // re-set left wrap distance
2118 }
2119 else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_PRINT_AREA )
2120 {
2121 // convert 'right to margin' to
2122 // 'from left -<width>-<left border distance> to right page border'
2124 eHRel = text::RelOrientation::PAGE_RIGHT;
2125 nXPos = -nWidth - nLeBorderMgn;
2126 // re-set right wrap distance
2128 }
2129 else if (rWW.bBorderLines)
2130 {
2131 /*
2132 #i582#
2133 Word has a curious bug where the offset stored do not take into
2134 account the internal distance from the corner both
2135 */
2136 WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT];
2137 sal_Int16 nLeLMgn = 0;
2138 sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeLMgn);
2139 nLeLMgn = nLeLMgn + nTemp;
2140
2141 if (nLeLMgn)
2142 {
2143 if (eHAlign == text::HoriOrientation::LEFT)
2145 nXPos = nXPos - nLeLMgn;
2146 }
2147 }
2148
2149 // adjustments for certain vertical alignments
2150 if ( eVAlign == text::VertOrientation::NONE && eVRel == text::RelOrientation::PAGE_PRINT_AREA )
2151 {
2152 // convert "<X> from top page text area" to
2153 // "<X + page top margin> from page"
2154 eVRel = text::RelOrientation::PAGE_FRAME;
2155 nYPos = static_cast< sal_Int16 >( nYPos + nWWPgTop );
2156 }
2157
2158 FlySecur1( nWidth, rWW.bBorderLines ); // Do the borders match ?
2160
2161}
2162
2163// If a Fly in WW has automatic width, this has to be simulated
2164// by modifying the Fly width (fixed in SW) afterwards.
2165// This can increase or decrease the Fly width, because the default value
2166// is set without knowledge of the contents.
2168{
2169 if( bAutoWidth && nInWidth > nNewNetWidth )
2170 nNewNetWidth = nInWidth;
2171}
2172
2174{
2175 if (!m_xFlyFormat)
2176 return nullptr;
2177 return static_cast<SwFlyFrameFormat*>(m_xFlyFormat->GetFormat());
2178}
2179
2181{
2182 if (pNewFlyFormat)
2183 m_xFlyFormat.reset(new FrameDeleteWatch(pNewFlyFormat));
2184 else
2185 m_xFlyFormat.reset();
2186}
2187
2188// The class WW8FlySet is derived from SfxItemSetFixed and does not
2189// provide more, but is easier to handle for me.
2190// WW8FlySet-ctor for Apos and graphics Apos
2192 const WW8SwFlyPara* pFS, bool bGraf)
2193 : SfxItemSetFixed(rReader.m_rDoc.GetAttrPool())
2194{
2195 Reader::ResetFrameFormatAttrs(*this); // remove distance/border
2196 // position
2197 Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
2198
2199/*Below can all go when we have from left in rtl mode*/
2200 SwTwips nXPos = pFS->nXPos;
2201 sal_Int16 eHRel = pFS->eHRel;
2202 rReader.MiserableRTLGraphicsHack(nXPos, pFS->nWidth, pFS->eHAlign, eHRel);
2203/*Above can all go when we have from left in rtl mode*/
2204 Put( SwFormatHoriOrient(nXPos, pFS->eHAlign, pFS->eHRel, pFS->bTogglePos ));
2205 Put( SwFormatVertOrient( pFS->nYPos, pFS->eVAlign, pFS->eVRel ) );
2206
2207 if (pFS->nLeftMargin || pFS->nRightMargin) // set borders
2209
2210 if (pFS->nUpperMargin || pFS->nLowerMargin)
2212
2213 //we no longer need to hack around the header/footer problems
2214 SwFormatSurround aSurround(pFS->eSurround);
2215 if ( pFS->eSurround == css::text::WrapTextMode_DYNAMIC )
2216 aSurround.SetAnchorOnly( true );
2217 Put( aSurround );
2218
2219 short aSizeArray[5]={0};
2220 SwWW8ImplReader::SetFlyBordersShadow(*this,pFW->brc,&aSizeArray[0]);
2221
2222 // the 5th parameter is always 0, thus we lose nothing due to the cast
2223
2224 // #i27767#
2225 // #i35017# - constant name has changed
2227 text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ) );
2228
2229 if( bGraf )
2230 return;
2231
2233 // adjust size
2234
2235 //Ordinarily with frames, the border width and spacing is
2236 //placed outside the frame, making it larger. With these
2237 //types of frames, the left right thickness and space makes
2238 //it wider, but the top bottom spacing and border thickness
2239 //is placed inside.
2240 Put( SwFormatFrameSize( pFS->eHeightFix, pFS->nWidth +
2241 aSizeArray[WW8_LEFT] + aSizeArray[WW8_RIGHT],
2242 pFS->nHeight));
2243}
2244
2245// WW8FlySet-ctor for character bound graphics
2247 const WW8_PIC& rPic, tools::Long nWidth, tools::Long nHeight )
2248 : SfxItemSetFixed<RES_FRMATR_BEGIN,RES_FRMATR_END-1>(rReader.m_rDoc.GetAttrPool())
2249{
2250 Init(rReader, pPaM);
2251
2252 Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
2253
2254 short aSizeArray[5]={0};
2255 /*
2256 If we have set borders then in word the graphic is displaced from the left
2257 and top the width of the borders of those sides, and then the shadow
2258 itself is drawn to the bottom and right of the displaced graphic. In word
2259 the total size is that of the graphic plus the borders, plus the total
2260 shadow around all edges, for this translation the top and left shadow
2261 region is translated spacing around the graphic to those sides, and the
2262 bottom and right shadow size is added to the graphic size.
2263 */
2264 WW8_BRCVer9 brcVer9[4];
2265 for (int i = 0; i < 4; i++)
2266 brcVer9[i] = WW8_BRCVer9(rPic.rgbrc[i]);
2267 if (SwWW8ImplReader::SetFlyBordersShadow( *this, brcVer9, &aSizeArray[0]))
2268 {
2269 Put(SvxLRSpaceItem( aSizeArray[WW8_LEFT], 0, 0, RES_LR_SPACE ) );
2270 Put(SvxULSpaceItem( aSizeArray[WW8_TOP], 0, RES_UL_SPACE ));
2271 aSizeArray[WW8_RIGHT]*=2;
2272 aSizeArray[WW8_BOT]*=2;
2273 }
2274
2275 Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth+aSizeArray[WW8_LEFT]+
2276 aSizeArray[WW8_RIGHT], nHeight+aSizeArray[WW8_TOP]
2277 + aSizeArray[WW8_BOT]) );
2278}
2279
2280void WW8FlySet::Init(const SwWW8ImplReader& rReader, const SwPaM* pPaM)
2281{
2282 Reader::ResetFrameFormatAttrs(*this); // remove distance/borders
2283
2284 Put(SvxLRSpaceItem(RES_LR_SPACE)); //inline writer ole2 objects start with 0.2cm l/r
2285 SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
2286
2287 aAnchor.SetAnchor(pPaM->GetPoint());
2288 Put(aAnchor);
2289
2290 //The horizontal default is on the baseline, the vertical is centered
2291 //around the character center it appears
2293 Put(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER,text::RelOrientation::CHAR));
2294 else
2295 Put(SwFormatVertOrient(0, text::VertOrientation::TOP, text::RelOrientation::FRAME));
2296}
2297
2299 : m_pCtrlStck(pStack),
2300 m_aChrSet(rDoc.GetAttrPool()),
2301 m_aParSet(rDoc.GetAttrPool())
2302{
2303 //Close any open character properties and duplicate them inside the
2304 //first table cell
2305 size_t nCnt = m_pCtrlStck->size();
2306 for (size_t i=0; i < nCnt; ++i)
2307 {
2308 const SwFltStackEntry& rEntry = (*m_pCtrlStck)[ i ];
2309 if (rEntry.m_bOpen)
2310 {
2311 if (isCHRATR(rEntry.m_pAttr->Which()))
2312 {
2313 m_aChrSet.Put( *rEntry.m_pAttr );
2314
2315 }
2316 else if (isPARATR(rEntry.m_pAttr->Which()))
2317 {
2318 m_aParSet.Put( *rEntry.m_pAttr );
2319 }
2320 }
2321 }
2322}
2323
2325{
2326 for (const SfxItemSet* pSet : {&m_aChrSet, &m_aParSet})
2327 {
2328 if( pSet->Count() )
2329 {
2330 SfxItemIter aIter( *pSet );
2331 const SfxPoolItem* pItem = aIter.GetCurItem();
2332 do
2333 {
2334 m_pCtrlStck->NewAttr(rPos, *pItem);
2335 } while ((pItem = aIter.NextItem()));
2336 }
2337 }
2338}
2339
2341{
2342 WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get());
2343
2344 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false);
2345
2346 // set Pam in FlyFrame
2347 const SwFormatContent& rContent = pFlyFormat->GetContent();
2348 OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." );
2349 m_pPaM->GetPoint()->Assign( rContent.GetContentIdx()->GetIndex() + 1 );
2350
2351 aDup.Insert(*m_pPaM->GetPoint());
2352}
2353
2355 const SwPosition &rPos, bool bTableJoin)
2356{
2357 SwTwips nRetWidth = 0;
2358 if (!pFlyFormat)
2359 return nRetWidth;
2360 // Close all attributes, because otherwise attributes can appear
2361 // that extend out of Flys
2362 WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get());
2363 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false);
2364
2365 /*
2366 #i1291
2367 If this fly frame consists entirely of one table inside a frame
2368 followed by an empty paragraph then we want to delete the empty
2369 paragraph so as to get the frame to autoshrink to the size of the
2370 table to emulate words behaviour closer.
2371 */
2372 if (bTableJoin)
2373 {
2374 const SwNodeIndex* pNodeIndex = pFlyFormat->GetContent().
2375 GetContentIdx();
2376 if (pNodeIndex)
2377 {
2378 SwNodeIndex aIdx( *pNodeIndex, 1 ),
2379 aEnd( *pNodeIndex->GetNode().EndOfSectionNode() );
2380
2381 if (aIdx < aEnd)
2382 {
2383 if(aIdx.GetNode().IsTableNode())
2384 {
2385 SwTableNode *pTable = aIdx.GetNode().GetTableNode();
2386 aIdx = *aIdx.GetNode().EndOfSectionNode();
2387 ++aIdx;
2388 if ( (aIdx < aEnd) && aIdx.GetNode().IsTextNode() )
2389 {
2390 SwTextNode *pNd = aIdx.GetNode().GetTextNode();
2391 ++aIdx;
2392 if (aIdx == aEnd && pNd && pNd->GetText().isEmpty())
2393 {
2394 //An extra pre-created by writer unused paragraph
2395
2396 //delete after import is complete rather than now
2397 //to avoid the complication of managing uncommitted
2398 //ctrlstack properties that refer to it.
2400
2401 SwTable& rTable = pTable->GetTable();
2402 SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
2403
2404 if (pTableFormat)
2405 {
2406 SwFormatFrameSize aSize = pTableFormat->GetFrameSize();
2408 aSize.SetHeight(MINLAY);
2409 pFlyFormat->SetFormatAttr(aSize);
2410 SwFormatHoriOrient aHori = pTableFormat->GetHoriOrient();
2411 // passing the table orientation of
2412 // LEFT_AND_WIDTH to the frame seems to
2413 // work better than FULL, especially if the
2414 // table width exceeds the page width, however
2415 // I am not brave enough to set it in all
2416 // instances
2417 pTableFormat->SetFormatAttr( SwFormatHoriOrient(0, ( aHori.GetHoriOrient() == text::HoriOrientation::LEFT_AND_WIDTH ) ? ::text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::FULL ) );
2418 nRetWidth = aSize.GetWidth();
2419 }
2420 }
2421 }
2422 }
2423 }
2424 }
2425 }
2426
2427 *m_pPaM->GetPoint() = rPos;
2428 aDup.Insert(*m_pPaM->GetPoint());
2429 return nRetWidth;
2430}
2431
2432std::unique_ptr<WW8FlyPara> SwWW8ImplReader::ConstructApo(const ApoTestResults &rApo,
2433 const WW8_TablePos *pTabPos)
2434{
2435 OSL_ENSURE(rApo.HasFrame() || pTabPos,
2436 "If no frame found, *MUST* be in a table");
2437
2438 std::unique_ptr<WW8FlyPara> pRet(new WW8FlyPara(m_bVer67, rApo.mpStyleApo));
2439
2440 // find APO parameter and test for bGrafApo
2441 if (rApo.HasFrame())
2442 pRet->ReadFull(rApo.m_nSprm29, this);
2443
2444 pRet->ApplyTabPos(pTabPos);
2445
2446 if (pRet->IsEmpty())
2447 {
2448 pRet.reset();
2449 }
2450 return pRet;
2451}
2452
2454{
2455 // Find the DCS (Drop Cap Specifier) for the paragraph
2456 // if does not exist or if the first three bits are 0
2457 // then there is no dropcap on the paragraph
2458 WW8PLCFx_Cp_FKP *pPap = m_xPlcxMan ? m_xPlcxMan->GetPapPLCF() : nullptr;
2459 if (pPap)
2460 {
2461 SprmResult aDCS;
2462 if (m_bVer67)
2463 aDCS = pPap->HasSprm(NS_sprm::v6::sprmPDcs);
2464 else
2465 aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PDcs::val);
2466 if (aDCS.pSprm && aDCS.nRemainingData >= 2)
2467 {
2468 /*
2469 fdct short :3 0007 drop cap type
2470 0 no drop cap
2471 1 normal drop cap
2472 2 drop cap in margin
2473 */
2474 short nDCS = SVBT16ToUInt16(aDCS.pSprm);
2475 if (nDCS & 7)
2476 return true;
2477 }
2478 }
2479 return false;
2480}
2481
2483{
2484 m_xWFlyPara = ConstructApo(rApo, pTabPos);
2485 if (!m_xWFlyPara)
2486 return false;
2487
2488 // <WW8SwFlyPara> constructor has changed - new 4th parameter
2489 // containing WW8 page top margin.
2490 m_xSFlyPara.reset(new WW8SwFlyPara( *m_pPaM, *this, *m_xWFlyPara,
2494
2495 // If this paragraph is a Dropcap set the flag and we will deal with it later
2496 if (IsDropCap())
2497 {
2498 m_bDropCap = true;
2499 m_xCurrentItemSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_END - 1>));
2500 return false;
2501 }
2502
2503 if (!m_xWFlyPara->bGrafApo)
2504 {
2505
2506 // Within the GrafApo text attributes have to be ignored, because
2507 // they would apply to the following lines. The frame is only inserted
2508 // if it is not merely positioning a single image. If it is an image
2509 // frame, pWFlyPara and pSFlyPara are retained and the resulting
2510 // attributes applied to the image when inserting the image.
2511
2512 WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), false);
2513
2514 // ofz#34749 we shouldn't anchor anything into an 'extra' paragraph scheduled for
2515 // removal at end of import, but check if that scenario is happening
2517
2518 if (pTabPos)
2519 {
2521 {
2522 // We just inserted a floating table and we'll insert a next one.
2523 SwFrameFormat* pFormat = m_xFormatOfJustInsertedApo->GetFormat();
2524 if (pFormat)
2525 {
2526 const SwNode* pAnchorNode = pFormat->GetAnchor().GetAnchorNode();
2527 SwPosition* pPoint = m_pPaM->GetPoint();
2528 if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
2529 {
2530 // The two fly frames would have the same anchor position, leading to
2531 // potentially overlapping text, prevent that.
2532 AppendTextNode(*pPoint);
2533 }
2534 }
2535 }
2536
2537 // Map a positioned table to a split fly.
2538 aFlySet.Put(SwFormatFlySplit(true));
2539 }
2540
2542 m_pPaM->GetPoint(), &aFlySet));
2543 OSL_ENSURE(m_xSFlyPara->GetFlyFormat()->GetAnchor().GetAnchorId() ==
2544 WW8SwFlyPara::eAnchor, "Not the anchor type requested!");
2545
2546 if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
2547 {
2548 if (!m_pDrawModel)
2549 GraphicCtor();
2550
2551 SdrObject* pOurNewObject = CreateContactObject(pFlyFormat);
2552 m_xWWZOrder->InsertTextLayerObject(pOurNewObject);
2553 }
2554
2555 if (RndStdIds::FLY_AS_CHAR != WW8SwFlyPara::eAnchor && m_xSFlyPara->GetFlyFormat())
2556 {
2557 m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), m_xSFlyPara->GetFlyFormat());
2558 }
2559
2560 // remember Pos in body text
2561 m_xSFlyPara->xMainTextPos = m_rDoc.CreateUnoCursor(*m_pPaM->GetPoint());
2562
2563 //remove fltanchors, otherwise they will be closed inside the
2564 //frame, which makes no sense, restore them after the frame is
2565 //closed
2566 m_xSFlyPara->xOldAnchorStck = std::move(m_xAnchorStck);
2568
2569 if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
2570 MoveInsideFly(pFlyFormat);
2571
2572 // 1) ReadText() is not called recursively because the length of
2573 // the Apo is unknown at that time, and ReadText() needs it.
2574 // 2) the CtrlStck is not re-created.
2575 // the Char attributes continue (trouble with Sw-attributes)
2576 // Para attributes must be reset at the end of every paragraph,
2577 // i.e. at the end of a paragraph there must not be para attributes
2578 // on the stack
2579 }
2580 return true;
2581}
2582
2583void wwSectionManager::JoinNode(const SwPosition &rPos, const SwNode &rNode)
2584{
2585 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
2586 maSegments.back().maStart.Assign(rNode);
2587}
2588
2589bool SwWW8ImplReader::JoinNode(SwPaM &rPam, bool bStealAttr)
2590{
2591 bool bRet = false;
2592 rPam.GetPoint()->SetContent(0); // go to start of paragraph
2593
2594 SwNodeIndex aPref(rPam.GetPoint()->GetNode(), -1);
2595
2596 if (SwTextNode* pNode = aPref.GetNode().GetTextNode())
2597 {
2598 m_aSectionManager.JoinNode(*rPam.GetPoint(), aPref.GetNode());
2599 rPam.GetPoint()->Assign(*pNode, pNode->GetText().getLength());
2600 if (bStealAttr)
2601 m_xCtrlStck->StealAttr(rPam.GetPoint()->GetNode());
2602
2603 if (m_oLastAnchorPos || m_xPreviousNode || (m_xSFlyPara && m_xSFlyPara->xMainTextPos))
2604 {
2605 SwNodeIndex aToBeJoined(aPref, 1);
2606
2607 if (m_oLastAnchorPos)
2608 {
2609 //If the last anchor pos is here, then clear the anchor pos.
2610 //This "last anchor pos" is only used for fixing up the
2611 //positions of things anchored to page breaks and here
2612 //we are removing the last paragraph of a frame, so there
2613 //cannot be a page break at this point so we can
2614 //safely reset m_pLastAnchorPos to avoid any dangling
2615 //SwContentIndex's pointing into the deleted paragraph
2616 SwNodeIndex aLastAnchorPos(m_oLastAnchorPos->GetNode());
2617 if (aLastAnchorPos == aToBeJoined)
2618 m_oLastAnchorPos.reset();
2619 }
2620
2621 if (m_xPreviousNode)
2622 {
2623 //If the drop character start pos is here, then clear it.
2624 SwNodeIndex aDropCharPos(*m_xPreviousNode->GetTextNode());
2625 if (aDropCharPos == aToBeJoined)
2626 m_xPreviousNode.reset();
2627 }
2628
2629 if (m_xSFlyPara && m_xSFlyPara->xMainTextPos)
2630 {
2631 // If an open apo pos is here, then clear it before
2632 // JoinNext destroys it
2633 SwNodeIndex aOpenApoPos(m_xSFlyPara->xMainTextPos->GetPoint()->GetNode());
2634 if (aOpenApoPos == aToBeJoined)
2635 m_xSFlyPara->xMainTextPos.reset();
2636 }
2637 }
2638
2639 pNode->JoinNext();
2640
2641 bRet = true;
2642 }
2643 return bRet;
2644}
2645
2646//In auto-width word frames negative after-indent values are ignored
2648{
2649 const SwNodeIndex* pSttNd = pFlyFormat->GetContent().GetContentIdx();
2650 if (!pSttNd)
2651 return;
2652
2653 SwNodeIndex aIdx(*pSttNd, 1);
2654 SwNodeIndex aEnd(*pSttNd->GetNode().EndOfSectionNode());
2655 while (aIdx < aEnd)
2656 {
2657 SwTextNode *pNd = aIdx.GetNode().GetTextNode();
2658 if (pNd)
2659 {
2660 const SvxRightMarginItem & rRightMargin(pNd->GetAttr(RES_MARGIN_RIGHT));
2661 if (rRightMargin.GetRight() < 0)
2662 {
2663 SvxRightMarginItem rightMargin(rRightMargin);
2664 rightMargin.SetRight(0);
2665 pNd->SetAttr(rightMargin);
2666 }
2667 }
2668 ++aIdx;
2669 }
2670}
2671
2673{
2674 OSL_ENSURE(m_xWFlyPara, "no pWFlyPara to close");
2675 if (!m_xWFlyPara)
2676 return;
2677 if (m_xWFlyPara->bGrafApo)
2678 {
2679 // image frame that has not been inserted: delete empty paragraph + attr
2680 JoinNode(*m_pPaM, true);
2681
2682 }
2683 else
2684 {
2685 if (!m_xSFlyPara->xMainTextPos)
2686 {
2687 OSL_ENSURE(m_xSFlyPara->xMainTextPos, "StopApo: xMainTextPos is nullptr");
2688 return;
2689 }
2690
2691 /*
2692 What we are doing with this temporary nodeindex is as follows: The
2693 stack of attributes normally only places them into the document when
2694 the current insertion point has passed them by. Otherwise the end
2695 point of the attribute gets pushed along with the insertion point. The
2696 insertion point is moved and the properties committed during
2697 MoveOutsideFly. We also may want to remove the final paragraph in the
2698 frame, but we need to wait until the properties for that frame text
2699 have been committed otherwise they will be lost. So we first get a
2700 handle to the last the filter inserted. After the attributes are
2701 committed, if that paragraph exists we join it with the para after it
2702 that comes with the frame by default so that as normal we don't end up
2703 with one more paragraph than we wanted.
2704 */
2705 SwNodeIndex aPref(m_pPaM->GetPoint()->GetNode(), -1);
2706
2707 SwTwips nNewWidth =
2708 MoveOutsideFly(m_xSFlyPara->GetFlyFormat(), *m_xSFlyPara->xMainTextPos->GetPoint());
2709 if (nNewWidth)
2710 m_xSFlyPara->BoxUpWidth(nNewWidth);
2711
2712 Color aBg(ColorTransparency, 0xFE, 0xFF, 0xFF, 0xFF); //Transparent by default
2713
2714 SwTextNode* pNd = aPref.GetNode().GetTextNode();
2715 SwTextNode* pJoinNext = nullptr;
2716 if (pNd && m_xSFlyPara->GetFlyFormat())
2717 {
2718 /*
2719 #i582#
2720 Take the last paragraph background colour and fill the frame with
2721 it. Otherwise, make it transparent, this appears to be how MSWord
2722 works
2723 */
2724 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_BACKGROUND);
2725 const SvxBrushItem &rBrush = static_cast<const SvxBrushItem&>(rItm);
2726 if (rBrush.GetColor() != COL_AUTO)
2727 aBg = rBrush.GetColor();
2728
2729 if (m_oLastAnchorPos)
2730 {
2731 //If the last anchor pos is here, then clear the anchor pos.
2732 //This "last anchor pos" is only used for fixing up the
2733 //positions of things anchored to page breaks and here
2734 //we are removing the last paragraph of a frame, so there
2735 //cannot be a page break at this point so we can
2736 //safely reset m_pLastAnchorPos to avoid any dangling
2737 //SwContentIndex's pointing into the deleted paragraph
2738 SwNodeIndex aLastAnchorPos(m_oLastAnchorPos->GetNode());
2739 SwNodeIndex aToBeJoined(aPref, 1);
2740 if (aLastAnchorPos == aToBeJoined)
2741 m_oLastAnchorPos.reset();
2742 }
2743
2744 //Get rid of extra empty paragraph
2745 pJoinNext = pNd;
2746 }
2747
2748 if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
2749 pFlyFormat->SetFormatAttr(SvxBrushItem(aBg, RES_BACKGROUND));
2750
2752 if (pJoinNext)
2753 pJoinNext->JoinNext();
2754
2755 m_xAnchorStck = std::move(m_xSFlyPara->xOldAnchorStck);
2756
2757 // When inserting a graphic into the fly frame using the auto
2758 // function, the extension of the SW-fly has to be set
2759 // manually as the SW fly has no auto function to adjust the
2760 // frame´s size.
2761 if (m_xSFlyPara->nNewNetWidth > MINFLY && m_xSFlyPara->GetFlyFormat()) // BoxUpWidth ?
2762 {
2763 tools::Long nW = m_xSFlyPara->nNewNetWidth;
2764 nW += m_xSFlyPara->nWidth - m_xSFlyPara->nNetWidth; // border for it
2765 m_xSFlyPara->GetFlyFormat()->SetFormatAttr(
2766 SwFormatFrameSize(m_xSFlyPara->eHeightFix, nW, m_xSFlyPara->nHeight));
2767 }
2768 /*
2769 Word set *no* width meaning it's an automatic width. The
2770 SwFlyPara reader will have already set a fallback width of the
2771 printable regions width, so we should reuse it. Despite the related
2772 problems with layout addressed with a hack in WW8FlyPara's constructor
2773 #i27204# Added AutoWidth setting. Left the old CalculateFlySize in place
2774 so that if the user unselects autowidth, the width doesn't max out
2775 */
2776 else if (!m_xWFlyPara->nSp28 && m_xSFlyPara->GetFlyFormat())
2777 {
2778 using namespace sw::util;
2779 SfxItemSet aFlySet( m_xSFlyPara->GetFlyFormat()->GetAttrSet() );
2780
2781 SwFormatFrameSize aSize(aFlySet.Get(RES_FRM_SIZE));
2782
2783 aFlySet.ClearItem(RES_FRM_SIZE);
2784
2785 if (!m_bFuzzing)
2786 {
2787 CalculateFlySize(aFlySet, m_xSFlyPara->xMainTextPos->GetPoint()->GetNode(),
2788 m_xSFlyPara->nWidth);
2789 }
2790
2791 nNewWidth = aFlySet.Get(RES_FRM_SIZE).GetWidth();
2792
2793 aSize.SetWidth(nNewWidth);
2795
2796 m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aSize);
2797 }
2798
2799 m_xSFlyPara->xMainTextPos.reset();
2800// To create the SwFrames when inserting into an existing document, fltshell.cxx
2801// will call pFlyFrame->MakeFrames() when setting the FltAnchor attribute
2802
2803 }
2804
2805 //#i8062#
2806 if (m_xSFlyPara && m_xSFlyPara->GetFlyFormat())
2807 m_xFormatOfJustInsertedApo.reset(new FrameDeleteWatch(m_xSFlyPara->GetFlyFormat()));
2808
2809 m_xSFlyPara.reset();
2810 m_xWFlyPara.reset();
2811}
2812
2813// TestSameApo() returns if it's the same Apo or a different one
2815 const WW8_TablePos *pTabPos)
2816{
2817 if (!m_xWFlyPara)
2818 {
2819 OSL_ENSURE(m_xWFlyPara, " Where is my pWFlyPara ? ");
2820 return true;
2821 }
2822
2823 // We need to a full comparison (excepting borders) to identify all
2824 // combinations style/hard correctly. For this reason we create a
2825 // temporary WW8FlyPara (depending on if style or not), apply the
2826 // hard attributes and then compare.
2827
2828 // For comparison
2829 WW8FlyPara aF(m_bVer67, rApo.mpStyleApo);
2830 // WWPara for current para
2831 if (rApo.HasFrame())
2832 aF.Read(rApo.m_nSprm29, m_xPlcxMan->GetPapPLCF());
2833 aF.ApplyTabPos(pTabPos);
2834
2835 return aF == *m_xWFlyPara;
2836}
2837
2839 const bool bFirstLineOfStSet,
2840 const bool bLeftIndentSet )
2841{
2842 if( m_bNoAttrImport ) // for ignoring styles during doc inserts
2843 return;
2844
2845 if (m_pCurrentColl)
2846 {
2847 OSL_ENSURE(rAttr.Which() != RES_FLTR_REDLINE, "redline in style!");
2849 }
2850 else if (m_xCurrentItemSet)
2851 {
2852 m_xCurrentItemSet->Put(rAttr);
2853 }
2854 else if (rAttr.Which() == RES_FLTR_REDLINE)
2855 {
2856 m_xRedlineStack->open(*m_pPaM->GetPoint(), rAttr);
2857 }
2858 else
2859 {
2860 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), rAttr);
2861 // #i103711#
2862 if ( bFirstLineOfStSet )
2863 {
2864 const SwNode* pNd = &(m_pPaM->GetPoint()->GetNode());
2866 }
2867 // #i105414#
2868 if ( bLeftIndentSet )
2869 {
2870 const SwNode* pNd = &(m_pPaM->GetPoint()->GetNode());
2872 }
2873 }
2874
2876 m_pPostProcessAttrsInfo->mItemSet.Put(rAttr);
2877}
2878
2879// fetches attribute from FormatColl / Stack / Doc
2881{
2882 const SfxPoolItem* pRet = nullptr;
2883 if (m_pCurrentColl)
2884 pRet = &(m_pCurrentColl->GetFormatAttr(nWhich));
2885 else if (m_xCurrentItemSet)
2886 {
2887 pRet = m_xCurrentItemSet->GetItem(nWhich);
2888 if (!pRet)
2889 pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr;
2890 if (!pRet)
2891 pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
2892 }
2893 else if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
2894 {
2895 pRet = m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), nWhich);
2896 if (!pRet)
2897 {
2898 if (m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_pFormat &&
2899 m_vColl[m_nCurrentColl].m_bColl)
2900 {
2901 pRet = &(m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(nWhich));
2902 }
2903 }
2904 if (!pRet)
2905 pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr;
2906 if (!pRet)
2907 pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
2908 }
2909 else
2910 pRet = m_xCtrlStck->GetFormatAttr(*m_pPaM->GetPoint(), nWhich);
2911 return pRet;
2912}
2913
2914// The methods get as parameters the token id and the length of the following
2915// parameters according to the table in WWScan.cxx.
2916void SwWW8ImplReader::Read_Special(sal_uInt16, const sal_uInt8* pData, short nLen)
2917{
2918 if (nLen < 1)
2919 {
2920 m_bSpec = false;
2921 return;
2922 }
2923 m_bSpec = ( *pData != 0 );
2924}
2925
2926// Read_Obj is used for fObj and for fOle2 !
2927void SwWW8ImplReader::Read_Obj(sal_uInt16 , const sal_uInt8* pData, short nLen)
2928{
2929 if (nLen < 1)
2930 m_bObj = false;
2931 else
2932 {
2933 m_bObj = 0 != *pData;
2934
2935 if( m_bObj && m_nPicLocFc && m_bEmbeddObj )
2936 {
2937 if (!m_aFieldStack.empty() && m_aFieldStack.back().mnFieldId == 56)
2938 {
2939 // For LINK fields, store the nObjLocFc value in the field entry
2940 m_aFieldStack.back().mnObjLocFc = m_nPicLocFc;
2941 }
2942 else
2943 {
2945 }
2946 }
2947 }
2948}
2949
2950void SwWW8ImplReader::Read_PicLoc(sal_uInt16 , const sal_uInt8* pData, short nLen )
2951{
2952 if (nLen < 4)
2953 {
2954 m_nPicLocFc = 0;
2955 m_bSpec = false; // Is this always correct?
2956 }
2957 else
2958 {
2959 m_nPicLocFc = SVBT32ToUInt32( pData );
2960 m_bSpec = true;
2961
2962 if( m_bObj && m_nPicLocFc && m_bEmbeddObj )
2964 }
2965}
2966
2967void SwWW8ImplReader::Read_POutLvl(sal_uInt16, const sal_uInt8* pData, short nLen )
2968{
2969 if (nLen < 0)
2970 {
2972 return;
2973 }
2974
2975 if (m_pCurrentColl != nullptr)
2976 {
2978 if (pSI && pSI->m_bColl && pSI->m_pFormat)
2979 {
2980 pSI->mnWW8OutlineLevel =
2981 static_cast< sal_uInt8 >( ( (pData && nLen >= 1) ? *pData : 0 ) );
2983 if (nLevel == 0)
2984 {
2985 SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pSI->m_pFormat);
2987 }
2989 }
2990 }
2991 else if (m_pPaM != nullptr)
2992 {
2993 const sal_uInt8 nOutlineLevel
2995 static_cast<sal_uInt8>(((pData && nLen >= 1) ? *pData : 0)));
2997 }
2998}
2999
3000void SwWW8ImplReader::Read_Symbol(sal_uInt16, const sal_uInt8* pData, short nLen )
3001{
3002 if( m_bIgnoreText )
3003 return;
3004
3005 if (nLen < (m_bVer67 ? 3 : 4))
3006 {
3007 //otherwise disable after we print the char
3008 if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
3009 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
3010 m_bSymbol = false;
3011 }
3012 else
3013 {
3014 // Make new Font-Attribute
3015 // (will be closed in SwWW8ImplReader::ReadChars() )
3016
3017 //Will not be added to the charencoding stack, for styles the real
3018 //font setting will be put in as the styles charset, and for plain
3019 //text encoding for symbols is moot. Drawing boxes will check bSymbol
3020 //themselves so they don't need to add it to the stack either.
3021 if (SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_FONT))
3022 {
3023 SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CJK_FONT);
3024 SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CTL_FONT);
3025 if( m_bVer67 )
3026 {
3027 //convert single byte from MS1252 to Unicode
3028 m_cSymbol = OUString(
3029 reinterpret_cast<const char*>(pData+2), 1,
3030 RTL_TEXTENCODING_MS_1252).toChar();
3031 }
3032 else
3033 {
3034 //already is Unicode
3035 m_cSymbol = SVBT16ToUInt16( pData+2 );
3036 }
3037 m_bSymbol = true;
3038 }
3039 }
3040}
3041
3043{
3044 return const_cast<SwWW8StyInf *>(nColl < m_vColl.size() ? &m_vColl[nColl] : nullptr);
3045}
3046
3047// Read_BoldUsw for italic, bold, small caps, majuscule, struck out,
3048// contour and shadow
3049void SwWW8ImplReader::Read_BoldUsw( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
3050{
3051 const int nContiguousWestern = 8;
3052 const int nWestern = nContiguousWestern + 1;
3053 const int nEastern = 2;
3054 const int nCTL = 2;
3055 const int nIds = nWestern + nEastern + nCTL;
3056 static const sal_uInt16 nEndIds[ nIds ] =
3057 {
3062
3064
3066
3068 };
3069
3070 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
3071
3072 sal_uInt8 nI;
3073 // the attribute number for "double strike-through" breaks rank
3075 nI = nContiguousWestern; // The out of sequence western id
3076 else
3077 {
3078 // The contiguous western ids
3079 if (eVersion <= ww::eWW2)
3080 nI = static_cast< sal_uInt8 >(nId - 60);
3081 else if (eVersion < ww::eWW8)
3082 nI = static_cast< sal_uInt8 >(nId - NS_sprm::v6::sprmCFBold);
3083 else
3084 nI = static_cast< sal_uInt8 >(nId - NS_sprm::CFBold::val);
3085 }
3086
3087 sal_uInt16 nMask = 1 << nI;
3088
3089 if (nLen < 1)
3090 {
3091 if (nI < 2)
3092 {
3093 if (eVersion <= ww::eWW6)
3094 {
3095 // reset the CTL Weight and Posture, because they are the same as their
3096 // western equivalents in ww6
3097 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nEastern + nI ] );
3098 }
3099 // reset the CJK Weight and Posture, because they are the same as their
3100 // western equivalents in word
3101 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nI ] );
3102 }
3103 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nI ] );
3104 m_xCtrlStck->SetToggleAttr(nI, false);
3105 return;
3106 }
3107 // value: 0 = off, 1 = on, 128 = like style, 129 contrary to style
3108 bool bOn = *pData & 1;
3110 if (m_xPlcxMan && eVersion > ww::eWW2)
3111 {
3112 SprmResult aCharIstd =
3114 if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2)
3115 pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm));
3116 }
3117
3118 if( m_pCurrentColl ) // StyleDef -> remember flags
3119 {
3120 if (pSI)
3121 {
3122 // The style based on has Bit 7 set ?
3123 if (
3124 pSI->m_nBase < m_vColl.size() && (*pData & 0x80) &&
3125 (m_vColl[pSI->m_nBase].m_n81Flags & nMask)
3126 )
3127 {
3128 bOn = !bOn; // invert
3129 }
3130
3131 if (bOn)
3132 pSI->m_n81Flags |= nMask; // set flag
3133 else
3134 pSI->m_n81Flags &= ~nMask; // delete flag
3135 }
3136 }
3137 else
3138 {
3139
3140 // in text -> look at flags
3141 if( *pData & 0x80 ) // bit 7 set?
3142 {
3143 if (pSI && pSI->m_n81Flags & nMask) // and in StyleDef at ?
3144 bOn = !bOn; // then invert
3145 // remember on stack that this is a toggle-attribute
3146 m_xCtrlStck->SetToggleAttr(nI, true);
3147 }
3148 }
3149
3150 SetToggleAttr( nI, bOn );
3151}
3152
3153void SwWW8ImplReader::Read_Bidi(sal_uInt16, const sal_uInt8* pData, short nLen)
3154{
3155 if (nLen < 1) //Property end
3156 {
3157 m_bBidi = false;
3159 }
3160 else //Property start
3161 {
3162 m_bBidi = true;
3163 sal_uInt8 nBidi = *pData;
3164 NewAttr( SfxInt16Item( RES_CHRATR_BIDIRTL, (nBidi!=0)? 1 : 0 ) );
3165 }
3166}
3167
3168/*
3169 tdf#91916, #i8726, #i42685# there is an ambiguity
3170 around certain properties as to what they mean,
3171 which appears to be a problem with different versions
3172 of the file format where properties conflict, i.e.
3173
3174ooo40606-2.doc, magic is a699
3175 : 0x6f 0x4 0x0 0x71 0x4 0x0
3176ooo40635-1.doc, magic is a699
3177 : 0x6f 0x4 0x0 0x71 0x4 0x0
3178ooo31093/SIMPCHIN.doc, magic is a699
3179 : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
3180 : 0x6f 0x5 0x0 0x70 0x5 0x0
3181ooo31093/TRADCHIN.doc, magic is a699
3182 : 0x6f 0x1 0x0 0x70 0x0 0x0 0x71 0x1 0x0
3183ooo31093/JAPANESE.doc, magic is a697
3184 : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
3185ooo31093/KOREAN.doc, magic is a698
3186 : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
3187ooo31093-1.doc, magic is a698
3188 : 0x6f 0x5 0x0 0x70 0x5 0x0
3189ooo31093-1.doc, magic is a698
3190 : 0x6f 0x5 0x0 0x70 0x5 0x0
3191
3192meanwhile...
3193
3194ooo27954-1.doc, magic is a5dc
3195 : 0x6f 0x1 0x81 0x71 0x2 0x4 0x0 0x74 0x2 0x20 0x0
3196
3197ooo33251-1.doc, magic is a5dc
3198 : 0x6f 0x1 0x81 0x71 0x2 0x3 0x0 0x74 0x2 0x1c 0x0
3199
3200---
3201
3202So we have the same sprm values, but different payloads, where
3203the a5dc versions appear to use a len argument, followed by len
3204bytes, while the a698<->a699 versions use a 2byte argument
3205
3206commit c2213db9ed70c1fd546482d22e36e4029c10aa45
3207
3208 INTEGRATION: CWS tl28 (1.169.24); FILE MERGED
3209 2006/10/25 13:40:41 tl 1.169.24.2: RESYNC: (1.169-1.170); FILE MERGED
3210 2006/09/20 11:55:50 hbrinkm 1.169.24.1: #i42685# applied patch
3211
3212changed 0x6f and 0x70 from Read_BoldBiDiUsw to Read_FontCode for all versions.
3213
3214In the Word for Window 2 spec we have...
3215 78 //sprmCMajority
3216 80 //sprmCFBoldBi
3217 81 //sprmCFItalicBi
3218 82 //sprmCFtcBi
3219 83 //sprmClidBi
3220 84 //sprmCIcoBi
3221 85 //sprmCHpsBi
3222as see in GetWW2SprmDispatcher, different numbers, but the sequence starts with
3223the same sprmCMajority as appears before 0x6f in word 6/95
3224
3225I think the easiest explanation is that the CJK Word for Window 95, or whatever
3226the product was went rogue, and did their own things with at least first three
3227slots after sprmCMajority to do a different thing. I have no reason to think Tono
3228was wrong with what they do in the a698<->a699 versions, but with magic
3229a5dc they probably did mean sprmCFBoldBi, sprmCFItalicBi cause they have that 0x81
3230pattern which has significance for those types of properties.
3231*/
3232void SwWW8ImplReader::Read_AmbiguousSPRM(sal_uInt16 nId, const sal_uInt8* pData,
3233 short nLen)
3234{
3235 if (m_xWwFib->m_wIdent >= 0xa697 && m_xWwFib->m_wIdent <= 0xa699)
3236 {
3237 Read_FontCode(nId, pData, nLen);
3238 }
3239 else
3240 {
3241 Read_BoldBiDiUsw(nId, pData, nLen);
3242 }
3243}
3244
3245// Read_BoldUsw for BiDi Italic, Bold
3246void SwWW8ImplReader::Read_BoldBiDiUsw(sal_uInt16 nId, const sal_uInt8* pData,
3247 short nLen)
3248{
3249 static const sal_uInt16 nEndIds[2] =
3250 {
3252 };
3253
3254 sal_uInt8 nI;
3255 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
3256 if (eVersion <= ww::eWW2)
3257 nI = static_cast< sal_uInt8 >(nId - 80);
3258 else if (eVersion < ww::eWW8)
3259 nI = static_cast< sal_uInt8 >(nId - 111);
3260 else
3261 nI = static_cast< sal_uInt8 >(nId - NS_sprm::CFBoldBi::val);
3262
3263 OSL_ENSURE(nI <= 1, "not happening");
3264 if (nI > 1)
3265 return;
3266
3267 sal_uInt16 nMask = 1 << nI;
3268
3269 if (nLen < 1)
3270 {
3271 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),nEndIds[nI]);
3272 m_xCtrlStck->SetToggleBiDiAttr(nI, false);
3273 return;
3274 }
3275 bool bOn = *pData & 1;
3277 if (m_xPlcxMan)
3278 {
3279 SprmResult aCharIstd =
3281 if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2)
3282 pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm));
3283 }
3284
3285 if (m_pCurrentColl && eVersion > ww::eWW2) // StyleDef -> remember flags
3286 {
3287 if (pSI)
3288 {
3289 if( pSI->m_nBase < m_vColl.size() // Style Based on
3290 && ( *pData & 0x80 ) // bit 7 set?
3291 && ( m_vColl[pSI->m_nBase].m_n81BiDiFlags & nMask ) ) // base mask?
3292 bOn = !bOn; // invert
3293
3294 if( bOn )
3295 pSI->m_n81BiDiFlags |= nMask; // set flag
3296 else
3297 pSI->m_n81BiDiFlags &= ~nMask; // delete flag
3298 }
3299 }
3300 else
3301 {
3302
3303 // in text -> look at flags
3304 if (*pData & 0x80) // Bit 7 set?
3305 {
3306 if (pSI && pSI->m_n81BiDiFlags & nMask) // and in StyleDef at ?
3307 bOn = !bOn; // then invert
3308 // remember on stack that this is a toggle-attribute
3309 m_xCtrlStck->SetToggleBiDiAttr(nI, true);
3310 }
3311 }
3312
3313 SetToggleBiDiAttr(nI, bOn);
3314}
3315
3317{
3318 switch (nAttrId)
3319 {
3320 case 0:
3321 {
3323 aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT );
3324 NewAttr( aAttr );
3325 }
3326 break;
3327 case 1:
3328 {
3330 aAttr.SetWhich( RES_CHRATR_CTL_POSTURE );
3331 NewAttr( aAttr );
3332 }
3333 break;
3334 default:
3335 OSL_ENSURE(false, "Unhandled unknown bidi toggle attribute");
3336 break;
3337
3338 }
3339}
3340
3342{
3343 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
3344
3345 switch (nAttrId)
3346 {
3347 case 0:
3348 {
3350 NewAttr( aAttr );
3351 aAttr.SetWhich( RES_CHRATR_CJK_WEIGHT );
3352 NewAttr( aAttr );
3353 if (eVersion <= ww::eWW6)
3354 {
3355 aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT );
3356 NewAttr( aAttr );
3357 }
3358 }
3359 break;
3360 case 1:
3361 {
3363 NewAttr( aAttr );
3364 aAttr.SetWhich( RES_CHRATR_CJK_POSTURE );
3365 NewAttr( aAttr );
3366 if (eVersion <= ww::eWW6)
3367 {
3368 aAttr.SetWhich( RES_CHRATR_CTL_POSTURE );
3369 NewAttr( aAttr );
3370 }
3371 }
3372 break;
3373 case 2:
3375 break;
3376 case 3:
3378 break;
3379 case 4:
3381 break;
3382 case 5:
3383 NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::SmallCaps
3384 : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) );
3385 break;
3386 case 6:
3387 NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::Uppercase
3388 : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) );
3389 break;
3390 case 7:
3392 {
3393 // We just inserted a frame and we're at the next paragraph start.
3394 SwFrameFormat* pFormat = m_xFormatOfJustInsertedApo->GetFormat();
3395 if (pFormat)
3396 {
3397 SwNode* pAnchorNode = pFormat->GetAnchor().GetAnchorNode();
3398 if (pAnchorNode && *pAnchorNode == m_pPaM->GetPoint()->GetNode())
3399 {
3400 // The anchor paragraph would be hidden, leading to hiding the frame as
3401 // well, prevent that.
3402 break;
3403 }
3404 }
3405 }
3406
3408 break;
3409 case 8:
3412 break;
3413 default:
3414 OSL_ENSURE(false, "Unhandled unknown toggle attribute");
3415 break;
3416 }
3417}
3418
3419void SwWW8ImplReader::ChkToggleAttr_( sal_uInt16 nOldStyle81Mask,
3420 sal_uInt16 nNewStyle81Mask )
3421{
3422 sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleAttrFlags();
3423 for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1)
3424 {
3425 if (
3426 (i & nToggleAttrFlags) &&
3427 ((i & nOldStyle81Mask) != (i & nNewStyle81Mask))
3428 )
3429 {
3430 SetToggleAttr(n, (i & nOldStyle81Mask));
3431 }
3432 }
3433}
3434
3435void SwWW8ImplReader::ChkToggleBiDiAttr_( sal_uInt16 nOldStyle81Mask,
3436 sal_uInt16 nNewStyle81Mask )
3437{
3438 sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleBiDiAttrFlags();
3439 for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1)
3440 {
3441 if (
3442 (i & nToggleAttrFlags) &&
3443 ((i & nOldStyle81Mask) != (i & nNewStyle81Mask))
3444 )
3445 {
3446 SetToggleBiDiAttr(n, (i & nOldStyle81Mask));
3447 }
3448 }
3449}
3450
3451void SwWW8ImplReader::Read_SubSuper( sal_uInt16, const sal_uInt8* pData, short nLen )
3452{
3453 if (nLen < 1)
3454 {
3456 return;
3457 }
3458
3459 short nEs;
3460 sal_uInt8 nProp;
3461 switch( *pData )
3462 {
3463 case 1:
3464 nEs = DFLT_ESC_AUTO_SUPER;
3465 nProp = DFLT_ESC_PROP;
3466 break;
3467 case 2:
3468 nEs = DFLT_ESC_AUTO_SUB;
3469 nProp = DFLT_ESC_PROP;
3470 break;
3471 default:
3472 nEs = 0;
3473 nProp = 100;
3474 break;
3475 }
3477}
3478
3480{
3481 /*
3482 For inline graphics and objects word has a hacked in feature to use
3483 subscripting to force the graphic into a centered position on the line, so
3484 we must check when applying sub/super to see if it the subscript range
3485 contains only a single graphic, and if that graphic is anchored as
3486 RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line.
3487 */
3488 SwFrameFormat *pRet=nullptr;
3489 SwNodeIndex aBegin(rRegion.Start()->GetNode());
3490 const sal_Int32 nBegin(rRegion.Start()->GetContentIndex());
3491 SwNodeIndex aEnd(rRegion.End()->GetNode());
3492 const sal_Int32 nEnd(rRegion.End()->GetContentIndex());
3493 const SwTextNode* pTNd;
3494 const SwTextAttr* pTFlyAttr;
3495 if (
3496 aBegin == aEnd && nBegin == nEnd - 1 &&
3497 nullptr != (pTNd = aBegin.GetNode().GetTextNode()) &&
3498 nullptr != (pTFlyAttr = pTNd->GetTextAttrForCharAt(nBegin, RES_TXTATR_FLYCNT))
3499 )
3500 {
3501 const SwFormatFlyCnt& rFly = pTFlyAttr->GetFlyCnt();
3502 SwFrameFormat *pFlyFormat = rFly.GetFrameFormat();
3503 if (pFlyFormat &&
3504 (RndStdIds::FLY_AS_CHAR == pFlyFormat->GetAnchor().GetAnchorId()))
3505 {
3506 pRet = pFlyFormat;
3507 }
3508 }
3509 return pRet;
3510}
3511
3513{
3514 /*
3515 For inline graphics and objects word has a hacked in feature to use
3516 subscripting to force the graphic into a centered position on the line, so
3517 we must check when applying sub/super to see if it the subscript range
3518 contains only a single graphic, and if that graphic is anchored as
3519 RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line.
3520 */
3521 bool bIsGraphicPlacementHack = false;
3522 sal_uInt16 nPos;
3523 if (m_xCtrlStck->GetFormatStackAttr(RES_CHRATR_ESCAPEMENT, &nPos))
3524 {
3525 SwPaM aRegion(*m_pPaM->GetPoint());
3526
3527 SwFltPosition aMkPos((*m_xCtrlStck)[nPos].m_aMkPos);
3528 SwFltPosition aPtPos(*m_pPaM->GetPoint());
3529
3530 SwFrameFormat *pFlyFormat = nullptr;
3532 && nullptr != (pFlyFormat = ContainsSingleInlineGraphic(aRegion)))
3533 {
3534 m_xCtrlStck->DeleteAndDestroy(nPos);
3535 pFlyFormat->SetFormatAttr(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER, text::RelOrientation::CHAR));
3536 bIsGraphicPlacementHack = true;
3537 }
3538 }
3539 return bIsGraphicPlacementHack;
3540}
3541
3542void SwWW8ImplReader::Read_SubSuperProp( sal_uInt16, const sal_uInt8* pData, short nLen )
3543{
3544 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
3545
3546 if (nLen < (eVersion <= ww::eWW2 ? 1 : 2))
3547 {
3550 return;
3551 }
3552
3553 // if the fontsize for these characters is specified, make sure it is updated first
3554 if ( m_xPlcxMan )
3555 {
3556 const sal_uInt16 nFontsizeID = m_bVer67 ? NS_sprm::v6::sprmCHps : NS_sprm::CHps::val;
3557 const SprmResult aFontsize = m_xPlcxMan->GetChpPLCF()->HasSprm( nFontsizeID, /*bFindFirst=*/false );
3558 if ( aFontsize.pSprm && aFontsize.nRemainingData )
3559 Read_FontSize(nFontsizeID, aFontsize.pSprm, aFontsize.nRemainingData);
3560 }
3561
3562 // font position in HalfPoints
3563 short nPos = eVersion <= ww::eWW2 ? static_cast< sal_Int8 >( *pData ) : SVBT16ToInt16( pData );
3564 sal_Int32 nPos2 = nPos * ( 10 * 100 ); // HalfPoints in 100 * tw
3565 const SvxFontHeightItem* pF
3566 = static_cast<const SvxFontHeightItem*>(GetFormatAttr(RES_CHRATR_FONTSIZE));
3567 OSL_ENSURE(pF, "Expected to have the fontheight available here");
3568
3569 // #i59022: Check ensure nHeight != 0. Div by zero otherwise.
3570 sal_Int32 nHeight = 240;
3571 if (pF != nullptr && pF->GetHeight() != 0)
3572 nHeight = pF->GetHeight();
3573 nPos2 /= nHeight; // ... now in % (rounded)
3574 if( nPos2 > MAX_ESC_POS )
3575 nPos2 = MAX_ESC_POS;
3576 if( nPos2 < -MAX_ESC_POS )
3577 nPos2 = -MAX_ESC_POS;
3578 SvxEscapementItem aEs( static_cast<short>(nPos2), 100, RES_CHRATR_ESCAPEMENT );
3579 NewAttr( aEs );
3580}
3581
3582void SwWW8ImplReader::Read_Underline( sal_uInt16, const sal_uInt8* pData, short nLen )
3583{
3584 FontLineStyle eUnderline = LINESTYLE_NONE;
3585 bool bWordLine = false;
3586 if (pData && nLen)
3587 {
3588 // Parameter: 0 = none, 1 = single, 2 = by Word,
3589 // 3 = double, 4 = dotted, 5 = hidden
3590 // 6 = thick, 7 = dash, 8 = dot(not used)
3591 // 9 = dotdash 10 = dotdotdash 11 = wave
3592 switch( *pData )
3593 {
3594 case 2: bWordLine = true;
3595 [[fallthrough]];
3596 case 1: eUnderline = LINESTYLE_SINGLE; break;
3597 case 3: eUnderline = LINESTYLE_DOUBLE; break;
3598 case 4: eUnderline = LINESTYLE_DOTTED; break;
3599 case 7: eUnderline = LINESTYLE_DASH; break;
3600 case 9: eUnderline = LINESTYLE_DASHDOT; break;
3601 case 10:eUnderline = LINESTYLE_DASHDOTDOT; break;
3602 case 6: eUnderline = LINESTYLE_BOLD; break;
3603 case 11:eUnderline = LINESTYLE_WAVE; break;
3604 case 20:eUnderline = LINESTYLE_BOLDDOTTED; break;
3605 case 23:eUnderline = LINESTYLE_BOLDDASH; break;
3606 case 39:eUnderline = LINESTYLE_LONGDASH; break;
3607 case 55:eUnderline = LINESTYLE_BOLDLONGDASH; break;
3608 case 25:eUnderline = LINESTYLE_BOLDDASHDOT; break;
3609 case 26:eUnderline = LINESTYLE_BOLDDASHDOTDOT;break;
3610 case 27:eUnderline = LINESTYLE_BOLDWAVE; break;
3611 case 43:eUnderline = LINESTYLE_DOUBLEWAVE; break;
3612 }
3613 }
3614
3615 // if necessary, mix up stack and exit!
3616 if (nLen < 1)
3617 {
3620 }
3621 else
3622 {
3624 if( bWordLine )
3626 }
3627}
3628
3629/*
3630//The last three vary, measurements, rotation ? ?
3631NoBracket 78 CA 06 - 02 00 00 02 34 52
3632() 78 CA 06 - 02 01 00 02 34 52
3633[] 78 CA 06 - 02 02 00 02 34 52
3634<> 78 CA 06 - 02 03 00 02 34 52
3635{} 78 CA 06 - 02 04 00 02 34 52
3636*/
3638 short nLen )
3639{
3640 if (nLen < 0) // close the tag
3641 {
3644 }
3645 else if( pData && 6 == nLen )
3646 {
3647 switch( *pData )
3648 {
3649 case 2: // double line
3650 {
3651 sal_Unicode cStt = 0, cEnd = 0;
3652 switch( SVBT16ToUInt16( pData+1 ) )
3653 {
3654 case 1: cStt = '('; cEnd = ')'; break;
3655 case 2: cStt = '['; cEnd = ']'; break;
3656 case 3: cStt = '<'; cEnd = '>'; break;
3657 case 4: cStt = '{'; cEnd = '}'; break;
3658 }
3659 NewAttr( SvxTwoLinesItem( true, cStt, cEnd, RES_CHRATR_TWO_LINES ));
3660 }
3661 break;
3662
3663 case 1: // rotated characters
3664 {
3665 bool bFitToLine = 0 != *(pData+1);
3666 NewAttr( SvxCharRotateItem( 900_deg10, bFitToLine, RES_CHRATR_ROTATE ));
3667 }
3668 break;
3669 }
3670 }
3671}
3672
3673void SwWW8ImplReader::Read_TextColor( sal_uInt16, const sal_uInt8* pData, short nLen )
3674{
3675 //Has newer colour variant, ignore this old variant
3676 if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CCv::val).pSprm)
3677 return;
3678
3679 if (nLen < 1)
3681 else
3682 {
3683 sal_uInt8 b = *pData; // parameter: 0 = Auto, 1..16 colors
3684
3685 if( b > 16 ) // unknown -> Black
3686 b = 0;
3687
3690 m_xStyles->mbTextColChanged = true;
3691 }
3692}
3693
3694void SwWW8ImplReader::Read_TextForeColor(sal_uInt16, const sal_uInt8* pData, short nLen)
3695{
3696 if (nLen < 4)
3698 else
3699 {
3700 Color aColor = msfilter::util::BGRToRGB(SVBT32ToUInt32(pData));
3701
3702 // At least when transparency is 0xff and the color is black, Word renders that as black.
3703 if (aColor.IsTransparent() && aColor != COL_AUTO)
3704 {
3705 aColor.SetAlpha(255);
3706 }
3707
3710 m_xStyles->mbTextColChanged = true;
3711 }
3712}
3713
3714void SwWW8ImplReader::Read_UnderlineColor(sal_uInt16, const sal_uInt8* pData, short nLen)
3715{
3716 if (nLen < 0)
3717 {
3718 //because the UnderlineColor is not a standalone attribute in SW, it belongs to the underline attribute.
3719 //And, the .doc file stores attributes separately, this attribute ends here, the "underline"
3720 //attribute also terminates (if the character next owns underline, that will be a new underline attribute).
3721 //so nothing is left to be done here.
3722 return;
3723 }
3724 else
3725 {
3726 if ( m_pCurrentColl ) //importing style
3727 {
3728 if( SfxItemState::SET == m_pCurrentColl->GetItemState( RES_CHRATR_UNDERLINE, false ) )
3729 {
3730 if (nLen >= 4)
3731 {
3732 const SwAttrSet& aSet = m_pCurrentColl->GetAttrSet();
3733 std::unique_ptr<SvxUnderlineItem> pUnderline(aSet.Get(RES_CHRATR_UNDERLINE, false).Clone());
3734 pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) );
3735 m_pCurrentColl->SetFormatAttr( *pUnderline );
3736 }
3737 }
3738 }
3739 else if (m_xCurrentItemSet)
3740 {
3741 if ( SfxItemState::SET == m_xCurrentItemSet->GetItemState( RES_CHRATR_UNDERLINE, false ) )
3742 {
3743 if (nLen >= 4)
3744 {
3745 std::unique_ptr<SvxUnderlineItem> pUnderline(m_xCurrentItemSet->Get(RES_CHRATR_UNDERLINE, false).Clone());
3746 pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) );
3747 m_xCurrentItemSet->Put( std::move(pUnderline) );
3748 }
3749 }
3750 }
3751 else
3752 {
3753 SvxUnderlineItem* pUnderlineAttr = const_cast<SvxUnderlineItem*>(static_cast<const SvxUnderlineItem*>(m_xCtrlStck->GetOpenStackAttr( *m_pPaM->GetPoint(), RES_CHRATR_UNDERLINE )));
3754 if (pUnderlineAttr && nLen >= 4)
3755 pUnderlineAttr->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32( pData ) ));
3756 }
3757 }
3758}
3759bool SwWW8ImplReader::GetFontParams( sal_uInt16 nFCode, FontFamily& reFamily,
3760 OUString& rName, FontPitch& rePitch, rtl_TextEncoding& reCharSet )
3761{
3762 // the definitions that are the base for these tables are in windows.h
3763 static const FontPitch ePitchA[] =
3764 {
3766 };
3767
3768 static const FontFamily eFamilyA[] =
3769 {
3772 };
3773
3774 const WW8_FFN* pF = m_xFonts->GetFont( nFCode ); // Info for it
3775 if( !pF ) // font number unknown ?
3776 return false; // then ignore
3777
3778 rName = pF->sFontname;
3779
3780 // pF->prg : Pitch
3781 rePitch = ePitchA[pF->aFFNBase.prg];
3782
3783 // pF->chs: Charset
3784 if( 77 == pF->aFFNBase.chs ) // Mac font in Mac Charset or
3785 reCharSet = m_eTextCharSet; // translated to ANSI charset
3786 else
3787 {
3788 // #i52786#, for word 67 we'll assume that ANSI is basically invalid,
3789 // might be true for (above) mac as well, but would need a mac example
3790 // that exercises this to be sure
3791 if (m_bVer67 && pF->aFFNBase.chs == 0)
3792 reCharSet = RTL_TEXTENCODING_DONTKNOW;
3793 else
3794 reCharSet = rtl_getTextEncodingFromWindowsCharset(pF->aFFNBase.chs);
3795 }
3796
3797 // make sure Font Family Code is set correctly
3798 // at least for the most important fonts
3799 // ( might be set wrong when Doc was not created by
3800 // Winword but by third party program like Applixware... )
3801 if (rName.startsWithIgnoreAsciiCase("Tms Rmn") ||
3802 rName.startsWithIgnoreAsciiCase("Timmons") ||
3803 rName.startsWithIgnoreAsciiCase("CG Times") ||
3804 rName.startsWithIgnoreAsciiCase("MS Serif") ||
3805 rName.startsWithIgnoreAsciiCase("Garamond") ||
3806 rName.startsWithIgnoreAsciiCase("Times Roman") ||
3807 rName.startsWithIgnoreAsciiCase("Times New Roman"))
3808 {
3809 reFamily = FAMILY_ROMAN;
3810 }
3811 else if (rName.startsWithIgnoreAsciiCase("Helv") ||
3812 rName.startsWithIgnoreAsciiCase("Arial") ||
3813 rName.startsWithIgnoreAsciiCase("Univers") ||
3814 rName.startsWithIgnoreAsciiCase("LinePrinter") ||
3815 rName.startsWithIgnoreAsciiCase("Lucida Sans") ||
3816 rName.startsWithIgnoreAsciiCase("Small Fonts") ||
3817 rName.startsWithIgnoreAsciiCase("MS Sans Serif"))
3818 {
3819 reFamily = FAMILY_SWISS;
3820 }
3821 else
3822 {
3823 reFamily = eFamilyA[pF->aFFNBase.ff];
3824 }
3825
3826 return true;
3827}
3828
3829bool SwWW8ImplReader::SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums,
3830 sal_uInt16 nWhich)
3831{
3832 FontFamily eFamily;
3833 OUString aName;
3834 FontPitch ePitch;
3835 rtl_TextEncoding eSrcCharSet;
3836
3837 if( !GetFontParams( nFCode, eFamily, aName, ePitch, eSrcCharSet ) )
3838 {
3839 //If we fail (and are not doing a style) then put something into the
3840 //character encodings stack anyway so that the property end that pops
3841 //off the stack will keep in sync
3843 {
3844 if (nWhich == RES_CHRATR_CJK_FONT)
3845 {
3846 if (!m_aFontSrcCJKCharSets.empty())
3847 {
3848 eSrcCharSet = m_aFontSrcCJKCharSets.top();
3849 }
3850 else
3851 {
3852 eSrcCharSet = RTL_TEXTENCODING_DONTKNOW;
3853 }
3854
3855 m_aFontSrcCJKCharSets.push(eSrcCharSet);
3856 }
3857 else
3858 {
3859 if (!m_aFontSrcCharSets.empty())
3860 {
3861 eSrcCharSet = m_aFontSrcCharSets.top();
3862 }
3863 else
3864 {
3865 eSrcCharSet = RTL_TEXTENCODING_DONTKNOW;
3866 }
3867
3868 m_aFontSrcCharSets.push(eSrcCharSet);
3869 }
3870 }
3871 return false;
3872 }
3873
3874 rtl_TextEncoding eDstCharSet = eSrcCharSet;
3875
3876 SvxFontItem aFont( eFamily, aName, OUString(), ePitch, eDstCharSet, nWhich);
3877
3878 if( bSetEnums )
3879 {
3880 if( m_pCurrentColl && m_nCurrentColl < m_vColl.size() ) // StyleDef
3881 {
3882 switch(nWhich)
3883 {
3884 default:
3885 case RES_CHRATR_FONT:
3886 m_vColl[m_nCurrentColl].m_eLTRFontSrcCharSet = eSrcCharSet;
3887 break;
3889 m_vColl[m_nCurrentColl].m_eRTLFontSrcCharSet = eSrcCharSet;
3890 break;
3892 m_vColl[m_nCurrentColl].m_eCJKFontSrcCharSet = eSrcCharSet;
3893 break;
3894 }
3895 }
3896 else if (IsListOrDropcap())
3897 {
3898 //Add character text encoding to stack
3899 if (nWhich == RES_CHRATR_CJK_FONT)
3900 m_aFontSrcCJKCharSets.push(eSrcCharSet);
3901 else
3902 m_aFontSrcCharSets.push(eSrcCharSet);
3903 }
3904 }
3905
3906 NewAttr( aFont ); // ...and insert
3907
3908 return true;
3909}
3910
3912{
3913 OSL_ENSURE(!m_aFontSrcCharSets.empty(),"no charset to remove");
3914 if (!m_aFontSrcCharSets.empty())
3915 m_aFontSrcCharSets.pop();
3916}
3917
3919{
3920 OSL_ENSURE(!m_aFontSrcCJKCharSets.empty(),"no charset to remove");
3921 if (!m_aFontSrcCJKCharSets.empty())
3923}
3924
3925void SwWW8ImplReader::openFont(sal_uInt16 nFCode, sal_uInt16 nId)
3926{
3927 if (SetNewFontAttr(nFCode, true, nId) && m_pCurrentColl && m_xStyles)
3928 {
3929 // remember for simulating default font
3930 if (RES_CHRATR_CJK_FONT == nId)
3931 m_xStyles->mbCJKFontChanged = true;
3932 else if (RES_CHRATR_CTL_FONT == nId)
3933 m_xStyles->mbCTLFontChanged = true;
3934 else
3935 m_xStyles->mbFontChanged = true;
3936 }
3937}
3938
3939void SwWW8ImplReader::closeFont(sal_uInt16 nId)
3940{
3941 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
3942 if (nId == RES_CHRATR_CJK_FONT)
3944 else
3946}
3947
3948/*
3949 Turn font on or off:
3950*/
3951void SwWW8ImplReader::Read_FontCode( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
3952{
3953 //Note: this function needs to be able to run multiple times on the same data.
3954 //It is called by Read_SubSuperProp to ensure that the current fontsize is known.
3955
3956 if (m_bSymbol) // if bSymbol, the symbol's font
3957 return;
3958
3959// (see sprmCSymbol) is valid!
3960 switch( nId )
3961 {
3962 case 113: //WW7
3963 case NS_sprm::CRgFtc2::val: //"Other" font, override with BiDi if it exists
3964 case NS_sprm::CFtcBi::val: //BiDi Font
3966 break;
3967 case NS_sprm::v6::sprmCFtc: //WW6
3968 case 111: //WW7
3971 break;
3972 case 112: //WW7
3975 break;
3976 default:
3977 return ;
3978 }
3979
3980 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
3981
3982 if (nLen < 2) // end of attribute
3983 {
3984 if (eVersion <= ww::eWW6)
3985 {
3988 }
3989 closeFont(nId);
3990 }
3991 else
3992 {
3993 sal_uInt16 nFCode = SVBT16ToUInt16( pData ); // font number
3994 openFont(nFCode, nId);
3995 if (eVersion <= ww::eWW6)
3996 {
3999 }
4000 }
4001}
4002
4003void SwWW8ImplReader::Read_FontSize( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4004{
4005 switch( nId )
4006 {
4007 case 74: // WW2
4009 case NS_sprm::CHps::val:
4011 break;
4012 case 85: //WW2
4013 case 116: //WW7
4016 break;
4017 default:
4018 return ;
4019 }
4020
4021 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
4022
4023 if (nLen < (eVersion <= ww::eWW2 ? 1 : 2)) // end of attribute
4024 {
4025 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
4026 if (eVersion <= ww::eWW6) // reset additionally the CTL size
4028 if (RES_CHRATR_FONTSIZE == nId) // reset additionally the CJK size
4030 }
4031 else
4032 {
4033 // Font-Size in half points e.g. 10 = 1440 / ( 72 * 2 )
4034 sal_uLong nFSize = eVersion <= ww::eWW2 ? *pData : SVBT16ToUInt16(pData);
4035 nFSize*= 10;
4036
4037 SvxFontHeightItem aSz( nFSize, 100, nId );
4038 NewAttr( aSz );
4039 if (RES_CHRATR_FONTSIZE == nId) // set additionally the CJK size
4040 {
4041 aSz.SetWhich( RES_CHRATR_CJK_FONTSIZE );
4042 NewAttr( aSz );
4043 }
4044 if (eVersion <= ww::eWW6) // set additionally the CTL size
4045 {
4046 aSz.SetWhich( RES_CHRATR_CTL_FONTSIZE );
4047 NewAttr( aSz );
4048 }
4049 if (m_pCurrentColl && m_xStyles) // Style-Def ?
4050 {
4051 // remember for simulating default font size
4053 m_xStyles->mbFCTLSizeChanged = true;
4054 else
4055 {
4056 m_xStyles->mbFSizeChanged = true;
4057 if (eVersion <= ww::eWW6)
4058 m_xStyles->mbFCTLSizeChanged= true;
4059 }
4060 }
4061 }
4062}
4063
4064void SwWW8ImplReader::Read_CharSet(sal_uInt16 , const sal_uInt8* pData, short nLen)
4065{
4066 if (nLen < 1)
4067 { // end of attribute
4068 m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW;
4069 return;
4070 }
4071 sal_uInt8 nfChsDiff = *pData;
4072
4073 if (nfChsDiff && nLen >= 2)
4074 m_eHardCharSet = rtl_getTextEncodingFromWindowsCharset( *(pData + 1) );
4075 else
4076 m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW;
4077}
4078
4079void SwWW8ImplReader::Read_Language( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4080{
4081 switch( nId )
4082 {
4087 break;
4091 break;
4092 case 83: // WW2
4093 case 114: // WW7
4096 break;
4097 default:
4098 return;
4099 }
4100
4101 if (nLen < 2) // end of attribute
4102 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
4103 else
4104 {
4105 sal_uInt16 nLang = SVBT16ToUInt16( pData ); // Language-Id
4107 }
4108}
4109
4110/*
4111 Turn on character style:
4112*/
4113void SwWW8ImplReader::Read_CColl( sal_uInt16, const sal_uInt8* pData, short nLen )
4114{
4115 if (nLen < 2) // end of attribute
4116 {
4118 m_nCharFormat = -1;
4119 return;
4120 }
4121 sal_uInt16 nId = SVBT16ToUInt16( pData ); // Style-Id (NOT Sprm-Id!)
4122
4123 if( nId >= m_vColl.size() || !m_vColl[nId].m_pFormat // invalid Id?
4124 || m_vColl[nId].m_bColl ) // or paragraph style?
4125 return; // then ignore
4126
4127 // if current on loading a TOX field, and current trying to apply a hyperlink character style,
4128 // just ignore. For the hyperlinks inside TOX in MS Word is not same with a common hyperlink
4129 // Character styles: without underline and blue font color. And such type style will be applied in others
4130 // processes.
4131 if (m_bLoadingTOXCache && m_vColl[nId].GetWWStyleId() == ww::stiHyperlink)
4132 {
4133 return;
4134 }
4135
4136 NewAttr( SwFormatCharFormat( static_cast<SwCharFormat*>(m_vColl[nId].m_pFormat) ) );
4137 m_nCharFormat = static_cast<short>(nId);
4138}
4139
4140/*
4141 Narrower or wider than normal:
4142*/
4143void SwWW8ImplReader::Read_Kern( sal_uInt16, const sal_uInt8* pData, short nLen )
4144{
4145 if (nLen < 2) // end of attribute
4146 {
4148 return;
4149 }
4150 sal_Int16 nKern = SVBT16ToUInt16( pData ); // Kerning in Twips
4152}
4153
4154void SwWW8ImplReader::Read_FontKern( sal_uInt16, const sal_uInt8* pData, short nLen )
4155{
4156 if (nLen < 2) // end of attribute
4157 {
4159 return;
4160 }
4161 sal_Int16 nAutoKern = SVBT16ToUInt16( pData ); // Kerning in Twips
4162 NewAttr(SvxAutoKernItem(static_cast<bool>(nAutoKern), RES_CHRATR_AUTOKERN));
4163}
4164
4165void SwWW8ImplReader::Read_CharShadow( sal_uInt16, const sal_uInt8* pData, short nLen )
4166{
4167 //Has newer colour variant, ignore this old variant
4168 if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CShd::val).pSprm)
4169 return;
4170
4171 if (nLen < 2)
4172 {
4174 }
4175 else
4176 {
4177 WW8_SHD aSHD;
4178 aSHD.SetWWValue( *reinterpret_cast<SVBT16 const *>(pData) );
4179 SwWW8Shade aSh( m_bVer67, aSHD );
4180
4182
4183 // Add a marker to the grabbag indicating that character background was imported from MSO shading
4184 SfxGrabBagItem aGrabBag = *static_cast<const SfxGrabBagItem*>(GetFormatAttr(RES_CHRATR_GRABBAG));
4185 std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
4186 rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::Any(true)));
4187 NewAttr(aGrabBag);
4188 }
4189}
4190
4191void SwWW8ImplReader::Read_TextBackColor(sal_uInt16, const sal_uInt8* pData, short nLen )
4192{
4193 if (nLen <= 0)
4194 {
4196 }
4197 else
4198 {
4199 OSL_ENSURE(nLen == 10, "Len of para back colour not 10!");
4200 if (nLen != 10)
4201 return;
4202 Color aColour(ExtractColour(pData, m_bVer67));
4204
4205 // Add a marker to the grabbag indicating that character background was imported from MSO shading
4206 SfxGrabBagItem aGrabBag = *static_cast<const SfxGrabBagItem*>(GetFormatAttr(RES_CHRATR_GRABBAG));
4207 std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
4208 rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::Any(true)));
4209 NewAttr(aGrabBag);
4210 }
4211}
4212
4213void SwWW8ImplReader::Read_CharHighlight(sal_uInt16, const sal_uInt8* pData, short nLen)
4214{
4215 // MS Word completely ignores character highlighting in character styles.
4217 return;
4218
4219 if (nLen < 1)
4220 {
4222 }
4223 else
4224 {
4225 sal_uInt8 b = *pData; // Parameter: 0 = Auto, 1..16 colors
4226
4227 if( b > 16 ) // invalid -> Black
4228 b = 0; // Auto -> Black
4229
4230 Color aCol(GetCol(b));
4232 }
4233}
4234
4235void SwWW8ImplReader::Read_NoLineNumb(sal_uInt16 , const sal_uInt8* pData, short nLen)
4236{
4237 if (nLen < 0) // end of attribute
4238 {
4239 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_LINENUMBER );
4240 return;
4241 }
4243 if (const SwFormatLineNumber* pLN
4244 = static_cast<const SwFormatLineNumber*>(GetFormatAttr(RES_LINENUMBER)))
4245 {
4246 aLN.SetStartValue( pLN->GetStartValue() );
4247 }
4248
4249 aLN.SetCountLines(pData && nLen >= 1 && (0 == *pData));
4250 NewAttr( aLN );
4251}
4252
4253static bool lcl_HasExplicitLeft(const WW8PLCFMan *pPlcxMan, bool bVer67)
4254{
4255 WW8PLCFx_Cp_FKP *pPap = pPlcxMan ? pPlcxMan->GetPapPLCF() : nullptr;
4256 if (pPap)
4257 {
4258 if (bVer67)
4260 else
4262 }
4263 return false;
4264}
4265
4266// Sprm 16, 17
4267void SwWW8ImplReader::Read_LR( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4268{
4269 if (nLen < 2) // end of attribute
4270 {
4274 return;
4275 }
4276
4277 short nPara = SVBT16ToUInt16( pData );
4278
4280 ::std::unique_ptr<SvxFirstLineIndentItem> pFirstLine(pItem
4281 ? static_cast<SvxFirstLineIndentItem*>(pItem->Clone())
4284 ::std::unique_ptr<SvxTextLeftMarginItem> pLeftMargin(pItem
4285 ? static_cast<SvxTextLeftMarginItem*>(pItem->Clone())
4288 ::std::unique_ptr<SvxRightMarginItem> pRightMargin(pItem
4289 ? static_cast<SvxRightMarginItem*>(pItem->Clone())
4291
4292 // Fix the regression issue: #i99822#: Discussion?
4293 // Since the list level formatting doesn't apply into paragraph style
4294 // for list levels of mode LABEL_ALIGNMENT.(see ww8par3.cxx
4295 // W8ImplReader::RegisterNumFormatOnTextNode).
4296 // Need to apply the list format to the paragraph here.
4297 SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode();
4298 if (pTextNode
4300 {
4301 SwNumRule * pNumRule = pTextNode->GetNumRule();
4302 if( pNumRule )
4303 {
4304 sal_uInt8 nLvl = static_cast< sal_uInt8 >(pTextNode->GetActualListLevel());
4305 const SwNumFormat* pFormat = pNumRule->GetNumFormat( nLvl );
4306 if ( pFormat && pFormat->GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
4307 {
4308 pLeftMargin->SetTextLeft(pFormat->GetIndentAt());
4309 pFirstLine->SetTextFirstLineOffset(static_cast<short>(pFormat->GetFirstLineIndent()));
4310 // make paragraph have hard-set indent attributes
4311 pTextNode->SetAttr(*pLeftMargin);
4312 pTextNode->SetAttr(*pFirstLine);
4313 }
4314 }
4315 }
4316
4317 /*
4318 The older word sprms mean left/right, while the new ones mean before/after.
4319 Writer now also works with before after, so when we see old left/right and
4320 we're RTL. We swap them
4321 */
4322 if (IsRightToLeft())
4323 {
4324 switch (nId)
4325 {
4326 //Left becomes after;
4329 break;
4332 break;
4333 //Right becomes before;
4336 break;
4339 break;
4340 }
4341 }
4342
4343 bool bFirstLinOfstSet( false ); // #i103711#
4344 bool bLeftIndentSet( false ); // #i105414#
4345
4346 switch (nId)
4347 {
4348 //sprmPDxaLeft
4352 pLeftMargin->SetTextLeft(nPara);
4353 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4354 {
4355 m_vColl[m_nCurrentColl].m_bListRelevantIndentSet = true;
4356 }
4357 bLeftIndentSet = true; // #i105414#
4358 break;
4359 //sprmPDxaLeft1
4363 /*
4364 As part of an attempt to break my spirit ww 8+ formats can contain
4365 ww 7- lists. If they do and the list is part of the style, then
4366 when removing the list from a paragraph of that style there
4367 appears to be a bug where the hanging indent value which the list
4368 set is still factored into the left indent of the paragraph. Its
4369 not listed in the winword dialogs, but it is clearly there. So if
4370 our style has a broken ww 7- list and we know that the list has
4371 been removed then we will factor the original list applied hanging
4372 into our calculation.
4373 */
4374 if (m_xPlcxMan && m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_bHasBrokenWW6List)
4375 {
4376 SprmResult aIsZeroed = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PIlfo::val);
4377 if (aIsZeroed.pSprm && aIsZeroed.nRemainingData >= 1 && *aIsZeroed.pSprm == 0)
4378 {
4379 const SvxFirstLineIndentItem & rFirstLine =
4380 m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE);
4381 nPara = nPara - rFirstLine.GetTextFirstLineOffset();
4382 }
4383 }
4384
4385 pFirstLine->SetTextFirstLineOffset(nPara);
4386
4387 if (!m_pCurrentColl)
4388 {
4389 if (const SwTextNode* pNode = m_pPaM->GetPointNode().GetTextNode())
4390 {
4391 if ( const SwNumFormat *pNumFormat = GetNumFormatFromTextNode(*pNode) )
4392 {
4394 {
4395 pLeftMargin->SetTextLeft(pNumFormat->GetIndentAt());
4396
4397 // If have not explicit left, set number format list tab position is doc default tab
4399 if ( pDefaultStopItem && pDefaultStopItem->Count() > 0 )
4400 const_cast<SwNumFormat*>(pNumFormat)->SetListtabPos( const_cast<SvxTabStop&>((*pDefaultStopItem)[0]).GetTabPos() );
4401 }
4402 }
4403 }
4404 }
4405 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4406 {
4407 m_vColl[m_nCurrentColl].m_bListRelevantIndentSet = true;
4408 }
4409 bFirstLinOfstSet = true; // #i103711#
4410 break;
4411 //sprmPDxaRight
4415 pRightMargin->SetRight(nPara);
4416 break;
4417 default:
4418 return;
4419 }
4420
4421 NewAttr(*pFirstLine, bFirstLinOfstSet, false); // #i103711#, #i105414#
4422 NewAttr(*pLeftMargin, false, bLeftIndentSet);
4423 NewAttr(*pRightMargin, false, false);
4424}
4425
4426// Sprm 20
4427void SwWW8ImplReader::Read_LineSpace( sal_uInt16, const sal_uInt8* pData, short nLen )
4428{
4429// comment see Read_UL()
4431 return;
4432
4433 ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
4434
4435 if (nLen < (eVersion <= ww::eWW2 ? 3 : 4))
4436 {
4438 if( !( m_nIniFlags & WW8FL_NO_IMPLPASP ) )
4439 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
4440 return;
4441 }
4442
4443 short nSpace = SVBT16ToUInt16( pData );
4444 short nMulti = (eVersion <= ww::eWW2) ? 1 : SVBT16ToUInt16( pData + 2 );
4445
4446 SvxLineSpaceRule eLnSpc;
4447 if( 0 > nSpace )
4448 {
4449 nSpace = -nSpace;
4450 eLnSpc = SvxLineSpaceRule::Fix;
4451 }
4452 else
4453 eLnSpc = SvxLineSpaceRule::Min;
4454
4455 // WW has implicit additional paragraph spacing depending on
4456 // the line spacing. It is, for "exactly", 0.8 * line spacing "before"
4457 // and 0.2 * line spacing "after".
4458 // For "at least", it is 1 * line spacing "before" and 0 * line spacing "after".
4459 // For "multiple", it is 0 "before" and min(0cm, FontSize*(nFach-1)) "after".
4460
4461 // SW also has implicit line spacing. It is, for "at least"
4462 // 1 * line spacing "before" and 0 "after".
4463 // For proportional, it is min(0cm, FontSize*(nFach-1)) both "before" and "after".
4464
4465 sal_uInt16 nSpaceTw = 0;
4466
4468
4469 if( 1 == nMulti ) // MultilineSpace ( proportional )
4470 {
4471 tools::Long n = nSpace * 10 / 24; // WW: 240 = 100%, SW: 100 = 100%
4472
4473 // here n is in [0..13653]
4474 aLSpc.SetPropLineSpace( o3tl::narrowing<sal_uInt16>(n) );
4475 const SvxFontHeightItem* pH = static_cast<const SvxFontHeightItem*>(
4477 nSpaceTw = o3tl::narrowing<sal_uInt16>( n * pH->GetHeight() / 100 );
4478 }
4479 else // Fixed / Minimum
4480 {
4481 // for negative space, the distance is "exact", otherwise "at least"
4482 nSpaceTw = o3tl::narrowing<sal_uInt16>(nSpace);
4483 aLSpc.SetLineHeight( nSpaceTw );
4484 aLSpc.SetLineSpaceRule( eLnSpc);
4485 }
4486 NewAttr( aLSpc );
4487 if (m_xSFlyPara)
4488 m_xSFlyPara->nLineSpace = nSpaceTw; // linespace for graphics APOs
4489}
4490
4491//#i18519# AutoSpace value depends on Dop fDontUseHTMLAutoSpacing setting
4492sal_uInt16 SwWW8ImplReader::GetParagraphAutoSpace(bool fDontUseHTMLAutoSpacing)
4493{
4494 if (fDontUseHTMLAutoSpacing)
4495 return 100; //Seems to be always 5points in this case
4496 else
4497 return 280; //Seems to be always 14points in this case
4498}
4499
4500void SwWW8ImplReader::Read_ParaAutoBefore(sal_uInt16, const sal_uInt8 *pData, short nLen)
4501{
4502 if (nLen < 1)
4503 {
4504 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE);
4505 return;
4506 }
4507
4508 if (*pData)
4509 {
4510 SvxULSpaceItem aUL(*static_cast<const SvxULSpaceItem*>(GetFormatAttr(RES_UL_SPACE)));
4511 aUL.SetUpper(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
4512 NewAttr(aUL);
4513 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4514 m_vColl[m_nCurrentColl].m_bParaAutoBefore = true;
4515 else
4516 m_bParaAutoBefore = true;
4517 }
4518 else
4519 {
4520 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4521 m_vColl[m_nCurrentColl].m_bParaAutoBefore = false;
4522 else
4523 m_bParaAutoBefore = false;
4524 }
4525}
4526
4527void SwWW8ImplReader::Read_ParaAutoAfter(sal_uInt16, const sal_uInt8 *pData, short nLen)
4528{
4529 if (nLen < 1)
4530 {
4531 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE);
4532 return;
4533 }
4534
4535 if (*pData)
4536 {
4537 SvxULSpaceItem aUL(*static_cast<const SvxULSpaceItem*>(GetFormatAttr(RES_UL_SPACE)));
4538 aUL.SetLower(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
4539 NewAttr(aUL);
4540 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4541 m_vColl[m_nCurrentColl].m_bParaAutoAfter = true;
4542 else
4543 m_bParaAutoAfter = true;
4544 }
4545 else
4546 {
4547 if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
4548 m_vColl[m_nCurrentColl].m_bParaAutoAfter = false;
4549 else
4550 m_bParaAutoAfter = false;
4551 }
4552}
4553
4554// Sprm 21, 22
4555void SwWW8ImplReader::Read_UL( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4556{
4557 // A workaround for an error in WW: For nProduct == 0c03d, usually
4558 // DyaAfter 240 (delta y distance after, comment of the translator)
4559 // is incorrectly inserted into style "Normal", even though it isn't there.
4560 // Using the ini flag WW8FL_NO_STY_DYA you can force this behavior for other
4561 // WW versions as well.
4562 // OSL_ENSURE( !bStyNormal || bWWBugNormal, "+This Document may point to a bug
4563 // in the WW version used for creating it. If the Styles <Standard> resp.
4564 // <Normal> differentiate between WW and SW in paragraph or line spacing,
4565 // then please send this Document to SH.");
4566 // bWWBugNormal is not a sufficient criterion for this distance being wrong.
4567
4568 if (nLen < 2)
4569 {
4570 // end of attribute
4571 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
4572 return;
4573 }
4574 short nPara = SVBT16ToUInt16( pData );
4575 if( nPara < 0 )
4576 nPara = -nPara;
4577
4578 SvxULSpaceItem aUL( *static_cast<const SvxULSpaceItem*>(GetFormatAttr( RES_UL_SPACE )));
4579
4580 switch( nId )
4581 {
4582 //sprmPDyaBefore
4585 aUL.SetUpper( nPara );
4586 break;
4587 //sprmPDyaAfter
4590 aUL.SetLower( nPara );
4591 break;
4592 default:
4593 return;
4594 }
4595
4596 NewAttr( aUL );
4597}
4598
4599void SwWW8ImplReader::Read_ParaContextualSpacing( sal_uInt16, const sal_uInt8* pData, short nLen )
4600{
4601 if (nLen < 1)
4602 {
4603 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
4604 return;
4605 }
4606 SvxULSpaceItem aUL( *static_cast<const SvxULSpaceItem*>(GetFormatAttr( RES_UL_SPACE )));
4607 aUL.SetContextValue(*pData != 0);
4608 NewAttr( aUL );
4609}
4610
4611void SwWW8ImplReader::Read_LineBreakClear(sal_uInt16 /*nId*/, const sal_uInt8* pData, short nLen)
4612{
4613 if (nLen == -1 && m_oLineBreakClear.has_value())
4614 {
4616 sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
4617 if (!pText || !nPos)
4618 {
4619 // There should have been a linebreak char.
4620 return;
4621 }
4622
4623 // Replace the linebreak char with a clearing break.
4624 --nPos;
4625 m_pPaM->SetMark();
4628 m_pPaM->DeleteMark();
4630 m_oLineBreakClear.reset();
4631 pText->InsertItem(aLineBreak, nPos, nPos);
4632 }
4633
4634 if (nLen < 1)
4635 {
4636 return;
4637 }
4638
4639 sal_uInt8 nClear = pData[0];
4640 if (nClear > 3)
4641 {
4642 return;
4643 }
4644
4645 auto eClear = static_cast<SwLineBreakClear>(nClear);
4646 m_oLineBreakClear = eClear;
4647}
4648
4649void SwWW8ImplReader::Read_IdctHint( sal_uInt16, const sal_uInt8* pData, short nLen )
4650{
4651 // sprmcidcthint (opcode 0x286f) specifies a script bias for the text in the run.
4652 // for unicode characters that are shared between far east and non-far east scripts,
4653 // this property determines what font and language the character will use.
4654 // when this value is 0, text properties bias towards non-far east properties.
4655 // when this value is 1, text properties bias towards far east properties.
4656 // when this value is 2, text properties bias towards complex properties.
4657 if (nLen < 1) //Property end
4658 {
4660 }
4661 else //Property start
4662 {
4664 }
4665}
4666
4667void SwWW8ImplReader::Read_Justify( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4668{
4669 if (nLen < 1)
4670 {
4672 return;
4673 }
4674
4675 SvxAdjust eAdjust(SvxAdjust::Left);
4676 bool bDistributed = false;
4677 switch (*pData)
4678 {
4679 default:
4680 case 0:
4681 break;
4682 case 1:
4683 eAdjust = SvxAdjust::Center;
4684 break;
4685 case 2:
4686 eAdjust = SvxAdjust::Right;
4687 break;
4688 case 3:
4689 eAdjust = SvxAdjust::Block;
4690 break;
4691 case 4:
4692 eAdjust = SvxAdjust::Block;
4693 bDistributed = true;
4694 break;
4695 }
4696 SvxAdjustItem aAdjust(eAdjust, RES_PARATR_ADJUST);
4697 if (bDistributed)
4698 aAdjust.SetLastBlock(SvxAdjust::Block);
4699
4700 NewAttr(aAdjust);
4702}
4703
4705{
4706 bool bRTL = false;
4707 SprmResult aDir;
4708 if (m_xPlcxMan)
4709 aDir = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PFBiDi::val);
4710 if (aDir.pSprm && aDir.nRemainingData >= 1)
4711 bRTL = *aDir.pSprm != 0;
4712 else
4713 {
4714 const SvxFrameDirectionItem* pItem=
4715 static_cast<const SvxFrameDirectionItem*>(GetFormatAttr(RES_FRAMEDIR));
4716 if (pItem && (pItem->GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4717 bRTL = true;
4718 }
4719 return bRTL;
4720}
4721
4722void SwWW8ImplReader::Read_RTLJustify( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
4723{
4724 if (nLen < 1)
4725 {
4727 return;
4728 }
4729
4730 //If we are in a ltr paragraph this is the same as normal Justify,
4731 //If we are in a rtl paragraph the meaning is reversed.
4732 if (!IsRightToLeft())
4733 Read_Justify(nId, pData, nLen);
4734 else
4735 {
4736 SvxAdjust eAdjust(SvxAdjust::Right);
4737 bool bDistributed = false;
4738 switch (*pData)
4739 {
4740 default:
4741 case 0:
4742 break;
4743 case 1:
4744 eAdjust = SvxAdjust::Center;
4745 break;
4746 case 2:
4747 eAdjust = SvxAdjust::Left;