LibreOffice Module sw (master) 1
ww8par2.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 <sal/config.h>
21#include <sal/log.hxx>
22
23#include <comphelper/string.hxx>
24#include <osl/diagnose.h>
25#include <o3tl/safeint.hxx>
26#include <o3tl/temporary.hxx>
27#include <tools/solar.h>
28#include <vcl/font.hxx>
29#include <hintids.hxx>
30#include <editeng/colritem.hxx>
31#include <editeng/orphitem.hxx>
32#include <editeng/widwitem.hxx>
33#include <editeng/brushitem.hxx>
34#include <editeng/boxitem.hxx>
35#include <editeng/lrspitem.hxx>
36#include <editeng/fhgtitem.hxx>
39#include <editeng/pgrditem.hxx>
40#include <msfilter.hxx>
41#include <pam.hxx>
42#include <deletelistener.hxx>
43#include <doc.hxx>
45#include <docary.hxx>
46#include <ndtxt.hxx>
47#include <paratr.hxx>
48#include <poolfmt.hxx>
49#include <swtable.hxx>
50#include <tblsel.hxx>
51#include <mdiexp.hxx>
52#include <txtftn.hxx>
53#include <frmfmt.hxx>
54#include <ftnidx.hxx>
55#include <fmtftn.hxx>
56#include <fmtlsplt.hxx>
57#include <charfmt.hxx>
58#include <fmtanchr.hxx>
59#include <fmtrowsplt.hxx>
60#include <fmtfollowtextflow.hxx>
61#include <numrule.hxx>
62#include "sprmids.hxx"
63#include <wwstyles.hxx>
64#include "ww8struc.hxx"
65#include "ww8par.hxx"
66#include "ww8par2.hxx"
67
68#include <frmatr.hxx>
69#include <itabenum.hxx>
70#include <unocrsr.hxx>
71
72#include <iostream>
73#include <memory>
74
75using namespace ::com::sun::star;
76
78 pNextBand(nullptr), nGapHalf(0), mnDefaultLeft(0), mnDefaultTop(0), mnDefaultRight(0),
79 mnDefaultBottom(0), mbHasSpacing(false), nLineHeight(0), nRows(0), nCenter{}, nWidth{},
80 nWwCols(0), nSwCols(0), bLEmptyCol(false), bREmptyCol(false), bCantSplit(false),
81 bCantSplit90(false), pTCs(nullptr), nOverrideSpacing{}, nOverrideValues{}, pSHDs(nullptr),
82 pNewSHDs(nullptr), bExist{}, nTransCell{}
83{
84 for (sal_uInt16 & rn : maDirections)
85 rn = 4;
86}
87
89{
90 delete[] pTCs;
91 delete[] pSHDs;
92 delete[] pNewSHDs;
93}
94
96 RedlineType eType, WW8TabDesc* pTabDesc )
97{
98 // If the redline type is not found in the redline stack, we have to check if there has been
99 // a tabledesc and to check its saved redline stack, too. (#136939, #i68139)
100 if( !close( rPos, eType ) )
101 {
102 if( pTabDesc && pTabDesc->getOldRedlineStack() )
103 {
104 bool const bResult =
105 pTabDesc->getOldRedlineStack()->close(rPos, eType);
106 OSL_ENSURE( bResult, "close without open!");
107 }
108 }
109}
110
112{
113 OSL_ENSURE(!maSegments.empty(),
114 "should not be possible, must be at least one segment");
115 if (!maSegments.empty())
116 maSegments.back().mbHasFootnote = true;
117}
118
119void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA)
120{
121 OSL_ENSURE(!maSegments.empty(),
122 "should not be possible, must be at least one segment");
123 if ( !maSegments.empty() )
124 maSegments.back().mnVerticalAdjustment = nVA;
125}
126
128{
129 OSL_ENSURE(!maSegments.empty(),
130 "should not be possible, must be at least one segment");
131 if (!maSegments.empty())
132 return maSegments.back().IsVertical();
133 return false;
134}
135
137{
138 OSL_ENSURE(!maSegments.empty(),
139 "should not be possible, must be at least one segment");
140 if (!maSegments.empty())
141 return SectionIsProtected(maSegments.back());
142 return false;
143}
144
146{
147 return !maSegments.empty() ? maSegments.back().m_nPgLeft : 0;
148}
149
151{
152 return !maSegments.empty() ? maSegments.back().m_nPgRight : 0;
153}
154
156{
157 return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0;
158}
159
161{
162 return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0;
163}
164
166{
167 return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0;
168}
169
171{
172 /*
173 Ignoring Footnote outside of the normal Text. People will put footnotes
174 into field results and field commands.
175 */
176 if (m_bIgnoreText ||
177 m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras())
178 {
179 return 0;
180 }
181
182 OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
183 if (m_aFootnoteStack.empty())
184 return 0;
185
186 bool bFtEdOk = false;
187 const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
188
189 //Get the footnote character and remove it from the txtnode. We'll
190 //replace it with the footnote
191 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
192 sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
193
194 OUString sChar;
195 SwTextFootnote* pFN = nullptr;
196 //There should have been a footnote char, we will replace this.
197 if (pText && nPos)
198 {
199 sChar += OUStringChar(pText->GetText()[--nPos]);
200 m_pPaM->SetMark();
201 m_pPaM->GetMark()->AdjustContent(-1);
202 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_oLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_oLastAnchorPos) : nullptr);
203 m_oLastAnchorPos.reset();
205 m_pPaM->DeleteMark();
206 if (xLastAnchorCursor)
207 m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
208 SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
209 pFN = static_cast<SwTextFootnote*>(pText->InsertItem(aFootnote, nPos, nPos));
210 }
211 OSL_ENSURE(pFN, "Problems creating the footnote text");
212 if (pFN)
213 {
214 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
215 WW8PLCFxSaveAll aSave;
216 m_xPlcxMan->SaveAllPLCFx( aSave );
217 std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan;
218
219 const SwNodeIndex* pSttIdx = pFN->GetStartNode();
220 assert(pSttIdx && "Problems creating footnote text");
221
223
224 bool bOld = m_bFootnoteEdn;
225 m_bFootnoteEdn = true;
226
227 SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr());
228
229 SvtDeleteListener aDeleteListener(rFormatFootnote.GetNotifier());
230
231 // read content of Ft-/End-Note
232 Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
233
234 m_bFootnoteEdn = bOld;
235
236 SAL_WARN_IF(aDeleteListener.WasDeleted(), "sw.ww8", "Footnode deleted during its import");
237 if (!aDeleteListener.WasDeleted())
238 {
239 bFtEdOk = true;
240
241 OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
242 "footnote autonumbering must be 0x02, and everything else must not be");
243
244 // If no automatic numbering use the following char from the main text
245 // as the footnote number
246 if (!rDesc.mbAutoNum)
247 pFN->SetNumber(0, 0, sChar);
248
249 /*
250 Delete the footnote char from the footnote if it's at the beginning
251 as usual. Might not be if the user has already deleted it, e.g.
252 #i14737#
253 */
254 SwPosition& rPaMPointPos = *m_pPaM->GetPoint();
255 rPaMPointPos.Assign(pSttIdx->GetIndex() + 1);
256 SwTextNode* pTNd = rPaMPointPos.GetNode().GetTextNode();
257 if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
258 {
259 const OUString &rText = pTNd->GetText();
260 if (rText[0] == sChar[0])
261 {
262 // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
263 sal_Int32 nFirstLineIndent=0;
265 if ( pTNd->GetAttr(aSet) )
266 {
268 if (pFirstLine)
269 {
270 nFirstLineIndent = pFirstLine->GetTextFirstLineOffset();
271 }
272 }
273
274 rPaMPointPos.SetContent(0);
275 m_pPaM->SetMark();
276 // Strip out aesthetic tabs we may have inserted on export #i24762#
277 if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09)
278 m_pPaM->GetMark()->AdjustContent(1);
279 m_pPaM->GetMark()->AdjustContent(1);
280 m_xReffingStck->Delete(*m_pPaM);
282 m_pPaM->DeleteMark();
283 }
284 }
285 }
286
287 *m_pPaM->GetPoint() = aTmpPos; // restore Cursor
288
289 m_xPlcxMan = xOldPlcxMan; // Restore attributes
290 m_xPlcxMan->RestoreAllPLCFx( aSave );
291 }
292
293 if (bFtEdOk)
294 m_aSectionManager.SetCurrentSectionHasFootnote();
295
296 m_aFootnoteStack.pop_back();
297 return 0;
298}
299
301{
302 /*
303 Ignoring Footnote outside of the normal Text. People will put footnotes
304 into field results and field commands.
305 */
306 if (m_bIgnoreText ||
307 m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras())
308 {
309 return 0;
310 }
311
312 FootnoteDescriptor aDesc;
313 aDesc.mbAutoNum = true;
314 if (eEDN == pRes->nSprmId)
315 {
316 aDesc.meType = MAN_EDN;
317 WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn();
318 if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
319 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
320 }
321 else
322 {
323 aDesc.meType = MAN_FTN;
324 WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote();
325 if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
326 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
327 }
328
329 aDesc.mnStartCp = pRes->nCp2OrIdx;
330 aDesc.mnLen = pRes->nMemLen;
331
332 m_aFootnoteStack.push_back(aDesc);
333
334 return 0;
335}
336
338 int nLevel) const
339{
340 WW8PLCFxDesc aRes;
341 aRes.pMemPos = nullptr;
342 aRes.nEndPos = rStartCp;
343 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
344
345 while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
346 {
347 if (pPap->Where() != WW8_CP_MAX)
348 {
349 SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel));
350 const sal_uInt8* pB = aSprmRes.pSprm;
351 if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1)
352 {
353 aSprmRes = pPap->HasSprm(0x6649);
354 const sal_uInt8 *pLevel = aSprmRes.pSprm;
355 if (pLevel && aSprmRes.nRemainingData >= 1)
356 {
357 if (nLevel + 1 == *pLevel)
358 return true;
359 }
360 else
361 {
362 OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
363 return true; // RowEnd found
364 }
365 }
366 }
367
368 aRes.nStartPos = aRes.nEndPos;
369 aRes.pMemPos = nullptr;
370 //Seek to our next block of properties
371 if (!(pPap->SeekPos(aRes.nStartPos)))
372 {
373 aRes.nEndPos = WW8_CP_MAX;
374 pPap->SetDirty(true);
375 }
376 pPap->GetSprms(&aRes);
377 pPap->SetDirty(false);
378 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
379 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
380 {
381 SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
382 break;
383 }
384 //Update our aRes to get the new starting point of the next properties
385 rStartCp = aRes.nEndPos;
386 }
387
388 return false;
389}
390
391ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
392 const WW8_TablePos *pTabPos)
393{
394 const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
395 ApoTestResults aRet;
396 // Frame in Style Definition (word appears to ignore them if inside an
397 // text autoshape)
398 sal_uInt16 const nStyle(m_xPlcxMan->GetColl());
399 if (!m_bTxbxFlySection && nStyle < m_vColl.size())
400 aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
401
402 /*
403 #i1140#
404 If I have a table and apply a style to one of its frames that should cause
405 a paragraph that it is applied to it to only exist as a separate floating
406 frame, then the behaviour depends on which cell that it has been applied
407 to. If it is the first cell of a row then the whole table row jumps into the
408 new frame, if it isn't then the paragraph attributes are applied
409 "except" for the floating frame stuff. i.e. it's ignored. So if there's a
410 table, and we're not in the first cell then we ignore the fact that the
411 paragraph style wants to be in a different frame.
412
413 This sort of mindbending inconsistency is surely why frames are deprecated
414 in word 97 onwards and hidden away from the user
415
416 #i1532# & #i5379#
417 If we are already a table in a frame then we must grab the para properties
418 to see if we are still in that frame.
419 */
420
421 aRet.m_bHasSprm37 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr; // sprmPWr
422 SprmResult aSrpm29 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B); // sprmPPc
423 const sal_uInt8 *pSrpm29 = aSrpm29.pSprm;
424 aRet.m_bHasSprm29 = pSrpm29 != nullptr;
425 const sal_Int16 nTPc = aRet.mpStyleApo ? aRet.mpStyleApo->nTPc : 0;
426 aRet.m_nSprm29 = (pSrpm29 && aSrpm29.nRemainingData >= 1) ? *pSrpm29 : nTPc;
427
428 // Is there some frame data here
429 bool bNowApo = aRet.HasFrame() || pTopLevelTable;
430 if (bNowApo)
431 {
432 if (!ConstructApo(aRet, pTabPos))
433 bNowApo = false;
434 }
435
436 bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
437 if (bTestAllowed)
438 {
439 //Test is allowed if there is no table.
440 //Otherwise only allowed if we are in the
441 //first paragraph of the first cell of a row.
442 //(And only if the row we are inside is at the
443 //same level as the previous row, think tables
444 //in tables)
445 if (nCellLevel == m_nInTable)
446 {
447
448 if (!m_nInTable)
449 bTestAllowed = true;
450 else
451 {
452 if (!m_xTableDesc)
453 {
454 OSL_ENSURE(m_xTableDesc, "What!");
455 bTestAllowed = false;
456 }
457 else
458 {
459 // #i39468#
460 // If current cell isn't valid, the test is allowed.
461 // The cell isn't valid, if e.g. there is a new row
462 // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()>
463 bTestAllowed =
464 m_xTableDesc->GetCurrentCol() == 0 &&
465 ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) ||
466 m_xTableDesc->InFirstParaInCell() );
467 }
468 }
469 }
470 }
471
472 if (!bTestAllowed)
473 return aRet;
474
475 aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
476 aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
477
478 //If it happens that we are in a table, then if it's not the first cell
479 //then any attributes that might otherwise cause the contents to jump
480 //into another frame don't matter, a table row sticks together as one
481 //unit no matter what else happens. So if we are not in a table at
482 //all, or if we are in the first cell then test that the last frame
483 //data is the same as the current one
484 if (bNowApo && InEqualApo(nCellLevel))
485 {
486 // two bordering each other
487 if (!TestSameApo(aRet, pTabPos))
488 aRet.mbStopApo = aRet.mbStartApo = true;
489 }
490
491 return aRet;
492}
493
494// helper methods for outline, numbering and bullets
495
496static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
497{
501
502 static const SvxAdjust eAdjA[4] = { SvxAdjust::Left,
503 SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left };
504 if (rAV.nfc < 8) {
505 rNum.SetNumberingType( eNumA[ rAV.nfc ] );
506 } else {
508 switch( rAV.nfc ) {
509 case 14:
510 case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
511 case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
512 case 31:nType = SVX_NUM_DI_ZI_ZH; break;
513 case 35:
514 case 36:
515 case 37:
516 case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
517 case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
518 case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
519 case 10:
520 case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
521 case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
522 case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
523 case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
524 case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
525 case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
526 case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
527 case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
528 //case 42:
529 //case 43:
530 case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
531 default:
532 nType= SVX_NUM_ARABIC;break;
533 }
534 rNum.SetNumberingType( nType );
535 }
536
537 if ((rAV.aBits1 & 0x4) >> 2)
538 {
539 rNum.SetIncludeUpperLevels(nSwLevel + 1);
540 }
541 rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) );
542 rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
543
544 rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) );
545 sal_Int16 nIndent = std::abs(static_cast<sal_Int16>(SVBT16ToUInt16( rAV.dxaIndent )));
546 if( rAV.aBits1 & 0x08 ) //fHang
547 {
548 rNum.SetFirstLineOffset( -nIndent );
549 rNum.SetAbsLSpace( nIndent );
550 }
551 else
552 rNum.SetCharTextDistance( nIndent ); // width of number is missing
553
554 if( rAV.nfc == 5 || rAV.nfc == 7 )
555 {
556 OUString sP = "." + rNum.GetSuffix();
557 rNum.SetListFormat("", sP, nSwLevel); // ordinal number
558 }
559 else
560 rNum.SetListFormat("", "", nSwLevel);
561}
562
563void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV,
564 const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
565{
566 if (nStart > nElements)
567 return;
568
569 pText += nStart;
570 nElements -= nStart;
571
572 bool bInsert = false; // Default
573 rtl_TextEncoding eCharSet = m_eStructCharSet;
574
575 const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo
576 bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/...
577
578 sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
579 OUStringBuffer sText(static_cast<sal_Int32>(nLen));
580 if (m_bVer67)
581 {
582 if (nLen > nElements)
583 {
584 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
585 << nLen << " vs " << nElements << " max");
586 return;
587 }
588 sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
589 // ofz#23961 in case of multi-byte input encoding resulting in shorter
590 // output pad to full length with something semi-arbitrary
592 }
593 else
594 {
595 if (nLen > nElements / 2)
596 {
597 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
598 << nLen << " vs " << nElements / 2 << " max");
599 return;
600 }
601 for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
602 {
603 sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText))));
604 }
605 }
606
607 if( bOutline )
608 { // outline
609 if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
610 || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
611 {
612 // if self defined digits
613 bInsert = true; // then apply character
614
615 // replace by simple Bullet ?
616 if( bListSymbol )
617 {
618 // use cBulletChar for correct mapping on MAC
619 sText.setLength(0);
621 + rAV.cbTextAfter, cBulletChar);
622 }
623 }
624 }
625 else
626 { // numbering / bullets
627 bInsert = true;
628 if( bListSymbol )
629 {
630 FontFamily eFamily;
631 OUString aName;
632 FontPitch ePitch;
633
634 if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName,
635 ePitch, eCharSet ) ){
636
637 vcl::Font aFont;
638 aFont.SetFamilyName( aName );
639 aFont.SetFamily( eFamily );
640
641 aFont.SetCharSet( eCharSet );
643
644 rNum.SetBulletFont( &aFont );
645
646 // take only the very first character
647 if (rAV.cbTextBefore || rAV.cbTextAfter)
648 {
649 rNum.SetBulletChar(
650 OUString::unacquired(sText).iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
651 }
652 else
653 rNum.SetBulletChar( 0x2190 );
654 }
655 }
656 }
657 if( !bInsert )
658 return;
659
660 OUString sPrefix;
661 OUString sSuffix;
662 if (rAV.cbTextBefore)
663 {
664 sPrefix = sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear();
665 }
666 if( rAV.cbTextAfter )
667 {
668 sSuffix = rNum.GetSuffix() + sText.subView( rAV.cbTextBefore, rAV.cbTextAfter);
669 }
670
671 rNum.SetListFormat(sPrefix, sSuffix, nLevel);
672
673// The characters before and after multiple digits do not apply because
674// those are handled differently by the writer and the result is in most
675// cases worse than without.
676}
677
678// SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
679// which are provided by pNumR. This is used for everything beside
680// outline inside the text.
681void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
682 bool bOutLine)
683{
684 SwNumFormat aNF;
685 aNF.SetListFormat("", "", nSwLevel);
686 if (pAD)
687 { // there is an Anld-Sprm
688 m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross;
689 WW8_ANLV const &rAV = pAD->eAnlv;
690 SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
691 SetAnlvStrings(aNF, nSwLevel, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
692 }
693 pNumR->Set(nSwLevel, aNF);
694}
695
696// chapter numbering and bullets
697
698// Chapter numbering happens in the style definition.
699// Sprm 13 provides the level, Sprm 12 the content.
700
702{
703 if( m_xStyles->mpStyRule ) // Bullet-Style already present
704 return m_xStyles->mpStyRule;
705
706 const OUString aBaseName("WW8StyleNum");
707 const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
708
709 // #i86652#
710 sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false,
712 m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul];
713 // Auto == false-> numbering style
714 m_xStyles->mpStyRule->SetAutoRule(false);
715
716 return m_xStyles->mpStyRule;
717}
718
719// Sprm 13
720void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
721{
722 m_nSwNumLevel = 0xff; // Default: invalid
723
724 if( nLen <= 0 )
725 return;
726
727 // StyleDef ?
728 if( m_pCurrentColl )
729 {
730 // only for SwTextFormatColl, not CharFormat
731 // WW: 0 = no Numbering
732 SwWW8StyInf * pColl = GetStyle(m_nCurrentColl);
733 if (pColl != nullptr && pColl->m_bColl && *pData)
734 {
735 // Range WW:1..9 -> SW:0..8 no bullets / numbering
736
737 if (*pData <= 9)
738 {
739 m_nSwNumLevel = *pData - 1;
740 if (!m_bNoAttrImport)
741 static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
742 // For WW-NoNumbering also NO_NUMBERING could be used.
743 // ( For normal numberierung NO_NUM has to be used:
744 // NO_NUM : pauses numbering,
745 // NO_NUMBERING : no numbering at all )
746
747 }
748 else if( *pData == 10 || *pData == 11 )
749 {
750 // remember type, the rest happens at Sprm 12
751 m_xStyles->mnWwNumLevel = *pData;
752 }
753 }
754 }
755 else
756 {
757 //Not StyleDef
758 if (!m_bAnl)
759 StartAnl(pData); // begin of outline / bullets
760 NextAnlLine(pData);
761 }
762}
763
764void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
765{
766 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
767 if( !m_pCurrentColl || nLen <= 0 // only for Styledef
768 || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
769 || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
770 {
771 m_nSwNumLevel = 0xff;
772 return;
773 }
774
775 if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD))
776 {
777 SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
778 m_nSwNumLevel = 0xff;
779 return;
780 }
781
782 if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8
783 {
784
785 // If NumRuleItems were set, either directly or through inheritance, disable them now
786 m_pCurrentColl->SetFormatAttr( SwNumRuleItem() );
787
788 const OUString aName("Outline");
791 OUTLINE_RULE );
792 aNR = *m_rDoc.GetOutlineNumRule();
793
794 SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
795
796 // Missing Levels need not be replenished
798 }
799 else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){
800 SwNumRule* pNR = GetStyRule();
801 SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
802 m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
803
804 pStyInf = GetStyle(m_nCurrentColl);
805 if (pStyInf != nullptr)
806 pStyInf->m_bHasStyNumRule = true;
807 }
808}
809
810// Numbering / Bullets
811
812// SetNumOlst() carries the Numrules for this cell to SwNumFormat.
813// For this the info is fetched from OLST and not from ANLD ( see later )
814// ( only for outline inside text; Bullets / numbering use ANLDs )
816{
817 SwNumFormat aNF;
818 WW8_ANLV &rAV = pO->rganlv[nSwLevel];
819 SetBaseAnlv(aNF, rAV, nSwLevel);
820 // ... and then the Strings
821 int nTextOfs = 0;
822 sal_uInt8 i;
823 WW8_ANLV* pAV1; // search String-Positions
824 for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
825 nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
826
827 if (!m_bVer67)
828 nTextOfs *= 2;
829 SetAnlvStrings(aNF, nSwLevel, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
830 pNumR->Set(nSwLevel, aNF);
831}
832
833// The OLST is at the beginning of each section that contains outlines.
834// The ANLDs that are connected to each outline-line contain only nonsense,
835// so the OLSTs are remembered for the section to have usable information
836// when outline-paragraphs occur.
837void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
838{
839 m_xNumOlst.reset();
840 if (nLen <= 0)
841 return;
842
843 if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST))
844 {
845 SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
846 return;
847 }
848
849 m_xNumOlst.reset(new WW8_OLST);
850 *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
851}
852
854{
855 WW8LvlType nRet = WW8_None;
856 if( nWwLevelNo == 12 )
857 nRet = WW8_Pause;
858 else if( nWwLevelNo == 10 )
859 nRet = WW8_Numbering;
860 else if( nWwLevelNo == 11 )
861 nRet = WW8_Sequence;
862 else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
863 nRet = WW8_Outline;
864 return nRet;
865}
866
868{
869 const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
870 if (rNumRule.isEmpty())
871 return nullptr;
872 return rDoc.FindNumRulePtr(rNumRule);
873}
874
875void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
876{
877 if (WW8_Numbering == nNumType)
878 msNumberingNumRule = rNumRule;
879 else
880 msOutlineNumRule = rNumRule;
881}
882
883// StartAnl is called at the beginning of a row area that contains
884// outline / numbering / bullets
886{
887 m_bCurrentAND_fNumberAcross = false;
888
889 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
890 if (nT == WW8_Pause || nT == WW8_None)
891 return;
892
893 m_nWwNumType = nT;
894 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
895
896 // check for COL numbering:
897 SprmResult aS12; // sprmAnld
898 OUString sNumRule;
899
900 if (m_xTableDesc)
901 {
902 sNumRule = m_xTableDesc->GetNumRuleName();
903 if (!sNumRule.isEmpty())
904 {
905 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
906 if (!pNumRule)
907 sNumRule.clear();
908 else
909 {
910 // this is ROW numbering ?
911 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
912 if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
913 sNumRule.clear();
914 }
915 }
916 }
917
918 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
919 if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
920 {
921 sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
922 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
923 if (!pNumRule)
924 sNumRule.clear();
925 }
926
927 if (sNumRule.isEmpty())
928 {
929 if (!pNumRule)
930 {
931 // #i86652#
932 pNumRule = m_rDoc.GetNumRuleTable()[
933 m_rDoc.MakeNumRule( sNumRule, nullptr, false,
935 }
936 if (m_xTableDesc)
937 {
938 if (!aS12.pSprm)
939 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
940 if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
941 m_xTableDesc->SetNumRuleName(pNumRule->GetName());
942 }
943 }
944
945 m_bAnl = true;
946
947 sNumRule = pNumRule ? pNumRule->GetName() : OUString();
948 // set NumRules via stack
949 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(),
951
952 m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
953}
954
955// NextAnlLine() is called once for every row of a
956// outline / numbering / bullet
958{
959 if (!m_bAnl)
960 return;
961
962 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
963
964 // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3
965
966 // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0
967 if (*pSprm13 == 10 || *pSprm13 == 11)
968 {
969 m_nSwNumLevel = 0;
970 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
971 {
972 // not defined yet
973 // sprmAnld o. 0
974 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
975 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
976 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
977 }
978 }
979 else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
980 {
981 m_nSwNumLevel = *pSprm13 - 1; // outline
982 // undefined
983 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
984 {
985 if (m_xNumOlst) // there was a OLST
986 {
987 //Assure upper levels are set, #i9556#
988 for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
989 {
990 if (!pNumRule->GetNumFormat(nI))
991 SetNumOlst(pNumRule, m_xNumOlst.get(), nI);
992 }
993
994 SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel);
995 }
996 else // no Olst -> use Anld
997 {
998 // sprmAnld
999 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1000 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1001 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1002 }
1003 }
1004 }
1005 else
1006 m_nSwNumLevel = 0xff; // no number
1007
1008 SwTextNode* pNd = m_pPaM->GetPointNode().GetTextNode();
1009 if (!pNd)
1010 return;
1011
1012 if (m_nSwNumLevel < MAXLEVEL)
1013 pNd->SetAttrListLevel( m_nSwNumLevel );
1014 else
1015 {
1016 pNd->SetAttrListLevel(0);
1017 pNd->SetCountedInList( false );
1018 }
1019}
1020
1022{
1023 //Of course we're not restarting, but we'll make use of our knowledge
1024 //of the implementation to do it.
1025 StopAnlToRestart(WW8_None, bGoBack);
1026}
1027
1029{
1030 if (bGoBack)
1031 {
1032 SwPosition aTmpPos(*m_pPaM->GetPoint());
1033 m_pPaM->Move(fnMoveBackward, GoInContent);
1034 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1035 *m_pPaM->GetPoint() = aTmpPos;
1036 }
1037 else
1038 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1039
1040 m_aANLDRules.msNumberingNumRule.clear();
1041 /*
1042 #i18816#
1043 my take on this problem is that moving either way from an outline to a
1044 numbering doesn't halt the outline, while the numbering is always halted
1045 */
1046 bool bNumberingNotStopOutline =
1047 (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
1048 ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
1049 if (!bNumberingNotStopOutline)
1050 m_aANLDRules.msOutlineNumRule.clear();
1051
1052 m_nSwNumLevel = 0xff;
1053 m_nWwNumType = WW8_None;
1054 m_bAnl = false;
1055}
1056
1058{
1059 *this = rBand;
1060 if( rBand.pTCs )
1061 {
1062 pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]);
1063 // create uninitialized
1064 memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
1065 }
1066 if( rBand.pSHDs )
1067 {
1068 pSHDs = new WW8_SHD[nWwCols];
1069 memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
1070 }
1071 if( rBand.pNewSHDs )
1072 {
1073 pNewSHDs = new Color[nWwCols];
1074 memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color));
1075 }
1076 memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
1077}
1078
1079// ReadDef reads the cell position and the borders of a band
1080void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen)
1081{
1082 --nLen; //reduce len by expected nCols arg
1083 if (nLen < 0)
1084 return;
1085 sal_uInt8 nCols = *pS; // number of cells
1086
1087 if (nCols > MAX_COL)
1088 return;
1089
1090 nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments
1091 if (nLen < 0)
1092 return;
1093
1094 short nOldCols = nWwCols;
1095 nWwCols = nCols;
1096
1097 const sal_uInt8* pT = &pS[1];
1098 for (int i = 0; i <= nCols; i++, pT+=2)
1099 nCenter[i] = static_cast<sal_Int16>(SVBT16ToUInt16( pT )); // X-borders
1100
1101 if( nCols != nOldCols ) // different column count
1102 {
1103 delete[] pTCs;
1104 pTCs = nullptr;
1105 delete[] pSHDs;
1106 pSHDs = nullptr;
1107 delete[] pNewSHDs;
1108 pNewSHDs = nullptr;
1109 }
1110
1111 short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
1112
1113 if (!pTCs && nCols)
1114 {
1115 // create empty TCs
1116 pTCs = new WW8_TCell[nCols];
1117 }
1118
1119 short nColsToRead = std::min<short>(nFileCols, nCols);
1120
1121 if (nColsToRead <= 0)
1122 return;
1123
1124 // read TCs
1125
1126 /*
1127 Attention: Beginning with Ver8 there is an extra ushort per TC
1128 added and the size of the border code is doubled.
1129 Because of this a simple copy (pTCs[i] = *pTc;)
1130 is not possible.
1131 ---
1132 Advantage: The work structure suits better.
1133 */
1134 WW8_TCell* pCurrentTC = pTCs;
1135 if( bVer67 )
1136 {
1137 WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
1138 for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc)
1139 {
1140 // TC from file ?
1141 sal_uInt8 aBits1 = pTc->aBits1Ver6;
1142 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
1143 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
1144 pCurrentTC->rgbrc[ WW8_TOP ]
1145 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
1146 pCurrentTC->rgbrc[ WW8_LEFT ]
1147 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
1148 pCurrentTC->rgbrc[ WW8_BOT ]
1149 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
1150 pCurrentTC->rgbrc[ WW8_RIGHT ]
1151 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1152 if( ( pCurrentTC->bMerged )
1153 && ( i > 0 ) )
1154 {
1155 // Cell merged -> remember
1156 //bWWMergedVer6[i] = true;
1157 pTCs[i-1].rgbrc[ WW8_RIGHT ]
1158 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1159 // apply right border to previous cell
1160 // bExist must not be set to false, because WW
1161 // does not count this cells in text boxes...
1162 }
1163 }
1164 }
1165 else
1166 {
1167 WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
1168 for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc )
1169 {
1170 sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 );
1171 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
1172 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
1173 pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
1174 pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
1175 pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
1176 pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
1177 pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
1178 pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
1179 // note: in aBits1 there are 7 bits unused,
1180 // followed by another 16 unused bits
1181
1182 pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
1183 pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
1184 pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
1185 pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
1186 }
1187 }
1188
1189 // #i25071 In '97 text direction appears to be only set using TC properties
1190 // not with sprmTTextFlow so we need to cycle through the maDirections and
1191 // double check any non-default directions
1192 for (int k = 0; k < nCols; ++k)
1193 {
1194 if(maDirections[k] == 4)
1195 {
1196 if(pTCs[k].bVertical)
1197 {
1198 if(pTCs[k].bBackward)
1199 maDirections[k] = 3;
1200 else
1201 maDirections[k] = 1;
1202 }
1203 }
1204 }
1205}
1206
1207void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen)
1208{
1209 if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s)
1210 return;
1211
1212 if (nParamsLen < 3)
1213 {
1214 SAL_WARN("sw.ww8", "table border property is too short");
1215 return;
1216 }
1217
1218 sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
1219 sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
1220 sal_uInt8 nFlag = *(pParamsTSetBRC+2);
1221
1222 if (nitcFirst >= nWwCols)
1223 return;
1224
1225 if (nitcLim > nWwCols)
1226 nitcLim = nWwCols;
1227
1228 bool bChangeRight = (nFlag & 0x08) != 0;
1229 bool bChangeBottom = (nFlag & 0x04) != 0;
1230 bool bChangeLeft = (nFlag & 0x02) != 0;
1231 bool bChangeTop = (nFlag & 0x01) != 0;
1232
1233 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1234 WW8_BRCVer9 brcVer9;
1235 if( nBrcVer == 6 )
1236 {
1237 if (nParamsLen < sizeof(WW8_BRCVer6) + 3)
1238 {
1239 SAL_WARN("sw.ww8", "table border property is too short");
1240 return;
1241 }
1242 brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
1243 }
1244 else if( nBrcVer == 8 )
1245 {
1246 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1247 if (nParamsLen < sizeof(WW8_BRC) + 3)
1248 {
1249 SAL_WARN("sw.ww8", "table border property is too short");
1250 return;
1251 }
1252 brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
1253 }
1254 else
1255 {
1256 if (nParamsLen < sizeof(WW8_BRCVer9) + 3)
1257 {
1258 SAL_WARN("sw.ww8", "table border property is too short");
1259 return;
1260 }
1261 brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
1262 }
1263
1264 for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC )
1265 {
1266 if( bChangeTop )
1267 pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9;
1268 if( bChangeLeft )
1269 pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9;
1270 if( bChangeBottom )
1271 pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9;
1272 if( bChangeRight )
1273 pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9;
1274 }
1275}
1276
1277void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen)
1278{
1279 // sprmTTableBorders
1280 if( nBrcVer == 6 )
1281 {
1282 if (nParamsLen < sizeof(WW8_BRCVer6) * 6)
1283 {
1284 SAL_WARN("sw.ww8", "table border property is too short");
1285 return;
1286 }
1287 WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
1288 for (int i = 0; i < 6; ++i)
1289 aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
1290 }
1291 else if ( nBrcVer == 8 )
1292 {
1293 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1294 if (nParamsLen < sizeof(WW8_BRC) * 6)
1295 {
1296 SAL_WARN("sw.ww8", "table border property is too short");
1297 return;
1298 }
1299 for( int i = 0; i < 6; ++i )
1300 aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
1301 }
1302 else
1303 {
1304 if (nParamsLen < sizeof( aDefBrcs ))
1305 {
1306 SAL_WARN("sw.ww8", "table border property is too short");
1307 return;
1308 }
1309 memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
1310 }
1311}
1312
1314{
1315 // sprmTDxaCol (opcode 0x7623) changes the width of cells
1316 // whose index is within a certain range to be a certain value.
1317
1318 if( !(nWwCols && pParamsTDxaCol) ) // set one or more cell length(s)
1319 return;
1320
1321 sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
1322 sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
1323 short nDxaCol = static_cast<sal_Int16>(SVBT16ToUInt16( pParamsTDxaCol + 2 ));
1324
1325 for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
1326 {
1327 const short nOrgWidth = nCenter[i+1] - nCenter[i];
1328 const short nDelta = nDxaCol - nOrgWidth;
1329 for( int j = i+1; j <= nWwCols; j++ )
1330 {
1331 nCenter[j] = nCenter[j] + nDelta;
1332 }
1333 }
1334}
1335
1337{
1338 if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s)
1339 return;
1340
1341 sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
1342 if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
1343 return;
1344 sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
1345 sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 );
1346
1347 short nNewWwCols;
1348 if (nitcInsert > nWwCols)
1349 {
1350 nNewWwCols = nitcInsert+nctc;
1351 //if new count would be outside max possible count, clip it, and calc a new replacement
1352 //legal nctc
1353 if (nNewWwCols > MAX_COL)
1354 {
1355 nNewWwCols = MAX_COL;
1356 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
1357 }
1358 }
1359 else
1360 {
1361 nNewWwCols = nWwCols+nctc;
1362 //if new count would be outside max possible count, clip it, and calc a new replacement
1363 //legal nctc
1364 if (nNewWwCols > MAX_COL)
1365 {
1366 nNewWwCols = MAX_COL;
1367 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
1368 }
1369 }
1370
1371 WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
1372
1373 if (pTCs)
1374 {
1375 memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
1376 delete[] pTCs;
1377 }
1378 pTCs = pTC2s;
1379
1380 //If we have to move some cells
1381 if (nitcInsert <= nWwCols)
1382 {
1383 // adjust the left x-position of the dummy at the very end
1384 nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
1385 for( int i = nWwCols-1; i >= nitcInsert; i--)
1386 {
1387 // adjust the left x-position
1388 nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
1389
1390 // adjust the cell's borders
1391 pTCs[i + nctc] = pTCs[i];
1392 }
1393 }
1394
1395 //if itcMac is larger than full size, fill in missing ones first
1396 for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
1397 nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
1398
1399 //now add in our new cells
1400 for( int j = 0;j < nctc; j++)
1401 nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
1402
1403 nWwCols = nNewWwCols;
1404
1405}
1406
1408{
1409 sal_uInt8 nStartCell = *pParams++;
1410 sal_uInt8 nEndCell = *pParams++;
1411 sal_uInt16 nCode = SVBT16ToUInt16(pParams);
1412
1413 OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
1414 OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
1415 if (nStartCell > MAX_COL)
1416 return;
1417 if (nEndCell > MAX_COL + 1)
1418 nEndCell = MAX_COL + 1;
1419
1420 for (;nStartCell < nEndCell; ++nStartCell)
1421 maDirections[nStartCell] = nCode;
1422}
1423
1425{
1426 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1427 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1428 if (nLen != 6)
1429 return;
1430 mbHasSpacing=true;
1431#if OSL_DEBUG_LEVEL > 0
1432 sal_uInt8 nWhichCell = *pParams;
1433 OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
1434#endif
1435 ++pParams; //Skip which cell
1436 ++pParams; //unknown byte
1437
1438 sal_uInt8 nSideBits = *pParams++;
1439 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1440 ++pParams; //unknown byte
1441 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1442 for (int i = wwTOP; i <= wwRIGHT; i++)
1443 {
1444 switch (nSideBits & (1 << i))
1445 {
1446 case 1 << wwTOP:
1447 mnDefaultTop = nValue;
1448 break;
1449 case 1 << wwLEFT:
1450 mnDefaultLeft = nValue;
1451 break;
1452 case 1 << wwBOTTOM:
1453 mnDefaultBottom = nValue;
1454 break;
1455 case 1 << wwRIGHT:
1456 mnDefaultRight = nValue;
1457 break;
1458 case 0:
1459 break;
1460 default:
1461 OSL_ENSURE(false, "Impossible");
1462 break;
1463 }
1464 }
1465}
1466
1468{
1469 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1470 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1471 if (nLen != 6)
1472 return;
1473
1474 const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to.
1475 const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins.
1476 OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings");
1477 if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 )
1478 return;
1479
1480 sal_uInt8 nSideBits = *pParams++;
1481 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1482
1483 const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin
1484 OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width");
1485 if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored
1486 return;
1487
1488 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1489
1490 for (int nCell = nStartCell; nCell < nEndCell; ++nCell)
1491 {
1492 nOverrideSpacing[ nCell ] |= nSideBits;
1493 OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits");
1494
1495 for (int i=0; i < 4; i++)
1496 {
1497 if (nSideBits & (1 << i))
1498 nOverrideValues[ nCell ][ i ] = nValue;
1499 }
1500 }
1501}
1502
1504{
1505 if( !(nWwCols && pParamsTDelete) ) // set one or more cell length(s)
1506 return;
1507
1508 sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
1509 if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
1510 return;
1511 sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
1512 if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
1513 return;
1514
1515 /*
1516 * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
1517 * greater than or equal to itcLim to be moved
1518 */
1519 int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
1520
1521 if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
1522 {
1523 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1524 int i = 0;
1525 while( i < nShlCnt )
1526 {
1527 // adjust the left x-position
1528 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1529
1530 // adjust the cell's borders
1531 *pCurrentTC = pTCs[ nitcLim + i];
1532
1533 ++i;
1534 ++pCurrentTC;
1535 }
1536 // adjust the left x-position of the dummy at the very end
1537 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1538 }
1539
1540 short nCellsDeleted = nitcLim - nitcFirst;
1541 //clip delete request to available number of cells
1542 if (nCellsDeleted > nWwCols)
1543 nCellsDeleted = nWwCols;
1544 nWwCols -= nCellsDeleted;
1545}
1546
1547// ReadShd reads the background color of a cell
1548// ReadDef must be called before
1550{
1551 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1552 if( !nLen )
1553 return;
1554
1555 if( !pSHDs )
1556 {
1557 pSHDs = new WW8_SHD[nWwCols];
1558 }
1559
1560 short nCount = nLen >> 1;
1561 if (nCount > nWwCols)
1562 nCount = nWwCols;
1563
1564 SVBT16 const * pShd;
1565 int i;
1566 for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
1567 pSHDs[i].SetWWValue( *pShd );
1568}
1569
1570void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart)
1571{
1572 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1573 if (!nLen || nStart >= nWwCols)
1574 return;
1575
1576 if (!pNewSHDs)
1577 pNewSHDs = new Color[nWwCols];
1578
1579 short nCount = nLen / 10 + nStart; //10 bytes each
1580 if (nCount > nWwCols)
1581 nCount = nWwCols;
1582
1583 int i=nStart;
1584 while (i < nCount)
1585 pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
1586
1587 while (i < nWwCols)
1588 pNewSHDs[i++] = COL_AUTO;
1589}
1590
1591namespace
1592{
1593 SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
1594 {
1595 if (bVer67)
1596 return pPap->HasSprm(24);
1597 SprmResult aRes = pPap->HasSprm(0x244B);
1598 if (aRes.pSprm == nullptr)
1599 aRes = pPap->HasSprm(0x2416);
1600 return aRes;
1601 }
1602}
1603
1604namespace {
1605
1606enum wwTableSprm
1607{
1608 sprmNil,
1609
1610 sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
1613 sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
1614 sprmTDefTableNewShd, sprmTDefTableNewShd2nd, sprmTDefTableNewShd3rd,
1615 sprmTCellPadding, sprmTCellPaddingDefault
1616};
1617
1618}
1619
1620static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
1621{
1622 switch (eVer)
1623 {
1624 case ww::eWW8:
1625 switch (nId)
1626 {
1628 return sprmTTableWidth;
1630 return sprmTTextFlow;
1632 return sprmTTableHeader;
1634 return sprmTFCantSplit;
1636 return sprmTJc;
1638 return sprmTFBiDi;
1640 return sprmTDelete;
1642 return sprmTInsert;
1644 return sprmTDxaCol;
1646 return sprmTDyaRowHeight;
1648 return sprmTDxaLeft;
1650 return sprmTDxaGapHalf;
1652 return sprmTTableBorders;
1654 return sprmTDefTable;
1656 return sprmTDefTableShd;
1658 return sprmTDefTableNewShd;
1660 return sprmTDefTableNewShd2nd;
1662 return sprmTDefTableNewShd3rd;
1664 return sprmTTableBorders90;
1666 return sprmTSetBrc;
1668 return sprmTSetBrc90;
1670 return sprmTCellPadding;
1672 return sprmTCellPaddingDefault;
1673 }
1674 break;
1675 case ww::eWW7:
1676 case ww::eWW6:
1677 switch (nId)
1678 {
1679 case 182:
1680 return sprmTJc;
1681 case 183:
1682 return sprmTDxaLeft;
1683 case 184:
1684 return sprmTDxaGapHalf;
1685 case 186:
1686 return sprmTTableHeader;
1687 case 187:
1688 return sprmTTableBorders;
1689 case 189:
1690 return sprmTDyaRowHeight;
1691 case 190:
1692 return sprmTDefTable;
1693 case 191:
1694 return sprmTDefTableShd;
1695 case 193:
1696 return sprmTSetBrc;
1697 case 194:
1698 return sprmTInsert;
1699 case 195:
1700 return sprmTDelete;
1701 case 196:
1702 return sprmTDxaCol;
1703 }
1704 break;
1705 case ww::eWW1:
1706 case ww::eWW2:
1707 switch (nId)
1708 {
1709 case 146:
1710 return sprmTJc;
1711 case 147:
1712 return sprmTDxaLeft;
1713 case 148:
1714 return sprmTDxaGapHalf;
1715 case 153:
1716 return sprmTDyaRowHeight;
1717 case 154:
1718 return sprmTDefTable;
1719 case 155:
1720 return sprmTDefTableShd;
1721 case 157:
1722 return sprmTSetBrc;
1723 case 158:
1724 return sprmTInsert;
1725 case 159:
1726 return sprmTDelete;
1727 case 160:
1728 return sprmTDxaCol;
1729 }
1730 break;
1731 }
1732 return sprmNil;
1733}
1734
1736 m_pIo(pIoClass),
1737 m_pFirstBand(nullptr),
1738 m_pActBand(nullptr),
1739 m_pTableNd(nullptr),
1740 m_pTabLines(nullptr),
1741 m_pTabLine(nullptr),
1742 m_pTabBoxes(nullptr),
1743 m_pTabBox(nullptr),
1744 m_pCurrentWWCell(nullptr),
1745 m_nRows(0),
1746 m_nDefaultSwCols(0),
1747 m_nBands(0),
1748 m_nMinLeft(0),
1749 m_nMaxRight(0),
1750 m_nSwWidth(0),
1751 m_nPreferredWidth(0),
1752 m_nPercentWidth(0),
1753 m_bOk(true),
1754 m_bClaimLineFormat(false),
1755 m_eOri(text::HoriOrientation::LEFT),
1756 m_bIsBiDi(false),
1757 m_nCurrentRow(0),
1758 m_nCurrentBandRow(0),
1759 m_nCurrentCol(0),
1760 m_nRowsToRepeat(0),
1761 m_pTable(nullptr),
1762 m_pParentPos(nullptr),
1763 m_pFlyFormat(nullptr),
1764 m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>)
1765{
1767
1768 static const sal_Int16 aOriArr[] =
1769 {
1770 text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
1771 };
1772
1773 bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
1774 WW8_TablePos aTabPos;
1775
1776 WW8PLCFxSave1 aSave;
1777 m_pIo->m_xPlcxMan->GetPap()->Save( aSave );
1778
1779 WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF();
1780
1781 WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
1782
1783 wwSprmParser aSprmParser(m_pIo->GetFib());
1784
1785 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
1786
1787 // process pPap until end of table found
1788 do
1789 {
1790 short nTabeDxaNew = SHRT_MAX;
1791 bool bTabRowJustRead = false;
1792 const sal_uInt8* pShadeSprm = nullptr;
1793 const sal_uInt8* pNewShadeSprm[3] = {nullptr, nullptr, nullptr};
1794 const sal_uInt8* pTableBorders = nullptr;
1795 sal_uInt16 nTableBordersLen = 0;
1796 const sal_uInt8* pTableBorders90 = nullptr;
1797 sal_uInt16 nTableBorders90Len = 0;
1798 // params, len
1799 std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s;
1800 WW8_TablePos *pTabPos = nullptr;
1801
1802 // search end of a tab row
1803 if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
1804 {
1805 m_bOk = false;
1806 break;
1807 }
1808
1809 // Get the SPRM chains:
1810 // first from PAP and then from PCD (of the Piece Table)
1811 WW8PLCFxDesc aDesc;
1812 pPap->GetSprms( &aDesc );
1813 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
1814
1815 for (int nLoop = 0; nLoop < 2; ++nLoop)
1816 {
1817 const sal_uInt8* pParams;
1818 while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams()))
1819 {
1820 sal_uInt16 nId = aSprmIter.GetCurrentId();
1821 sal_Int32 nFixedLen = aSprmParser.DistanceToData(nId);
1822 sal_Int32 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen());
1823 sal_Int32 nLen = nL - nFixedLen;
1824 wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
1825 switch (eSprm)
1826 {
1827 case sprmTTableWidth:
1828 {
1829 const sal_uInt8 b0 = pParams[0];
1830 const sal_uInt8 b1 = pParams[1];
1831 const sal_uInt8 b2 = pParams[2];
1832 if (b0 == 3) // Twips
1833 m_nPreferredWidth = b2 * 0x100 + b1;
1834 else if (b0 == 2) // percent in fiftieths of a percent
1835 {
1836 m_nPercentWidth = (b2 * 0x100 + b1);
1837 // MS documentation: non-negative, and 600% max
1838 if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 )
1839 m_nPercentWidth *= .02;
1840 else
1841 m_nPercentWidth = 100;
1842 }
1843 }
1844 break;
1845 case sprmTTextFlow:
1846 pNewBand->ProcessDirection(pParams);
1847 break;
1848 case sprmTFCantSplit:
1849 pNewBand->bCantSplit = *pParams;
1850 m_bClaimLineFormat = true;
1851 break;
1852 case sprmTTableBorders:
1853 pTableBorders = pParams; // process at end
1854 nTableBordersLen = nLen;
1855 break;
1856 case sprmTTableBorders90:
1857 pTableBorders90 = pParams; // process at end
1858 nTableBorders90Len = nLen;
1859 break;
1860 case sprmTTableHeader:
1861 // tdf#105570
1862 if ( m_nRowsToRepeat == m_nRows )
1863 m_nRowsToRepeat = (m_nRows + 1);
1864 break;
1865 case sprmTJc:
1866 // sprmTJc - Justification Code
1867 if (m_nRows == 0)
1868 m_eOri = aOriArr[*pParams & 0x3];
1869 break;
1870 case sprmTFBiDi:
1871 m_bIsBiDi = SVBT16ToUInt16(pParams) != 0;
1872 break;
1873 case sprmTDxaGapHalf:
1874 pNewBand->nGapHalf = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1875 break;
1876 case sprmTDyaRowHeight:
1877 pNewBand->nLineHeight = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1878 m_bClaimLineFormat = true;
1879 break;
1880 case sprmTDefTable:
1881 pNewBand->ReadDef(bOldVer, pParams, nLen);
1882 bTabRowJustRead = true;
1883 break;
1884 case sprmTDefTableShd:
1885 pShadeSprm = pParams;
1886 break;
1887 case sprmTDefTableNewShd:
1888 pNewShadeSprm[0] = pParams;
1889 break;
1890 case sprmTDefTableNewShd2nd:
1891 pNewShadeSprm[1] = pParams;
1892 break;
1893 case sprmTDefTableNewShd3rd:
1894 pNewShadeSprm[2] = pParams;
1895 break;
1896 case sprmTDxaLeft:
1897 // our Writer cannot shift single table lines
1898 // horizontally so we have to find the smallest
1899 // parameter (meaning the left-most position) and then
1900 // shift the whole table to that margin (see below)
1901 {
1902 short nDxaNew = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1903 if( nDxaNew < nTabeDxaNew )
1904 nTabeDxaNew = nDxaNew;
1905 }
1906 break;
1907 case sprmTSetBrc:
1908 aTSetBrcs.emplace_back(pParams, nLen); // process at end
1909 break;
1910 case sprmTSetBrc90:
1911 aTSetBrc90s.emplace_back(pParams, nLen); // process at end
1912 break;
1913 case sprmTDxaCol:
1914 pNewBand->ProcessSprmTDxaCol(pParams);
1915 break;
1916 case sprmTInsert:
1917 pNewBand->ProcessSprmTInsert(pParams);
1918 break;
1919 case sprmTDelete:
1920 pNewBand->ProcessSprmTDelete(pParams);
1921 break;
1922 case sprmTCellPaddingDefault:
1923 pNewBand->ProcessSpacing(pParams);
1924 break;
1925 case sprmTCellPadding:
1926 pNewBand->ProcessSpecificSpacing(pParams);
1927 break;
1928 default:
1929 ;
1930 }
1931 aSprmIter.advance();
1932 }
1933
1934 if( !nLoop )
1935 {
1936 pPap->GetPCDSprms( aDesc );
1937 aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
1938 }
1939 }
1940
1941 // WW-Tables can contain Fly-changes. For this abort tables here
1942 // and start again. *pPap is still before TabRowEnd, so TestApo()
1943 // can be called with the last parameter set to false and therefore
1944 // take effect.
1945
1946 if (bTabRowJustRead)
1947 {
1948 // Some SPRMs need to be processed *after* ReadDef is called
1949 // so they were saved up until here
1950 if (pShadeSprm)
1951 pNewBand->ReadShd(pShadeSprm);
1952 if (pNewShadeSprm[0])
1953 pNewBand->ReadNewShd(pNewShadeSprm[0], bOldVer, /*nStart=*/0);
1954 if (pNewShadeSprm[1])
1955 pNewBand->ReadNewShd(pNewShadeSprm[1], bOldVer, /*nStart=*/22);
1956 if (pNewShadeSprm[2])
1957 pNewBand->ReadNewShd(pNewShadeSprm[2], bOldVer, /*nStart=*/44);
1958 if (pTableBorders90)
1959 pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len);
1960 else if (pTableBorders)
1961 pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
1962 pTableBorders, nTableBordersLen);
1963 for (const auto& a : aTSetBrcs)
1964 pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second);
1965 for (const auto& a : aTSetBrc90s)
1966 pNewBand->ProcessSprmTSetBRC(9, a.first, a.second);
1967 }
1968
1969 if( nTabeDxaNew < SHRT_MAX )
1970 {
1971 short* pCenter = pNewBand->nCenter;
1972 short firstDxaCenter = *pCenter;
1973 for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
1974 {
1975 // #i30298# Use sprmTDxaLeft to adjust the left indent
1976 // #i40461# Use dxaGapHalf during calculation
1977 *pCenter +=
1978 (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
1979 }
1980 }
1981
1982 if (!m_pActBand)
1983 m_pActBand = m_pFirstBand = pNewBand;
1984 else
1985 {
1986 m_pActBand->pNextBand = pNewBand;
1987 m_pActBand = pNewBand;
1988 }
1989 m_nBands++;
1990
1991 pNewBand = new WW8TabBandDesc;
1992
1993 m_nRows++;
1994 m_pActBand->nRows++;
1995
1996 //Seek our pap to its next block of properties
1997 WW8PLCFxDesc aRes;
1998 aRes.pMemPos = nullptr;
1999 aRes.nStartPos = nStartCp;
2000
2001 if (!(pPap->SeekPos(aRes.nStartPos)))
2002 {
2003 aRes.nEndPos = WW8_CP_MAX;
2004 pPap->SetDirty(true);
2005 }
2006 pPap->GetSprms(&aRes);
2007 pPap->SetDirty(false);
2008
2009 //Are we at the end of available properties
2010 if (
2011 !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
2012 aRes.nStartPos == WW8_CP_MAX
2013 )
2014 {
2015 m_bOk = false;
2016 break;
2017 }
2018
2019 //Are we still in a table cell
2020 SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer);
2021 const sal_uInt8* pParams = aParamsRes.pSprm;
2022 SprmResult aLevelRes = pPap->HasSprm(0x6649);
2023 const sal_uInt8 *pLevel = aLevelRes.pSprm;
2024 // InTable
2025 if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) ||
2026 (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable)))
2027 {
2028 break;
2029 }
2030
2031 //Get the end of row new table positioning data
2032 WW8_CP nMyStartCp=nStartCp;
2033 if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
2034 if (SwWW8ImplReader::ParseTabPos(&aTabPos, pPap))
2035 pTabPos = &aTabPos;
2036
2037 //Move back to this cell
2038 aRes.pMemPos = nullptr;
2039 aRes.nStartPos = nStartCp;
2040
2041 // PlcxMan currently points too far ahead so we need to bring
2042 // it back to where we are trying to make a table
2043 m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
2044 m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
2045 if (!(pPap->SeekPos(aRes.nStartPos)))
2046 {
2047 aRes.nEndPos = WW8_CP_MAX;
2048 pPap->SetDirty(true);
2049 }
2050 pPap->GetSprms(&aRes);
2051 pPap->SetDirty(false);
2052
2053 //Does this row match up with the last row closely enough to be
2054 //considered part of the same table
2055 ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
2056
2057 /*
2058 ##513##, #79474# If this is not sufficient, then we should look at
2059 sprmPD{y|x}aAbs as our indicator that the following set of rows is not
2060 part of this table, but instead is an absolutely positioned table
2061 outside of this one
2062 */
2063 if (aApo.mbStopApo)
2064 break;
2065 if (aApo.mbStartApo)
2066 {
2067 //if there really is a fly here, and not a "null" fly then break.
2068 if (m_pIo->ConstructApo(aApo, pTabPos))
2069 break;
2070 }
2071
2072 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
2073 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
2074 {
2075 SAL_WARN("sw.ww8", "WW8TabDesc, loop in paragraph property chain");
2076 break;
2077 }
2078 nStartCp = aRes.nEndPos;
2079 }
2080 while(true);
2081
2082 if( m_bOk )
2083 {
2084 if( m_pActBand->nRows > 1 )
2085 {
2086 // last band has more than 1 cell
2087 delete pNewBand;
2088 pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
2089 m_pActBand->nRows--; // because of special treatment of border defaults
2090 pNewBand->nRows = 1;
2091 m_pActBand->pNextBand = pNewBand; // append at the end
2092 m_nBands++;
2093 pNewBand = nullptr; // do not delete
2094 }
2095 CalcDefaults();
2096 }
2097 delete pNewBand;
2098
2099 m_pIo->m_xPlcxMan->GetPap()->Restore( aSave );
2100}
2101
2103{
2105 while(pR)
2106 {
2107 WW8TabBandDesc* pR2 = pR->pNextBand;
2108 delete pR;
2109 pR = pR2;
2110 }
2111
2112 delete m_pParentPos;
2113}
2114
2116{
2117 short nMinCols = SHRT_MAX;
2118 WW8TabBandDesc* pR;
2119
2120 m_nMinLeft = SHRT_MAX;
2121 m_nMaxRight = SHRT_MIN;
2122
2123 /*
2124 If we are an honestly inline centered table, then the normal rules of
2125 engagement for left and right margins do not apply. The multiple rows are
2126 centered regardless of the actual placement of rows, so we cannot have
2127 mismatched rows as is possible in other configurations.
2128
2129 e.g. change the example bugdoc in word from text wrapping of none (inline)
2130 to around (in frame (bApo)) and the table splits into two very disjoint
2131 rows as the beginning point of each row are very different
2132 */
2133 if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
2134 {
2135 for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
2136 for( short i = pR->nWwCols; i >= 0; --i)
2137 pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
2138 }
2139
2140 // First loop: find outermost L and R borders
2141 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2142 {
2143 if( pR->nCenter[0] < m_nMinLeft )
2144 m_nMinLeft = pR->nCenter[0];
2145
2146 // Following adjustment moves a border and then uses it to find width
2147 // of next cell, so collect current widths, to avoid situation when width
2148 // adjustment to too narrow cell makes next cell have negative width
2149 short nOrigWidth[MAX_COL + 1];
2150 for( short i = 0; i < pR->nWwCols; i++ )
2151 {
2152 nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
2153 }
2154
2155 for( short i = 0; i < pR->nWwCols; i++ )
2156 {
2157 /*
2158 If the margins are so large as to make the displayable
2159 area inside them smaller than the minimum allowed then adjust the
2160 width to fit. But only do it if the two cells are not the exact
2161 same value, if they are then the cell does not really exist and will
2162 be blended together into the same cell through the use of the
2163 nTrans(late) array.
2164 #i28333# If the nGapHalf is greater than the cell width best to ignore it
2165 */
2166 int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
2167 if (nCellWidth != nOrigWidth[i])
2168 {
2169 if (nOrigWidth[i] == 0)
2170 nCellWidth = 0; // restore zero-width "cell"
2171 else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
2172 nCellWidth = pR->nGapHalf + 1; // avoid false ignore
2173 else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
2174 nCellWidth = 1; // minimal non-zero width to minimize distortion
2175 }
2176 if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
2177 {
2178 nCellWidth = MINLAY + pR->nGapHalf * 2;
2179 }
2180 pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
2181 }
2182
2183 if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
2184 m_nMaxRight = pR->nCenter[pR->nWwCols];
2185 }
2187
2188 // If the table is right aligned we need to align all rows to the
2189 // row that has the furthest right point
2190
2191 if(m_eOri == text::HoriOrientation::RIGHT)
2192 {
2193 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2194 {
2195 int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
2196 for( short i = 0; i < pR->nWwCols + 1; i++ )
2197 {
2198 pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
2199 }
2200
2201 }
2202 }
2203
2204 // 2. pass: Detect number of writer columns. This can exceed the count
2205 // of columns in WW by 2, because SW in contrast to WW does not provide
2206 // fringed left and right borders and has to fill with empty boxes.
2207 // Non existent cells can reduce the number of columns.
2208
2209 // 3. pass: Replace border with defaults if needed
2210 for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
2211 {
2212 if( !pR->pTCs )
2213 {
2214 pR->pTCs = new WW8_TCell[ pR->nWwCols ];
2215 }
2216 for (int k = 0; k < pR->nWwCols; ++k)
2217 {
2218 WW8_TCell& rT = pR->pTCs[k];
2219 for (int i = 0; i < 4; ++i)
2220 {
2221 if (rT.rgbrc[i].brcType()==0)
2222 {
2223 // if shadow is set, its invalid
2224 int j = i;
2225 switch( i )
2226 {
2227 case 0:
2228 // outer top / horizontally inside
2229 j = (pR == m_pFirstBand) ? 0 : 4;
2230 break;
2231 case 1:
2232 // outer left / vertically inside
2233 j = k ? 5 : 1;
2234 break;
2235 case 2:
2236 // outer bottom / horizontally inside
2237 j = pR->pNextBand ? 4 : 2;
2238 break;
2239 case 3:
2240 // outer right / vertically inside
2241 j = (k == pR->nWwCols - 1) ? 3 : 5;
2242 break;
2243 }
2244 // merge with above defaults
2245 rT.rgbrc[i] = pR->aDefBrcs[j];
2246 }
2247 }
2248 }
2249 }
2250
2251 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2252 {
2253 pR->nSwCols = pR->nWwCols;
2254 pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
2255 pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols]) >= MINLAY;
2256
2257 short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
2258 sal_uInt16 i;
2259 sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
2260 for (i = 0; i < pR->nWwCols; ++i)
2261 {
2262 pR->nTransCell[i] = static_cast<sal_Int8>(j);
2263 if ( pR->nCenter[i] < pR->nCenter[i+1] )
2264 {
2265 pR->bExist[i] = true;
2266 j++;
2267 }
2268 else
2269 {
2270 pR->bExist[i] = false;
2271 nAddCols--;
2272 }
2273 }
2274
2275 OSL_ENSURE(i,"no columns in row ?");
2276
2277 /*
2278 If the last cell was "false" then there is no valid cell following it,
2279 so the default mapping forward won't work. So map it (and
2280 contiguous invalid cells backwards to the last valid cell instead.)
2281 */
2282 if (i && !pR->bExist[i-1])
2283 {
2284 sal_uInt16 k=i-1;
2285 while (k && !pR->bExist[k])
2286 k--;
2287 for (sal_uInt16 n=k+1;n<i;n++)
2288 pR->nTransCell[n] = pR->nTransCell[k];
2289 }
2290
2291 pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other
2292 pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol
2293
2294 pR->nSwCols = pR->nSwCols + nAddCols;
2295 if( pR->nSwCols < nMinCols )
2296 nMinCols = pR->nSwCols;
2297 }
2298
2299 if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
2300 (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
2301 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
2302
2303 m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
2304 if( m_nDefaultSwCols == 0 )
2305 m_bOk = false;
2308 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2309}
2310
2312{
2313 SwFrameFormat* pApply = pFrameFormat;
2314 if (!pApply )
2315 pApply = m_pTable->GetFrameFormat();
2316 OSL_ENSURE(pApply,"No frame");
2317 pApply->SetFormatAttr(m_aItemSet);
2318 if (pFrameFormat)
2319 {
2320 SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
2322 aSize.SetHeight(MINLAY);
2323 pFrameFormat->SetFormatAttr(aSize);
2324 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
2325 }
2326}
2327
2329 const SwNode &rNode)
2330{
2331 OSL_ENSURE(!maSegments.empty(),
2332 "should not be possible, must be at least one segment");
2333 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
2334 maSegments.back().maStart.Assign(rNode);
2335}
2336
2338{
2340
2341 // if there is already some content on the Node append new node to ensure
2342 // that this content remains ABOVE the table
2343 SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
2344 bool bInsNode = pPoint->GetContentIndex() != 0;
2345 bool bSetMinHeight = false;
2346
2347 /*
2348 #i8062#
2349 Set fly anchor to its anchor pos, so that if a table starts immediately
2350 at this position a new node will be inserted before inserting the table.
2351 */
2352 SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo)
2353 ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr;
2354 if (pFormat)
2355 {
2356 const SwNode* pAnchorNode =
2357 pFormat->GetAnchor().GetAnchorNode();
2358 if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
2359 {
2360 bInsNode = true;
2361 bSetMinHeight = true;
2362
2363 SwFormatSurround aSur(pFormat->GetSurround());
2364 aSur.SetAnchorOnly(true);
2365 pFormat->SetFormatAttr(aSur);
2366 }
2367 }
2368
2369 if (bSetMinHeight)
2370 {
2371 // minimize Fontsize to minimize height growth of the header/footer
2372 // set font size to 1 point to minimize y-growth of Hd/Ft
2374 m_pIo->NewAttr( aSz );
2375 m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
2376 }
2377
2378 if (bInsNode)
2379 m_pIo->AppendTextNode(*pPoint);
2380
2382
2383 // Because SW cannot handle multi-page floating frames,
2384 // _any unnecessary_ floating tables have been converted to inline.
2385 tools::Long nLeft = 0;
2386 if (m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->GetFlyFormat())
2387 {
2388 // Get the table orientation from the fly
2389 // Do we also need to check m_pIo->m_xSFlyPara->bTogglePos/IsPosToggle()? [Probably not - layout-only concern]
2390 const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos;
2391 const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT
2392 : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT;
2393 if ( bIsInsideMargin && bAdjustMargin )
2394 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH;
2395 else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE )
2396 m_eOri = m_pIo->m_xSFlyPara->eHAlign;
2397 if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH )
2398 {
2399 nLeft = m_pIo->m_xSFlyPara->nXPos;
2400 if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME )
2401 {
2402 if ( !m_bIsBiDi )
2404 else
2406 }
2407 }
2408 }
2409
2410 // The table is small: The number of columns is the lowest count of
2411 // columns of the origin, because inserting is faster than deleting.
2412 // The number of rows is the count of bands because (identically)
2413 // rows of a band can be duplicated easy.
2416 *m_xTmpPos->GetPoint(), m_nBands, m_nDefaultSwCols, m_eOri );
2417
2418 OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
2419 if (!m_pTable || !m_pTable->GetFrameFormat())
2420 return;
2421
2422 SwTableNode* pTableNode = m_pTable->GetTableNode();
2423 OSL_ENSURE(pTableNode, "no table node!");
2424 if (pTableNode)
2425 {
2427 *pTableNode);
2428 }
2429
2430 // Check if the node into which the table should be inserted already
2431 // contains a Pagedesc. If so that Pagedesc would be moved to the
2432 // row after the table, that would be wrong. So delete and
2433 // set later to the table format.
2434 if (SwTextNode *const pNd = m_xTmpPos->GetPoint()->GetNode().GetTextNode())
2435 {
2436 if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
2437 {
2438 std::unique_ptr<SfxPoolItem> pSetAttr;
2439
2440 if (const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet(RES_BREAK, false))
2441 {
2442 pSetAttr.reset(new SvxFormatBreakItem( *pBreakItem ));
2443 pNd->ResetAttr( RES_BREAK );
2444 }
2445
2446 // eventually set the PageDesc/Break now to the table
2447 if (pSetAttr)
2448 {
2449 m_aItemSet.Put(std::move(pSetAttr));
2450 }
2451 }
2452 }
2453
2454 // total width of table
2456 {
2458 // Don't set relative width if the table is in a floating frame
2459 if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->GetFlyFormat()) )
2460 aFrameSize.SetWidthPercent(m_nPercentWidth);
2461 m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize);
2462 m_aItemSet.Put(aFrameSize);
2463 }
2464
2466 m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
2468
2469 if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
2470 {
2472 m_pIo->m_xSFlyPara->GetFlyFormat() && GetMinLeft())
2473 {
2474 //If we are inside a frame and we have a border, the frames
2475 //placement does not consider the tables border, which word
2476 //displays outside the frame, so adjust here.
2477 SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->GetFlyFormat()->GetHoriOrient());
2478 sal_Int16 eHori = aHori.GetHoriOrient();
2479 if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
2480 (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
2481 {
2482 //With multiple table, use last table settings. Perhaps
2483 //the maximum is what word does ?
2484 aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft());
2486 m_pIo->m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aHori);
2487 }
2488 }
2489 else // Not directly in a floating frame.
2490 {
2491 //Historical note: If InLocalApo(), then this table is being placed in a floating
2492 //frame, and the frame matches the left and right *lines* of the
2493 //table, so the space to the left of the table isn't to be used
2494 //inside the frame, in word the dialog involved greys out the
2495 //ability to set the margin.
2497
2498 if (!m_bIsBiDi)
2499 nLeft += GetMinLeft();
2500 else
2501 {
2502 const short nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth;
2504 nLeft = nLeft - nTableWidth - GetMinLeft();
2505 }
2506 aL.SetLeft(nLeft);
2507
2508 m_aItemSet.Put(aL);
2509 }
2510 }
2511
2514}
2515
2517{
2518 // init global Vars
2521
2522 m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
2523 GetSttNd()->FindTableNode());
2524 OSL_ENSURE( m_pTableNd, "Where is my table node" );
2525
2526 // #i69519# - Restrict rows to repeat to a decent value
2527 if ( m_nRowsToRepeat == o3tl::narrowing<sal_uInt16>(m_nRows) )
2528 m_nRowsToRepeat = 1;
2529
2531 // insert extra cells if needed and something like this
2532 AdjustNewBand();
2533
2535 m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
2536
2537 // now set the correct PaM and prepare first merger group if any
2539 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2540
2541 m_pIo->m_bWasTabRowEnd = false;
2542 m_pIo->m_bWasTabCellEnd = false;
2543}
2544
2546{
2547 short nRow;
2548
2550 {
2551 // insert current box into merge group if appropriate.
2552 // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
2553 if( m_pActBand->pTCs )
2554 {
2555 for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
2556 for( short i = 0; i < m_pActBand->nWwCols; i++ )
2557 {
2558 WW8SelBoxInfo* pActMGroup = nullptr;
2559
2560 // start a new merge group if appropriate
2561
2562 OSL_ENSURE(nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()),
2563 "Too few lines, table ended early");
2564 if (nRow >= o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2565 return;
2566 m_pTabLine = (*m_pTabLines)[ nRow ];
2568
2569 sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
2570 if (!m_pActBand->bExist[i])
2571 continue;
2572 OSL_ENSURE(nCol < m_pTabBoxes->size(),
2573 "Too few columns, table ended early");
2574 if (nCol >= m_pTabBoxes->size())
2575 return;
2576 m_pTabBox = (*m_pTabBoxes)[nCol];
2577 WW8_TCell& rCell = m_pActBand->pTCs[ i ];
2578 // is this the left upper cell of a merge group ?
2579
2580 bool bMerge = false;
2581 if ( rCell.bVertRestart && !rCell.bMerged )
2582 bMerge = true;
2583 else if (rCell.bFirstMerged && m_pActBand->bExist[i])
2584 {
2585 // Some tests to avoid merging cells which previously were
2586 // declared invalid because of sharing the exact same dimensions
2587 // as their previous cell
2588
2589 //If there's anything underneath/above we're ok.
2590 if (rCell.bVertMerge || rCell.bVertRestart)
2591 bMerge = true;
2592 else
2593 {
2594 //If it's a hori merge only, and the only things in
2595 //it are invalid cells then it's already taken care
2596 //of, so don't merge.
2597 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2598 if (m_pActBand->pTCs[ i2 ].bMerged &&
2599 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2600 {
2601 if (m_pActBand->bExist[i2])
2602 {
2603 bMerge = true;
2604 break;
2605 }
2606 }
2607 else
2608 break;
2609 }
2610 }
2611
2612 // remove numbering from cells that will be disabled in the merge
2613 if( rCell.bVertMerge && !rCell.bVertRestart )
2614 {
2615 SwPaM aPam( *m_pTabBox->GetSttNd(), 0 );
2616 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2617 SwTextNode* pNd = aPam.GetPointNode().GetTextNode();
2618 while( pNd )
2619 {
2620 pNd->SetCountedInList( false );
2621
2622 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2623 pNd = aPam.GetPointNode().GetTextNode();
2624 }
2625 }
2626
2627 if (bMerge)
2628 {
2629 short nX1 = m_pActBand->nCenter[ i ];
2630 short nWidth = m_pActBand->nWidth[ i ];
2631
2632 // 2. create current merge group
2633 pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
2634
2635 // determine size of new merge group
2636 // before inserted the new merge group.
2637 // Needed to correctly locked previously created merge groups.
2638 // Calculate total width and set
2639 short nSizCell = m_pActBand->nWidth[ i ];
2640 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2641 if (m_pActBand->pTCs[ i2 ].bMerged &&
2642 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2643 {
2644 nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
2645 }
2646 else
2647 break;
2648 pActMGroup->m_nGroupWidth = nSizCell;
2649
2650 // locked previously created merge groups,
2651 // after determining the size for the new merge group.
2652 // 1. If necessary close old merge group(s) that overlap
2653 // the X-area of the new group
2654 for (;;)
2655 {
2657 nX1, pActMGroup->m_nGroupWidth, false );
2658 if (p == nullptr)
2659 {
2660 break;
2661 }
2662 p->m_bGroupLocked = true;
2663 }
2664
2665 // 3. push to group array
2666 m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
2667 }
2668
2669 // if necessary add the current box to a merge group
2670 // (that can be a newly created or another group)
2671 UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
2672 }
2673 }
2674 }
2675}
2676
2677//There is a limbo area in word at the end of the row marker
2678//where properties can live in word, there is no location in
2679//writer equivalent, so try and park the cursor in the best
2680//match, see #i23022#/#i18644#
2682{
2683 SwTableBox *pTabBox2 = nullptr;
2684 short nRow = m_nCurrentRow + 1;
2685 if (nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2686 {
2687 if (SwTableLine *pLine = (*m_pTabLines)[nRow])
2688 {
2689 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
2690 pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
2691 }
2692 }
2693
2694 if (!pTabBox2 || !pTabBox2->GetSttNd())
2695 {
2697 return;
2698 }
2699
2700 SwNodeOffset nSttNd = pTabBox2->GetSttIdx() + 1,
2701 nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
2702
2703 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2704 {
2705 do
2706 {
2707 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2708 }
2709 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2710
2713 }
2714}
2715
2717{
2718 OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored");
2719 if (m_xTmpPos && m_pIo)
2720 *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos->GetPoint();
2721}
2722
2724{
2725 m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
2726
2727 // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2728 // place, or somewhere close if that place got destroyed
2729 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_oLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_oLastAnchorPos) : nullptr);
2730 m_pIo->m_oLastAnchorPos.reset();
2731
2732 SwTableNode* pTableNode = m_pTable->GetTableNode();
2733 SwDeleteListener aListener(*pTableNode);
2735
2736 if (xLastAnchorCursor)
2737 m_pIo->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2738
2740 m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
2741
2743 m_xTmpPos.reset();
2744
2745 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2746
2747 m_pIo->m_bWasTabRowEnd = false;
2748 m_pIo->m_bWasTabCellEnd = false;
2749
2751
2752 if (aListener.WasDeleted())
2753 throw std::runtime_error("table unexpectedly destroyed by applying redlines");
2754
2755 MergeCells();
2756
2757 // if needed group cells together that should be merged
2758 if (m_MergeGroups.empty())
2759 return;
2760
2761 // process all merge groups one by one
2762 for (auto const& groupIt : m_MergeGroups)
2763 {
2764 if((1 < groupIt->size()) && groupIt->row(0)[0])
2765 {
2766 SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
2767 pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->m_nGroupWidth, 0));
2768 const sal_uInt16 nRowSpan = groupIt->rowsCount();
2769 for (sal_uInt16 n = 0; n < nRowSpan; ++n)
2770 {
2771 auto& rRow = groupIt->row(n);
2772 for (size_t i = 0; i<rRow.size(); ++i)
2773 {
2774 const sal_Int32 nRowSpanSet = (n == 0) && (i == 0) ?
2775 nRowSpan :
2776 (-1 * (nRowSpan - n));
2777 SwTableBox* pCurrentBox = rRow[i];
2778 pCurrentBox->setRowSpan(nRowSpanSet);
2779
2780 if (i == 0)
2781 pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
2782 else
2783 {
2784 SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
2786 }
2787 }
2788 }
2789 }
2790 }
2792 m_MergeGroups.clear();
2793}
2794
2795// browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
2796
2797// Parameter: nXcenter = center position of asking box
2798// nWidth = width of asking box
2799// bExact = flag, if box has to fit into group
2800// or only has to touch
2801
2802WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
2803{
2804 if (!m_MergeGroups.empty())
2805 {
2806 // still valid area near the boundary
2807 const short nTolerance = 4;
2808 // box boundary
2809 short nX2 = nX1 + nWidth;
2810 // approximate group boundary
2811 short nGrX1;
2812 short nGrX2;
2813
2814 // improvement: search backwards
2815 for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
2816 {
2817 // the currently inspected group
2818 WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
2819 if (!rActGroup.m_bGroupLocked)
2820 {
2821 // approximate group boundary with room (tolerance) to the *outside*
2822 nGrX1 = rActGroup.m_nGroupXStart - nTolerance;
2823 nGrX2 = rActGroup.m_nGroupXStart
2824 + rActGroup.m_nGroupWidth + nTolerance;
2825
2826 // If box fits report success
2827
2828 if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
2829 {
2830 return &rActGroup;
2831 }
2832
2833 // does the box share areas with the group?
2834
2835 if( !bExact )
2836 {
2837 // successful if nX1 *or* nX2 are inside the group
2838 if( ( ( nX1 > nGrX1 )
2839 && ( nX1 < nGrX2 - 2*nTolerance ) )
2840 || ( ( nX2 > nGrX1 + 2*nTolerance )
2841 && ( nX2 < nGrX2 ) )
2842 // or nX1 and nX2 surround the group
2843 || ( ( nX1 <=nGrX1 )
2844 && ( nX2 >=nGrX2 ) ) )
2845 {
2846 return &rActGroup;
2847 }
2848 }
2849 }
2850 }
2851 }
2852 return nullptr;
2853}
2854
2855bool WW8TabDesc::IsValidCell(short nCol) const
2856{
2858 m_pActBand->bExist[nCol] &&
2860}
2861
2863{
2864 //e.g. #i19718#
2865 if (!m_pTabBox || !m_pTabBox->GetSttNd())
2866 {
2867 OSL_FAIL("Problem with table");
2868 return false;
2869 }
2870
2871 if (!IsValidCell(GetCurrentCol()))
2872 return false;
2873
2874 return m_pIo->m_pPaM->GetPoint()->GetNodeIndex() == m_pTabBox->GetSttIdx() + 1;
2875}
2876
2877void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
2878{
2879 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2880 if (!m_pActBand)
2881 return;
2882
2883 sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
2884
2886 {
2887 OSL_ENSURE(false, "Actual row bigger than expected." );
2888 if (bPam)
2890 return;
2891 }
2892
2893 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
2895
2896 if (nCol >= m_pTabBoxes->size())
2897 {
2898 if (bPam)
2899 {
2900 // The first paragraph in a cell with upper autospacing has upper
2901 // spacing set to 0
2902 if (
2904 !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing
2905 )
2906 {
2908 }
2909
2910 // The last paragraph in a cell with lower autospacing has lower
2911 // spacing set to 0
2912 if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2914
2915 ParkPaM();
2916 }
2917 return;
2918 }
2919 m_pTabBox = (*m_pTabBoxes)[nCol];
2920 if( !m_pTabBox->GetSttNd() )
2921 {
2922 OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table");
2923 if (bPam)
2925 return;
2926 }
2927 if (!bPam)
2928 return;
2929
2930 m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ];
2931
2932 // The first paragraph in a cell with upper autospacing has upper spacing set to 0
2933 if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2935
2936 // The last paragraph in a cell with lower autospacing has lower spacing set to 0
2937 if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2939
2940 //We need to set the pPaM on the first cell, invalid
2941 //or not so that we can collect paragraph properties over
2942 //all the cells, but in that case on the valid cell we do not
2943 //want to reset the fmt properties
2944 SwNodeOffset nSttNd = m_pTabBox->GetSttIdx() + 1,
2945 nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
2946 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2947 {
2948 do
2949 {
2950 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2951 }
2952 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2954 // Precautionally set now, otherwise the style is not set for cells
2955 // that are inserted for margin balancing.
2957 // because this cells are invisible helper constructions only to simulate
2958 // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
2959 }
2960
2961 // Better to turn Snap to Grid off for all paragraphs in tables
2962 SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
2963 SwTextNode *pNd = pGridPos->GetNode().GetTextNode();
2964 if(!pNd)
2965 return;
2966
2967 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
2968 const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
2969
2970 if(!rSnapToGrid.GetValue())
2971 return;
2972
2973 SvxParaGridItem aGridItem( rSnapToGrid );
2974 aGridItem.SetValue(false);
2975
2976 const sal_Int32 nEnd = pGridPos->GetContentIndex();
2977 pGridPos->SetContent(0);
2978 m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem);
2979 pGridPos->SetContent(nEnd);
2980 m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
2981}
2982
2983void WW8TabDesc::InsertCells( short nIns )
2984{
2985 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
2987 m_pTabBox = (*m_pTabBoxes)[0];
2988
2990 const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
2991 // The third parameter contains the FrameFormat of the boxes.
2992 // Here it is possible to optimize to save (reduce) FrameFormats.
2993}
2994
2995void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
2996{
2997 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
2998 return; // faked cells -> no border
2999
3000 SvxBoxItem aFormatBox( RES_BOX );
3001 if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
3002 {
3003 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3005 SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
3006 }
3007
3009 {
3010 aFormatBox.SetDistance(
3012 SvxBoxItemLine::TOP);
3013 }
3014 else
3015 aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
3017 {
3018 aFormatBox.SetDistance(
3020 SvxBoxItemLine::BOTTOM);
3021 }
3022 else
3023 aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
3024
3025 // nGapHalf for WW is a *horizontal* gap between table cell and content.
3026 short nLeftDist =
3028 short nRightDist =
3031 {
3032 aFormatBox.SetDistance(
3034 SvxBoxItemLine::LEFT);
3035 }
3036 else
3037 aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
3039 {
3040 aFormatBox.SetDistance(
3042 SvxBoxItemLine::RIGHT);
3043 }
3044 else
3045 aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
3046
3047 pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
3048}
3049
3050void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
3051{
3052 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3053 return; // faked cells -> no color
3054
3055 bool bFound=false;
3056 if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
3057 {
3058 Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
3060 bFound = true;
3061 }
3062
3063 //If there was no new shades, or no new shade setting
3064 if (m_pActBand->pSHDs && !bFound)
3065 {
3066 WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
3067 if (!rSHD.GetValue()) // auto
3068 return;
3069
3070 SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
3072 }
3073}
3074
3075static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
3076{
3077 SvxFrameDirection eDir = SvxFrameDirection::Environment;
3078 // 1: Asian layout with rotated CJK characters
3079 // 5: Asian layout
3080 // 3: Western layout rotated by 90 degrees
3081 // 4: Western layout
3082 switch (nCode)
3083 {
3084 default:
3085 OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield");
3086 [[fallthrough]];
3087 case 3:
3088 eDir = SvxFrameDirection::Vertical_LR_BT;
3089 break;
3090 case 5:
3091 eDir = SvxFrameDirection::Vertical_RL_TB;
3092 break;
3093 case 1:
3094 eDir = SvxFrameDirection::Vertical_RL_TB;
3095 break;
3096 case 4:
3097 eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables
3098 break;
3099 }
3100 return eDir;
3101}
3102
3104{
3105 if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
3106 return;
3108 pBox->GetFrameFormat()->SetFormatAttr(aItem);
3109}
3110
3111void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
3112{
3113 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3114 return;
3115
3116 sal_Int16 eVertOri=text::VertOrientation::TOP;
3117
3118 if( m_pActBand->pTCs )
3119 {
3120 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3121 switch (pT->nVertAlign)
3122 {
3123 case 0:
3124 default:
3125 eVertOri = text::VertOrientation::TOP;
3126 break;
3127 case 1:
3128 eVertOri = text::VertOrientation::CENTER;
3129 break;
3130 case 2:
3131 eVertOri = text::VertOrientation::BOTTOM;
3132 break;
3133 }
3134 }
3135
3136 pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
3137}
3138
3140{
3141 if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
3143
3144 SetPamInCell( 0, false);
3145 OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == o3tl::narrowing<sal_uInt16>(m_pActBand->nSwCols),
3146 "Wrong column count in table" );
3147
3148 if( m_bClaimLineFormat )
3149 {
3150 m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
3151 SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default
3152
3153 if (m_pActBand->nLineHeight == 0) // 0 = Auto
3155 else
3156 {
3157 if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
3158 {
3161 }
3162 if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
3164
3165 aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
3166 }
3168 }
3169
3170 //Word stores 1 for bCantSplit if the row cannot be split, we set true if
3171 //we can split the row
3172 bool bSetCantSplit = m_pActBand->bCantSplit;
3174
3175 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
3176 if (bSetCantSplit && m_pTabLines->size() == 1)
3178
3179 short i; // SW-Index
3180 short j; // WW-Index
3181 short nW; // Width
3183 j = m_pActBand->bLEmptyCol ? -1 : 0;
3184
3185 for( i = 0; i < m_pActBand->nSwCols; i++ )
3186 {
3187 // set cell width
3188 if( j < 0 )
3189 nW = m_pActBand->nCenter[0] - m_nMinLeft;
3190 else
3191 {
3192 //Set j to first non invalid cell
3193 while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
3194 j++;
3195
3196 if( j < m_pActBand->nWwCols )
3197 nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3198 else
3199 nW = m_nMaxRight - m_pActBand->nCenter[j];
3200 m_pActBand->nWidth[ j ] = nW;
3201 }
3202
3203 SwTableBox* pBox = (*m_pTabBoxes)[i];
3204 // could be reduced further by intelligent moving of FrameFormats
3205 pBox->ClaimFrameFormat();
3206
3207 SetTabBorders(pBox, j);
3208
3209 SvxBoxItem aCurrentBox(pBox->GetFrameFormat()->GetFormatAttr(RES_BOX));
3210 pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
3211
3212 SetTabVertAlign(pBox, j);
3213 SetTabDirection(pBox, j);
3215 SetTabShades(pBox, j);
3216 j++;
3217
3218 aFS.SetWidth( nW );
3219 pBox->GetFrameFormat()->SetFormatAttr( aFS );
3220
3221 // skip non existing cells
3222 while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
3223 {
3225 j++;
3226 }
3227 }
3228}
3229
3231{
3233
3234 // new line/row
3235 if( m_pIo->m_bWasTabRowEnd )
3236 {
3237 // bWasTabRowEnd will be deactivated in
3238 // SwWW8ImplReader::ProcessSpecial()
3239
3240 sal_uInt16 iCol = GetLogicalWWCol();
3241 if (iCol < m_aNumRuleNames.size())
3242 {
3243 m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
3244 m_aNumRuleNames.end());
3245 }
3246
3247 m_nCurrentCol = 0;
3248 m_nCurrentRow++;
3250 OSL_ENSURE( m_pActBand , "pActBand is 0" );
3251 if( m_pActBand )
3252 {
3253 if( m_nCurrentRow >= m_nRows ) // nothing to at end of table
3254 return;
3255
3256 bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows;
3257 if( bNewBand )
3258 { // new band needed ?
3261 OSL_ENSURE( m_pActBand, "pActBand is 0" );
3262 AdjustNewBand();
3263 }
3264 else
3265 {
3266 SwTableBox* pBox = (*m_pTabBoxes)[0];
3267 SwSelBoxes aBoxes;
3268 m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
3269 }
3270 }
3271 }
3272 else
3273 { // new column ( cell )
3274 m_nCurrentCol++;
3275 }
3277
3278 // finish Annotated Level Numbering ?
3281}
3282
3283// if necessary register the box for the merge group for this column
3285 WW8SelBoxInfo* pActGroup,
3286 SwTableBox* pActBox,
3287 sal_uInt16 nCol )
3288{
3289 // check if the box has to be merged
3290 // If cell is the first one to be merged, a new merge group has to be provided.
3291 // E.g., it could be that a cell is the first one to be merged, but no
3292 // new merge group is provided, because the potential other cell to be merged
3293 // doesn't exist - see method <WW8TabDesc::MergeCells>.
3294 if ( !(m_pActBand->bExist[ nCol ] &&
3295 ( ( rCell.bFirstMerged && pActGroup ) ||
3296 rCell.bMerged ||
3297 rCell.bVertMerge ||
3298 rCell.bVertRestart )) )
3299 return;
3300
3301 // detect appropriate merge group
3302 WW8SelBoxInfo* pTheMergeGroup = nullptr;
3303 if( pActGroup )
3304 // assign group
3305 pTheMergeGroup = pActGroup;
3306 else
3307 {
3308 // find group
3309 pTheMergeGroup = FindMergeGroup(
3310 m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
3311 }
3312 if( pTheMergeGroup )
3313 {
3314 // add current box to merge group
3315 pTheMergeGroup->push_back(pActBox);
3316 }
3317}
3318
3319sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
3320{
3321 sal_uInt16 nCol = 0;
3322 if( m_pActBand && m_pActBand->pTCs)
3323 {
3324 for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol )
3325 {
3326 if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
3327 ++nCol;
3328 }
3329 }
3330 return nCol;
3331}
3332
3333// find name of numrule valid for current WW-COL
3335{
3336 sal_uInt16 nCol = GetLogicalWWCol();
3337 if (nCol < m_aNumRuleNames.size())
3338 return m_aNumRuleNames[nCol];
3339 return OUString();
3340}
3341
3342void WW8TabDesc::SetNumRuleName( const OUString& rName )
3343{
3344 sal_uInt16 nCol = GetLogicalWWCol();
3345 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
3346 m_aNumRuleNames.emplace_back();
3347 m_aNumRuleNames[nCol] = rName;
3348}
3349
3351{
3352 // Entering a table so make sure the FirstPara flag gets set
3353 m_bFirstPara = true;
3354 // no recursive table, not with InsertFile in table or foot note
3355 if (m_bReadNoTable)
3356 return false;
3357
3358 if (m_xTableDesc)
3359 m_aTableStack.push(std::move(m_xTableDesc));
3360
3361 // #i33818# - determine absolute position object attributes,
3362 // if possible. It's needed for nested tables.
3363 std::unique_ptr<WW8FlyPara> pTableWFlyPara;
3364 WW8SwFlyPara* pTableSFlyPara( nullptr );
3365 // #i45301# - anchor nested table inside Writer fly frame
3366 // only at-character, if absolute position object attributes are available.
3367 // Thus, default anchor type is as-character anchored.
3368 RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR );
3369 if ( m_nInTable )
3370 {
3371 WW8_TablePos* pNestedTabPos( nullptr );
3372 WW8_TablePos aNestedTabPos;
3373 WW8PLCFxSave1 aSave;
3374 m_xPlcxMan->GetPap()->Save( aSave );
3375 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
3376 WW8_CP nMyStartCp = nStartCp;
3377 if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
3378 ParseTabPos( &aNestedTabPos, pPap ) )
3379 {
3380 pNestedTabPos = &aNestedTabPos;
3381 }
3382 m_xPlcxMan->GetPap()->Restore( aSave );
3383 if ( pNestedTabPos )
3384 {
3385 ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
3386 pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
3387 if ( pTableWFlyPara )
3388 {
3389 // <WW8SwFlyPara> constructor has changed - new 4th parameter
3390 // containing WW8 page top margin.
3391 pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
3395
3396 // #i45301# - anchor nested table Writer fly frame at-character
3397 eAnchor = RndStdIds::FLY_AT_CHAR;
3398 }
3399 }
3400 }
3401 // if first paragraph in table has break-before-page, transfer that setting to the table itself.
3402 else if( StyleExists(m_nCurrentColl) )
3403 {
3404 const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat;
3405 if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
3406 NewAttr( pStyleFormat->GetBreak() );
3407 }
3408
3409 m_xTableDesc.reset(new WW8TabDesc(this, nStartCp));
3410
3411 if( m_xTableDesc->Ok() )
3412 {
3413 int nNewInTable = m_nInTable + 1;
3414
3415 if ((eAnchor == RndStdIds::FLY_AT_CHAR)
3416 && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
3417 {
3418 m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
3420 // #i33818# - anchor the Writer fly frame for the nested table at-character.
3421 // #i45301#
3422 SwFormatAnchor aAnchor( eAnchor );
3423 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3424 aItemSet.Put( aAnchor );
3425 m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
3426 m_xTableDesc->m_pParentPos, &aItemSet);
3427 OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
3428 "Not the anchor type requested!" );
3429 MoveInsideFly(m_xTableDesc->m_pFlyFormat);
3430 }
3431 m_xTableDesc->CreateSwTable();
3432 if (m_xTableDesc->m_pFlyFormat)
3433 {
3434 m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat);
3435 // #i33818# - Use absolute position object attributes,
3436 // if existing, and apply them to the created Writer fly frame.
3437 if ( pTableWFlyPara && pTableSFlyPara )
3438 {
3439 WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false );
3440 SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
3441 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3442 aFlySet.Put( aAnchor );
3443 m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
3444 }
3445 else
3446 {
3447 SwFormatHoriOrient aHori =
3448 m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
3449 m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
3450 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) );
3451 }
3452 // #i33818# - The nested table doesn't have to leave
3453 // the table cell. Thus, the Writer fly frame has to follow the text flow.
3454 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
3455 }
3456 else
3457 m_xTableDesc->SetSizePosition(nullptr);
3458 m_xTableDesc->UseSwTable();
3459 }
3460 else
3461 PopTableDesc();
3462
3463 // #i33818#
3464 delete pTableSFlyPara;
3465
3466 return m_xTableDesc != nullptr;
3467}
3468
3470{
3471 if (m_nInTable && m_xTableDesc)
3472 m_xTableDesc->TableCellEnd();
3473
3474 m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
3475 m_bReadTable = false;
3476}
3477
3478void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
3479{
3480 if( ( nLen > 0 ) && ( *pData == 1 ) )
3481 m_bWasTabCellEnd = true;
3482}
3483
3484void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
3485{
3486 if( ( nLen > 0 ) && ( *pData == 1 ) )
3487 m_bWasTabRowEnd = true;
3488}
3489
3491{
3492 if (m_xTableDesc && m_xTableDesc->m_pFlyFormat)
3493 {
3494 MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos);
3495 }
3496
3497 m_xTableDesc.reset();
3498 if (!m_aTableStack.empty())
3499 {
3500 m_xTableDesc = std::move(m_aTableStack.top());
3501 m_aTableStack.pop();
3502 }
3503}
3504
3506{
3507 OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!");
3508 if (!m_xTableDesc)
3509 return;
3510
3511 // We are leaving a table so make sure the next paragraph doesn't think
3512 // it's the first paragraph
3513 m_bFirstPara = false;
3514
3515 m_xTableDesc->FinishSwTable();
3516 PopTableDesc();
3517
3518 m_bReadTable = true;
3519}
3520
3522{
3523 if( !m_xTableDesc )
3524 return false;
3525
3526 const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell();
3527
3528 return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() )
3529 || ( pCell
3530 && ( !pCell->bFirstMerged
3531 && ( pCell->bMerged
3532 || ( pCell->bVertMerge
3533 && !pCell->bVertRestart
3534 )
3535 )
3536 )
3537 );
3538}
3539
3540sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
3541{
3542 sal_uInt16 nRes = USHRT_MAX;
3543 if( !m_vColl.empty() )
3544 {
3545 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3546 if( m_vColl[ nI ].m_bValid
3547 && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
3548 nRes = nI;
3549 }
3550 return nRes;
3551}
3552
3553const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( std::u16string_view rName ) const
3554{
3555 SwFormat* pRet = nullptr;
3556 if( !m_vColl.empty() )
3557 {
3558 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3559 if( m_vColl[ nI ].m_bValid
3560 && (rName == m_vColl[ nI ].GetOrgWWName()) )
3561 {
3562 pRet = m_vColl[ nI ].m_pFormat;
3563 break;
3564 }
3565 }
3566 return pRet;
3567}
3568
3569
3571{
3572 if( !mpParaSprms || !mnSprmsLen )
3573 return SprmResult();
3574
3576}
3577
3578void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
3579{
3580 if (!nLen)
3581 return;
3582
3583 if( bPap )
3584 {
3585 mpParaSprms = pSprms; // for HasParaSprms()
3586 mnSprmsLen = nLen;
3587 }
3588
3589 WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
3590 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
3591 {
3592#ifdef DEBUGSPRMREADER
3593 fprintf(stderr, "id is %x\n", aIter.GetCurrentId());
3594#endif
3595 mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
3596 aSprmIter.advance();
3597 }
3598
3599 mpParaSprms = nullptr;
3600 mnSprmsLen = 0;
3601}
3602
3603void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
3604{
3605 if (!nLen)
3606 return;
3607
3608 if (checkSeek(*mpStStrm, nPosFc))
3609 {
3610 std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
3611 nLen = mpStStrm->ReadBytes(pSprms.get(), nLen);
3612 ImportSprms(pSprms.get(), nLen, bPap);
3613 }
3614}
3615
3616static short WW8SkipOdd(SvStream* pSt )
3617{
3618 if ( pSt->Tell() & 0x1 )
3619 {
3620 sal_uInt8 c;
3621 return pSt->ReadBytes( &c, 1 );
3622 }
3623 return 0;
3624}
3625
3626static short WW8SkipEven(SvStream* pSt )
3627{
3628 if (!(pSt->Tell() & 0x1))
3629 {
3630 sal_uInt8 c;
3631 return pSt->ReadBytes( &c, 1 );
3632 }
3633 return 0;
3634}
3635
3636short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
3637{
3638 if( 0 < nLen ) // Empty ?
3639 {
3640 if (bOdd)
3641 nLen = nLen - WW8SkipEven( mpStStrm );
3642 else
3643 nLen = nLen - WW8SkipOdd( mpStStrm );
3644
3645 sal_Int16 cbUPX(0);
3646 mpStStrm->ReadInt16( cbUPX );
3647
3648 nLen-=2;
3649
3650 if ( cbUPX > nLen )
3651 cbUPX = nLen; // shrink cbUPX to nLen
3652
3653 if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
3654 {
3655 if( bPAP )
3656 {
3657 sal_uInt16 id;
3658 mpStStrm->ReadUInt16( id );
3659
3660 cbUPX-= 2;
3661 nLen-= 2;
3662 }
3663
3664 if( 0 < cbUPX )
3665 {
3666 sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong,
3667 // this should make it work again
3668 ImportSprms( nPos, cbUPX, bPAP );
3669
3670 if ( mpStStrm->Tell() != nPos + cbUPX )
3671 mpStStrm->Seek( nPos+cbUPX );
3672
3673 nLen = nLen - cbUPX;
3674 }
3675 }
3676 }
3677 return nLen;
3678}
3679
3680void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
3681{
3682 if( nLen <= 0 )
3683 return;
3684 if (bOdd)
3685 nLen = nLen - WW8SkipEven( mpStStrm );
3686 else
3687 nLen = nLen - WW8SkipOdd( mpStStrm );
3688
3689 if( bPara ) // Grupx.Papx
3690 nLen = ImportUPX(nLen, true, bOdd);
3691 ImportUPX(nLen, false, bOdd); // Grupx.Chpx
3692}
3693
3695 : WW8Style(*pI->m_pTableStream, _rFib)
3696 , maSprmParser(_rFib)
3697 , mpIo(pI)
3698 , mpStStrm(pI->m_pTableStream)
3699 , mpStyRule(nullptr)
3700 , mpParaSprms(nullptr)
3701 , mnSprmsLen(0)
3702 , mnWwNumLevel(0)
3703 , mbTextColChanged(false)
3704 , mbFontChanged(false)
3705 , mbCJKFontChanged(false)
3706 , mbCTLFontChanged(false)
3707 , mbFSizeChanged(false)
3708 , mbFCTLSizeChanged(false)
3709 , mbWidowsChanged(false)
3710 , mbBidiChanged(false)
3711{
3712 mpIo->m_vColl.resize(m_cstd);
3713}
3714
3716{
3717 // see #i25247#, #i25561#, #i48064#, #i92341# for default font
3718 if (!mbCJKFontChanged) // Style no CJK Font? set the default
3720
3721 if (!mbCTLFontChanged) // Style no CTL Font? set the default
3723
3724 // western 2nd to make western charset conversion the default
3725 if (!mbFontChanged) // Style has no Font? set the default,
3727
3728 if( mpIo->m_bNoAttrImport )
3729 return;
3730
3731 // Style has no text color set, winword default is auto
3732 if ( !mbTextColChanged )
3734
3735 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3736 if( !mbFSizeChanged )
3737 {
3738 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3740 aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
3742 }
3743
3744 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3745 if( !mbFCTLSizeChanged )
3746 {
3747 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3748 aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
3750 }
3751
3752 if( !mbWidowsChanged ) // Widows ?
3753 {
3756 }
3757
3758 // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of
3759 // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr
3760 if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style
3761 {
3763 SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
3764 }
3765}
3766
3767bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle,
3768 sal_uInt16 nNextStyle,
3769 std::map<OUString, sal_Int32>& rParaCollisions,
3770 std::map<OUString, sal_Int32>& rCharCollisions)
3771{
3772 SwFormat* pColl;
3773 bool bStyExist;
3774
3775 if (rSI.m_bColl)
3776 {
3777 // Para-Style
3779 mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rParaCollisions);
3780 pColl = aResult.first;
3781 bStyExist = aResult.second;
3782 }
3783 else
3784 {
3785 // Char-Style
3787 mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rCharCollisions);
3788 pColl = aResult.first;
3789 bStyExist = aResult.second;
3790 }
3791
3792 bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ?
3793
3794 // Do not override character styles the list import code created earlier.
3795 if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
3796 bImport = false;
3797
3798 bool bOldNoImp = mpIo->m_bNoAttrImport;
3799 rSI.m_bImportSkipped = !bImport;
3800
3801 if( !bImport )
3802 mpIo->m_bNoAttrImport = true;
3803 else
3804 {
3805 if (bStyExist)
3806 {
3807 pColl->ResetAllFormatAttr(); // #i73790# - method renamed
3808 }
3809 pColl->SetAuto(false); // suggested by JP
3810 } // but changes the UI
3811 mpIo->m_pCurrentColl = pColl;
3812 rSI.m_pFormat = pColl; // remember translation WW->SW
3813 rSI.m_bImportSkipped = !bImport;
3814
3815 // Set Based on style
3816 sal_uInt16 j = rSI.m_nBase;
3817 if (j != nThisStyle && j < m_cstd )
3818 {
3819 SwWW8StyInf* pj = &mpIo->m_vColl[j];
3820 if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
3821 {
3822 rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
3826 rSI.m_n81Flags = pj->m_n81Flags;
3828 if (!rSI.IsWW8BuiltInHeadingStyle())
3829 {
3831 }
3834
3835 if (pj->m_xWWFly)
3836 rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get());
3837 }
3838 }
3839 else if( mpIo->m_bNewDoc && bStyExist )
3841
3842 rSI.m_nFollow = nNextStyle; // remember Follow
3843
3844 mpStyRule = nullptr; // recreate if necessary
3847 mpIo->SetNCurrentColl( nThisStyle );
3848 mpIo->m_bStyNormal = nThisStyle == 0;
3849 return bOldNoImp;
3850}
3851
3852void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp)
3853{
3854 // Reset attribute flags, because there are no style-ends.
3855
3857 mpIo->m_nCharFormat = -1;
3858
3859 // if style is based on nothing or base ignored
3860 if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
3861 {
3862 // If Char-Styles does not work
3863 // -> set hard WW-Defaults
3865 }
3866
3867 mpStyRule = nullptr; // to be on the safe side
3868 mpIo->m_bStyNormal = false;
3869 mpIo->SetNCurrentColl( 0 );
3870 mpIo->m_bNoAttrImport = bOldNoImp;
3871 // reset the list-remember-fields, if used when reading styles
3872 mpIo->m_nLFOPosition = USHRT_MAX;
3874}
3875
3876void WW8RStyle::Import1Style(sal_uInt16 nNr,
3877 std::map<OUString, sal_Int32>& rParaCollisions,
3878 std::map<OUString, sal_Int32>& rCharCollisions)
3879{
3880 if (nNr >= mpIo->m_vColl.size())
3881 return;
3882
3883 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3884
3885 if( rSI.m_bImported || !rSI.m_bValid )
3886 return;
3887
3888 rSI.m_bImported = true; // set flag now to avoid endless loops
3889
3890 // valid and not NUL and not yet imported
3891
3892 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3893 Import1Style(rSI.m_nBase, rParaCollisions, rCharCollisions);
3894
3895 mpStStrm->Seek( rSI.m_nFilePos );
3896
3897 sal_uInt16 nSkip;
3898 OUString sName;
3899
3900 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName)); // read Style
3901
3902 if (xStd)
3903 rSI.SetOrgWWIdent( sName, xStd->sti );
3904
3905 // either no Name or unused Slot or unknown Style
3906
3907 if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
3908 {
3909 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
3910 mpStStrm->Seek(mpStStrm->Tell() + nSkip);
3911 return;
3912 }
3913
3914 bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti),
3915 nNr, xStd->istdNext,
3916 rParaCollisions, rCharCollisions);
3917
3918 // if something is interpreted wrong, this should make it work again
3919 sal_uInt64 nPos = mpStStrm->Tell();
3920
3921 //Variable parts of the STD start at even byte offsets, but "inside
3922 //the STD", which I take to meaning even in relation to the starting
3923 //position of the STD, which matches findings in #89439#, generally it
3924 //doesn't matter as the STSHI starts off nearly always on an even
3925 //offset
3926
3927 //Import of the Style Contents
3928 ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
3929
3930 PostStyle(rSI, bOldNoImp);
3931
3932 mpStStrm->Seek( nPos+nSkip );
3933}
3934
3935void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
3936{
3937 if (nNr >= mpIo->m_vColl.size())
3938 return;
3939
3940 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3941 if( rSI.m_bImported || !rSI.m_bValid )
3942 return;
3943
3944 rSI.m_bImported = true;
3945
3946 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3947 RecursiveReg(rSI.m_nBase);
3948
3950
3951}
3952
3953/*
3954 After all styles are imported then we can recursively apply numbering
3955 styles to them, and change their tab stop settings if they turned out
3956 to have special first line indentation.
3957*/
3959{
3960 sal_uInt16 i;
3961 /*
3962 Clear all imported flags so that we can recursively apply numbering
3963 formats and use it to mark handled ones
3964 */
3965 for (i=0; i < m_cstd; ++i)
3966 mpIo->m_vColl[i].m_bImported = false;
3967
3968 /*
3969 Register the num formats and tabstop changes on the styles recursively.
3970 */
3971
3972 /*
3973 In the same loop apply the tabstop changes required because we need to
3974 change their location if there's a special indentation for the first line,
3975 By avoiding making use of each styles margins during reading of their
3976 tabstops we don't get problems with doubly adjusting tabstops that
3977 are inheritied.
3978 */
3979 for (i=0; i < m_cstd; ++i)
3980 {
3981 if (mpIo->m_vColl[i].m_bValid)
3982 {
3983 RecursiveReg(i);
3984 }
3985 }
3986}
3987
3988void WW8RStyle::ScanStyles() // investigate style dependencies
3989{ // and detect Filepos for each Style
3990 for (sal_uInt16 i = 0; i < m_cstd; ++i)
3991 {
3992 SwWW8StyInf &rSI = mpIo->m_vColl[i];
3993
3994 rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos
3995 sal_uInt16 nSkip;
3996 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD
3997 rSI.m_bValid = xStd != nullptr;
3998 if (rSI.m_bValid)
3999 {
4000 rSI.m_nBase = xStd->istdBase; // remember Basis
4001 rSI.m_bColl = xStd->sgc == 1; // Para-Style
4002 }
4003 else
4004 rSI = SwWW8StyInf();
4005
4006 xStd.reset();
4007 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
4008 mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms
4009 }
4010}
4011
4012std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
4013{
4014 std::vector<sal_uInt8> aRet
4015 {
4016 60,
4017 static_cast< sal_uInt8 >(128 + rChpx.fBold),
4018
4019 61,
4020 static_cast< sal_uInt8 >(128 + rChpx.fItalic),
4021
4022 62,
4023 static_cast< sal_uInt8 >(128 + rChpx.fStrike),
4024
4025 63,
4026 static_cast< sal_uInt8 >(128 + rChpx.fOutline),
4027
4028 65,
4029 static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps),
4030
4031 66,
4032 static_cast< sal_uInt8 >(128 + rChpx.fCaps),
4033
4034 67,
4035 static_cast< sal_uInt8 >(128 + rChpx.fVanish)
4036 };
4037 if (rChpx.fsFtc)
4038 {
4039 aRet.push_back(68);
4040 SVBT16 a;
4041 ShortToSVBT16(rChpx.ftc, a);
4042 aRet.push_back(a[1]);
4043 aRet.push_back(a[0]);
4044 }
4045
4046 if (rChpx.fsKul)
4047 {
4048 aRet.push_back(69);
4049 aRet.push_back(rChpx.kul);
4050 }
4051
4052 if (rChpx.fsLid)
4053 {
4054 aRet.push_back(72);
4055 SVBT16 a;
4056 ShortToSVBT16(rChpx.lid, a);
4057 aRet.push_back(a[1]);
4058 aRet.push_back(a[0]);
4059 }
4060
4061 if (rChpx.fsIco)
4062 {
4063 aRet.push_back(73);
4064 aRet.push_back(rChpx.ico);
4065 }
4066
4067 if (rChpx.fsHps)
4068 {
4069 aRet.push_back(74);
4070
4071 SVBT16 a;
4072 ShortToSVBT16(rChpx.hps, a);
4073 aRet.push_back(a[0]);
4074 }
4075
4076 if (rChpx.fsPos)
4077 {
4078 aRet.push_back(76);
4079 aRet.push_back(rChpx.hpsPos);
4080 }
4081
4082 aRet.push_back(80);
4083 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
4084
4085 aRet.push_back(81);
4086 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
4087
4088 if (rChpx.fsFtcBi)
4089 {
4090 aRet.push_back(82);
4091 SVBT16 a;
4092 ShortToSVBT16(rChpx.fsFtcBi, a);
4093 aRet.push_back(a[1]);
4094 aRet.push_back(a[0]);
4095 }
4096
4097 if (rChpx.fsLidBi)
4098 {
4099 aRet.push_back(83);
4100 SVBT16 a;
4101 ShortToSVBT16(rChpx.lidBi, a);
4102 aRet.push_back(a[1]);
4103 aRet.push_back(a[0]);
4104 }
4105
4106 if (rChpx.fsIcoBi)
4107 {
4108 aRet.push_back(84);
4109 aRet.push_back(rChpx.icoBi);
4110 }
4111
4112 if (rChpx.fsHpsBi)
4113 {
4114 aRet.push_back(85);
4115 SVBT16 a;
4116 ShortToSVBT16(rChpx.hpsBi, a);
4117 aRet.push_back(a[1]);
4118 aRet.push_back(a[0]);
4119 }
4120
4121 return aRet;
4122}
4123
4124Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
4125{
4126 Word2CHPX aChpx;
4127
4128 if (!nSize || !checkSeek(rSt, nOffset))
4129 return aChpx;
4130
4131 const size_t nMaxByteCount = rSt.remainingSize();
4132 if (!nMaxByteCount)
4133 return aChpx;
4134
4135 if (nSize > nMaxByteCount)
4136 {
4137 SAL_WARN("sw.ww8", "ReadWord2Chpx: truncating out of range "
4138 << nSize << " to " << nMaxByteCount);
4139 nSize = nMaxByteCount;
4140 }
4141
4142 sal_uInt8 nCount=0;
4143
4144 while (true)
4145 {
4146 sal_uInt8 nFlags8;
4147 rSt.ReadUChar( nFlags8 );
4148 nCount++;
4149
4150 if (!rSt.good())
4151 break;
4152
4153 aChpx.fBold = nFlags8 & 0x01;
4154 aChpx.fItalic = (nFlags8 & 0x02) >> 1;
4155 aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
4156 aChpx.fOutline = (nFlags8 & 0x08) >> 3;
4157 aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
4158 aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
4159 aChpx.fCaps = (nFlags8 & 0x40) >> 6;
4160 aChpx.fVanish = (nFlags8 & 0x80) >> 7;
4161
4162 if (nCount >= nSize) break;
4163 rSt.ReadUChar( nFlags8 );
4164 nCount++;
4165
4166 if (!rSt.good())
4167 break;
4168
4169 aChpx.fRMark = nFlags8 & 0x01;
4170 aChpx.fSpec = (nFlags8 & 0x02) >> 1;
4171 aChpx.fStrike = (nFlags8 & 0x04) >> 2;
4172 aChpx.fObj = (nFlags8 & 0x08) >> 3;
4173 aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
4174 aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
4175 aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
4176 aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
4177
4178 if (nCount >= nSize) break;
4179 rSt.ReadUChar( nFlags8 );
4180 nCount++;
4181
4182 if (!rSt.good())
4183 break;
4184
4185 aChpx.fsIco = nFlags8 & 0x01;
4186 aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
4187 aChpx.fsHps = (nFlags8 & 0x04) >> 2;
4188 aChpx.fsKul = (nFlags8 & 0x08) >> 3;
4189 aChpx.fsPos = (nFlags8 & 0x10) >> 4;
4190 aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
4191 aChpx.fsLid = (nFlags8 & 0x40) >> 6;
4192 aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
4193
4194 if (nCount >= nSize) break;
4195 rSt.ReadUChar( nFlags8 );
4196 nCount++;
4197
4198 if (!rSt.good())
4199 break;
4200
4201 aChpx.fsFtcBi = nFlags8 & 0x01;
4202 aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
4203 aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
4204
4205 if (nCount >= nSize) break;
4206 rSt.ReadUInt16( aChpx.ftc );
4207 nCount+=2;
4208
4209 if (nCount >= nSize) break;
4210 rSt.ReadUInt16( aChpx.hps );
4211 nCount+=2;
4212
4213 if (nCount >= nSize) break;
4214 rSt.ReadUChar( nFlags8 );
4215 nCount++;
4216
4217 if (!rSt.good())
4218 break;
4219
4220 aChpx.qpsSpace = nFlags8 & 0x3F;
4221 aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
4222 aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
4223
4224 if (nCount >= nSize) break;
4225 rSt.ReadUChar( nFlags8 );
4226 nCount++;
4227
4228 if (!rSt.good())
4229 break;
4230
4231 aChpx.ico = nFlags8 & 0x1F;
4232 aChpx.kul = (nFlags8 & 0xE0) >> 5;
4233
4234 if (nCount >= nSize) break;
4235 rSt.ReadUChar( aChpx.hpsPos );
4236 nCount++;
4237
4238 if (nCount >= nSize) break;
4239 rSt.ReadUChar( aChpx.icoBi );
4240 nCount++;
4241
4242 if (nCount >= nSize) break;
4243 rSt.ReadUInt16( aChpx.lid );
4244 nCount+=2;
4245
4246 if (nCount >= nSize) break;
4247 rSt.ReadUInt16( aChpx.ftcBi );
4248 nCount+=2;
4249
4250 if (nCount >= nSize) break;
4251 rSt.ReadUInt16( aChpx.hpsBi );
4252 nCount+=2;
4253
4254 if (nCount >= nSize) break;
4255 rSt.ReadUInt16( aChpx.lidBi );
4256 nCount+=2;
4257
4258 if (nCount >= nSize) break;
4259 rSt.ReadUInt32( aChpx.fcPic );
4260 nCount+=4;
4261
4262 break;
4263 }
4264
4265 rSt.SeekRel(nSize-nCount);
4266 return aChpx;
4267}
4268
4269namespace
4270{
4271 struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
4272}
4273
4275{
4276 for (sal_uInt16 i=0; i < m_cstd; ++i)
4277 {
4278 mpIo->m_vColl[i].m_bColl = true;
4279 //every chain must end eventually at the null style (style code 222)
4280 mpIo->m_vColl[i].m_nBase = 222;
4281 }
4282
4283 rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
4284 mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid);
4285
4286 sal_uInt16 cstcStd(0);
4287 m_rStream.ReadUInt16( cstcStd );
4288
4289 size_t nMaxByteCount = m_rStream.remainingSize();
4290 sal_uInt16 cbName(0);
4291 m_rStream.ReadUInt16(cbName);
4292 if (cbName > nMaxByteCount)
4293 {
4294 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4295 << cbName << " to " << nMaxByteCount);
4296 cbName = nMaxByteCount;
4297 }
4298 sal_uInt16 nByteCount = 2;
4299 sal_uInt16 stcp=0;
4300 while (nByteCount < cbName)
4301 {
4302 sal_uInt8 nCount(0);
4304 nByteCount++;
4305
4306 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4307 if (stc >=mpIo->m_vColl.size())
4308 continue;
4309
4310 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4311 OUString sName;
4312
4313 if (nCount != 0xFF) // undefined style
4314 {
4315 if (nCount != 0) // user style
4316 {
4317 OString aTmp = read_uInt8s_ToOString(m_rStream, nCount);
4318 nByteCount += aTmp.getLength();
4319 sName = OStringToOUString(aTmp, eStructChrSet);
4320 }
4321 rSI.m_bImported = true;
4322 }
4323
4324 if (sName.isEmpty())
4325 {
4327 if (const char *pStr = GetEnglishNameFromSti(eSti))
4328 sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
4329 }
4330
4331 if (sName.isEmpty())
4332 sName = "Unknown Style: " + OUString::number(stc);
4333
4334 rSI.SetOrgWWIdent(sName, stc);
4335 stcp++;
4336 }
4337
4338 sal_uInt16 nStyles=stcp;
4339
4340 std::vector<pxoffset> aCHPXOffsets(stcp);
4341 nMaxByteCount = m_rStream.remainingSize();
4342 sal_uInt16 cbChpx(0);
4343 m_rStream.ReadUInt16(cbChpx);
4344 if (cbChpx > nMaxByteCount)
4345 {
4346 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4347 << cbChpx << " to " << nMaxByteCount);
4348 cbChpx = nMaxByteCount;
4349 }
4350 nByteCount = 2;
4351 stcp=0;
4352 std::vector< std::vector<sal_uInt8> > aConvertedChpx;
4353 while (nByteCount < cbChpx)
4354 {
4355 if (stcp == aCHPXOffsets.size())
4356 {
4357 //more data than style slots, skip remainder
4358 m_rStream.SeekRel(cbChpx-nByteCount);
4359 break;
4360 }
4361
4362 sal_uInt8 cb(0);
4363 m_rStream.ReadUChar( cb );
4364 nByteCount++;
4365
4366 aCHPXOffsets[stcp].mnSize = 0;
4367
4368 if (cb != 0xFF)
4369 {
4370 sal_uInt8 nRemainder = cb;
4371
4372 aCHPXOffsets[stcp].mnOffset = m_rStream.Tell();
4373 aCHPXOffsets[stcp].mnSize = nRemainder;
4374
4375 Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset,
4376 aCHPXOffsets[stcp].mnSize);
4377 aConvertedChpx.push_back( ChpxToSprms(aChpx) );
4378
4379 nByteCount += nRemainder;
4380 }
4381 else
4382 aConvertedChpx.emplace_back( );
4383
4384 ++stcp;
4385 }
4386
4387 std::vector<pxoffset> aPAPXOffsets(stcp);
4388 nMaxByteCount = m_rStream.remainingSize();
4389 sal_uInt16 cbPapx(0);
4390 m_rStream.ReadUInt16(cbPapx);
4391 if (cbPapx > nMaxByteCount)
4392 {
4393 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4394 << cbPapx << " to " << nMaxByteCount);
4395 cbPapx = nMaxByteCount;
4396 }
4397 nByteCount = 2;
4398 stcp=0;
4399 while (nByteCount < cbPapx)
4400 {
4401 if (stcp == aPAPXOffsets.size())
4402 {
4403 m_rStream.SeekRel(cbPapx-nByteCount);
4404 break;
4405 }
4406
4407 sal_uInt8 cb(0);
4408 m_rStream.ReadUChar( cb );
4409 nByteCount++;
4410
4411 aPAPXOffsets[stcp].mnSize = 0;
4412
4413 if (cb != 0xFF)
4414 {
4415 sal_uInt8 stc2(0);
4416 m_rStream.ReadUChar( stc2 );
4417 m_rStream.SeekRel(6);
4418 nByteCount+=7;
4419 sal_uInt8 nRemainder = cb-7;
4420
4421 aPAPXOffsets[stcp].mnOffset = m_rStream.Tell();
4422 aPAPXOffsets[stcp].mnSize = nRemainder;
4423
4424 m_rStream.SeekRel(nRemainder);
4425 nByteCount += nRemainder;
4426 }
4427
4428 ++stcp;
4429 }
4430
4431 sal_uInt16 iMac(0);
4432 m_rStream.ReadUInt16( iMac );
4433
4434 if (iMac > nStyles) iMac = nStyles;
4435
4436 std::map<OUString, sal_Int32> aParaCollisions;
4437 std::map<OUString, sal_Int32> aCharCollisions;
4438
4439 for (stcp = 0; stcp < iMac; ++stcp)
4440 {
4441 sal_uInt8 stcNext(0), stcBase(0);
4442 m_rStream.ReadUChar( stcNext );
4443 m_rStream.ReadUChar( stcBase );
4444
4445 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4446
4447 /*
4448 #i64557# style based on itself
4449 every chain must end eventually at the null style (style code 222)
4450 */
4451 if (stc == stcBase)
4452 stcBase = 222;
4453
4454 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4455 rSI.m_nBase = stcBase;
4456
4458
4459 if (eSti == ww::stiNil)
4460 continue;
4461
4462 if (stcp >= aPAPXOffsets.size())
4463 continue;
4464
4465 rSI.m_bValid = true;
4466
4467 if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
4468 mpIo->m_vColl[stc].m_bColl = false;
4469
4470 bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext,
4471 aParaCollisions,
4472 aCharCollisions);
4473
4474 ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
4475 true);
4476
4477 if (!aConvertedChpx[stcp].empty())
4478 ImportSprms(aConvertedChpx[stcp].data(),
4479 static_cast< short >(aConvertedChpx[stcp].size()),
4480 false);
4481
4482 PostStyle(rSI, bOldNoImp);
4483 }
4484}
4485
4487{
4488 ScanStyles(); // Scan Based On
4489
4490 std::map<OUString, sal_Int32> aParaCollisions;
4491 std::map<OUString, sal_Int32> aCharCollisions;
4492
4493 for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles
4494 if (mpIo->m_vColl[i].m_bValid)
4495 Import1Style(i, aParaCollisions, aCharCollisions);
4496}
4497
4499{
4503
4505 return;
4506
4507 if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2)
4509 else
4511
4512 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4513 {
4514 // Follow chain
4515 SwWW8StyInf* pi = &mpIo->m_vColl[i];
4516 sal_uInt16 j = pi->m_nFollow;
4517 if( j < m_cstd )
4518 {
4519 SwWW8StyInf* pj = &mpIo->m_vColl[j];
4520 if ( j != i // rational Index ?
4521 && pi->m_pFormat // Format ok ?
4522 && pj->m_pFormat // Derived-Format ok ?
4523 && pi->m_bColl // only possible for paragraph templates (WW)
4524 && pj->m_bColl ){ // identical Type ?
4525 static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
4526 *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
4527 }
4528 }
4529 }
4530
4531 // Missing special handling for default character template
4532 // "Absatz-Standardschriftart" ( Style-ID 65 ).
4533 // That is empty by default ( WW6 dt and US ) and not changeable
4534 // via WW-UI so this does not matter.
4535 // This could be done by:
4536 // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
4537
4538 // for e.g. tables an always valid Std-Style is necessary
4539
4540 if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() &&
4541 mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid )
4542 mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat);
4543 else
4545
4546 // set Hyphenation flag on BASIC para-style
4548 {
4549 if (mpIo->m_xWDop->fAutoHyphen
4550 && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState(
4551 RES_PARATR_HYPHENZONE, false) )
4552 {
4554 aAttr.GetMinLead() = 2;
4555 aAttr.GetMinTrail() = 2;
4556 aAttr.GetMaxHyphens() = 0;
4557
4559 }
4560 }
4561
4562 // we do not read styles anymore:
4563 mpIo->m_pCurrentColl = nullptr;
4564}
4565
4566rtl_TextEncoding SwWW8StyInf::GetCharSet() const
4567{
4568 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4569 return m_eRTLFontSrcCharSet;
4570 return m_eLTRFontSrcCharSet;
4571}
4572
4573rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
4574{
4575 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4576 return m_eRTLFontSrcCharSet;
4577 return m_eCJKFontSrcCharSet;
4578}
4579
4580/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & GetValue() const
virtual void DeleteRange(SwPaM &)=0
Delete a range SwFlyFrameFormat.
virtual SwTextFormatColl * GetTextCollFromPool(sal_uInt16 nId, bool bRegardLanguage=true)=0
Return "Auto-Collection with ID.
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
sal_uInt64 Tell() const
bool good() const
SvStream & ReadInt16(sal_Int16 &rInt16)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
sal_uInt64 Seek(sal_uInt64 nPos)
std::size_t ReadBytes(void *pData, std::size_t nSize)
sal_uInt64 SeekRel(sal_Int64 nPos)
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
sal_uInt64 remainingSize()
SvStream & ReadUChar(unsigned char &rChar)
bool WasDeleted() const
void SetDistance(sal_Int16 nNew, SvxBoxItemLine nLine)
short GetTextFirstLineOffset() const
SvxBreak GetBreak() const
sal_uInt8 & GetMinLead()
sal_uInt8 & GetMaxHyphens()
sal_uInt8 & GetMinTrail()
void SetLeft(const tools::Long nL, const sal_uInt16 nProp=100)
void SetStart(sal_uInt16 nSet)
void SetAbsLSpace(sal_Int32 nSet)
sal_uInt8 GetIncludeUpperLevels() const
void SetBulletFont(const vcl::Font *pFont)
void SetCharTextDistance(short nSet)
void SetListFormat(const OUString &rPrefix, const OUString &rSuffix, int nLevel)
void SetNumAdjust(SvxAdjust eSet)
void SetIncludeUpperLevels(sal_uInt8 nSet)
void SetFirstLineOffset(sal_Int32 nSet)
void SetBulletChar(sal_UCS4 cSet)
const OUString & GetSuffix() const
void SetNumberingType(SvxNumType nSet)
SvxNumType GetNumberingType() const
void SetHeight(tools::Long n)
void SetWidth(tools::Long n)
bool WasDeleted() const
Definition: doc.hxx:197
SwFlyFrameFormat * MakeFlySection(RndStdIds eAnchorType, const SwPosition *pAnchorPos, const SfxItemSet *pSet=nullptr, SwFrameFormat *pParent=nullptr, bool bCalledFromShell=false)
Definition: doclay.cxx:288
OUString GetUniqueNumRuleName(const OUString *pChkStr=nullptr, bool bAutoNum=true) const
Definition: docnum.cxx:2532
SwNumRule * FindNumRulePtr(const OUString &rName) const
Definition: docnum.cxx:2454
const SwNumRuleTable & GetNumRuleTable() const
Definition: doc.hxx:1081
SwNumRule * GetOutlineNumRule() const
Definition: doc.hxx:1039
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:329
SwNodes & GetNodes()
Definition: doc.hxx:422
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:649
void InsertRow(const SwCursor &rCursor, sal_uInt16 nCnt=1, bool bBehind=true)
Definition: ndtbl.cxx:1750
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:440
const SwTextFormatColl * GetDfltTextFormatColl() const
Definition: doc.hxx:791
std::shared_ptr< SwUnoCursor > CreateUnoCursor(const SwPosition &rPos, bool bTableCursor=false)
Definition: doc.cxx:1810
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1337
const SwTable * InsertTable(const SwInsertTableOptions &rInsTableOpts, const SwPosition &rPos, sal_uInt16 nRows, sal_uInt16 nCols, sal_Int16 eAdjust, const SwTableAutoFormat *pTAFormat=nullptr, const std::vector< sal_uInt16 > *pColArr=nullptr, bool bCalledFromShell=false, bool bNewModel=true, const OUString &rTableName={})
Insert new table at position.
Definition: ndtbl.cxx:337
bool SetTextFormatColl(const SwPaM &rRg, SwTextFormatColl *pFormat, const bool bReset=true, const bool bResetListAttrs=false, SwRootFrame const *pLayout=nullptr)
Add 4th optional parameter <bResetListAttrs>.
Definition: docfmt.cxx:1100
sal_uInt16 MakeNumRule(const OUString &rName, const SwNumRule *pCpy=nullptr, bool bBroadcast=false, const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode=SvxNumberFormat::LABEL_WIDTH_AND_POSITION)
Definition: docnum.cxx:2488
void SetOutlineNumRule(const SwNumRule &rRule)
Definition: docnum.cxx:112
FlyAnchors.
Definition: fmtanchr.hxx:37
void SetAnchor(const SwPosition *pPos)
Definition: atrfrm.cxx:1593
SwNode * GetAnchorNode() const
Definition: atrfrm.cxx:1614
SfxPoolItem subclass for footnotes and endnotes, stored in the anchor text node.
Definition: fmtftn.hxx:47
void SetWidthPercent(sal_uInt8 n)
Definition: fmtfsize.hxx:95
void SetHeightSizeType(SwFrameSize eSize)
Definition: fmtfsize.hxx:81
Defines the horizontal position of a fly frame.
Definition: fmtornt.hxx:73
void SetPos(SwTwips nNew)
Definition: fmtornt.hxx:100
void SetHoriOrient(sal_Int16 eNew)
Definition: fmtornt.hxx:96
sal_Int16 GetHoriOrient() const
Definition: fmtornt.hxx:94
Controls if a table row is allowed to split or not.
Definition: fmtrowsplt.hxx:32
void SetAnchorOnly(bool bNew)
Definition: fmtsrnd.hxx:56
Defines the vertical position of a fly frame.
Definition: fmtornt.hxx:37
Base class for various Writer styles.
Definition: format.hxx:47
const SvxFrameDirectionItem & GetFrameDir(bool=true) const
Definition: frmatr.hxx:118
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
void SetAuto(bool bNew)
Definition: format.hxx:179
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
Definition: format.cxx:385
const SwNumRuleItem & GetNumRule(bool=true) const
Definition: paratr.hxx:241
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:88
const SvxFormatBreakItem & GetBreak(bool=true) const
Definition: frmatr.hxx:114
bool SetDerivedFrom(SwFormat *pDerivedFrom=nullptr)
0 is Default.
Definition: format.cxx:318
const SwFormatSurround & GetSurround(bool=true) const
Definition: fmtsrnd.hxx:66
const SfxPoolItem & GetFormatAttr(sal_uInt16 nWhich, bool bInParents=true) const
If bInParents is FALSE, search only in this format for attribute.
Definition: format.cxx:366
virtual bool SetFormatAttr(const SfxPoolItem &rAttr)
Definition: format.cxx:447
virtual sal_uInt16 ResetAllFormatAttr()
Takes all hints from Delta-Array,.
Definition: format.cxx:646
Style of a layout element.
Definition: frmfmt.hxx:72
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:111
Base class of the Writer document model elements.
Definition: node.hxx:98
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:901
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:691
SwNodeType GetNodeType() const
Definition: node.hxx:166
SwNode & GetEndOfExtras() const
This is the last EndNode of a special section.
Definition: ndarr.hxx:163
bool InsBoxen(SwTableNode *, SwTableLine *, SwTableBoxFormat *, SwTextFormatColl *, const SfxItemSet *pAutoAttr, sal_uInt16 nInsPos, sal_uInt16 nCnt=1)
Insert a new box in the line before InsPos.
Definition: ndtbl.cxx:234
const SwNumFormat * GetNumFormat(sal_uInt16 i) const
Definition: number.cxx:97
void Set(sal_uInt16 i, const SwNumFormat *)
Definition: number.cxx:618
const OUString & GetName() const
Definition: numrule.hxx:224
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
SwNode & GetPointNode() const
Definition: pam.hxx:275
const SwPosition * GetPoint() const
Definition: pam.hxx:253
SwTableBox is one table cell in the document model.
Definition: swtable.hxx:443
SwNodeOffset GetSttIdx() const
Definition: swtable.cxx:2242
void setRowSpan(sal_Int32 nNewRowSpan)
Definition: swtable.cxx:81
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:481
const SwStartNode * GetSttNd() const
Definition: swtable.hxx:495
void ChgFrameFormat(SwTableBoxFormat *pNewFormat, bool bNeedToReregister=true)
Definition: swtable.cxx:2130
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:2094
SwTableLine is one table row in the document model.
Definition: swtable.hxx:376
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:398
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1482
SwTableBoxes & GetTabBoxes()
Definition: swtable.hxx:386
size_type size() const
Definition: swtable.hxx:76
const SwTable & GetTable() const
Definition: node.hxx:542
SwTableNode * GetTableNode() const
Definition: swtable.cxx:2315
void SetRowsToRepeat(sal_uInt16 nNumOfRows)
Definition: swtable.hxx:202
SwTableLines & GetTabLines()
Definition: swtable.hxx:206
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:209
static SwSelBoxes & SelLineFromBox(const SwTableBox *pBox, SwSelBoxes &rBoxes, bool bToTop=true)
Definition: tblcpy.cxx:1017
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:167
SwTextAttr subclass for footnotes and endnotes.
Definition: txtftn.hxx:34
void SetSeqNo(sal_uInt16 n)
Definition: txtftn.hxx:65
void SetNumber(sal_uInt16 nNumber, sal_uInt16 nNumberRLHidden, const OUString &sNumStr)
Definition: atrftn.cxx:367
const SwNodeIndex * GetStartNode() const
Definition: txtftn.hxx:43
Represents the style of a paragraph.
Definition: fmtcol.hxx:61
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
void SetAttrListLevel(int nLevel)
Sets the list level of this text node.
Definition: ndtxt.cxx:4218
SwTextAttr * InsertItem(SfxPoolItem &rAttr, const sal_Int32 nStart, const sal_Int32 nEnd, const SetAttrMode nMode=SetAttrMode::DEFAULT)
create new text attribute from rAttr and insert it
Definition: thints.cxx:1305
const SfxPoolItem & GetAttr(sal_uInt16 nWhich, bool bInParent=true) const
End: Data collected during idle time.
Definition: node.hxx:732
void SetCountedInList(bool bCounted)
Definition: ndtxt.cxx:4378
const OUString & GetText() const
Definition: ndtxt.hxx:244
std::optional< SwPosition > m_oLastAnchorPos
Definition: ww8par.hxx:1411
SwPaM * m_pPaM
Definition: ww8par.hxx:1133
sal_uInt16 m_nLFOPosition
Definition: ww8par.hxx:1329
bool InLocalApo() const
Definition: ww8par.hxx:1591
SwDocShell * m_pDocShell
Definition: ww8par.hxx:1110
sal_uInt16 End_Footnote()
Definition: ww8par2.cxx:170
const WW8Fib & GetFib() const
Definition: ww8par.hxx:1920
friend struct WW8SwFlyPara
Definition: ww8par.hxx:1116
std::vector< SwWW8StyInf > m_vColl
Definition: ww8par.hxx:1273
void PopTableDesc()
Definition: ww8par2.cxx:3490
sal_Int32 m_nIniFlyDx
Definition: ww8par.hxx:1320
bool m_bCurrentAND_fNumberAcross
Definition: ww8par.hxx:1386
const SwTextFormatColl * m_pDfltTextFormatColl
Definition: ww8par.hxx:1274
SwNumRule * GetStyRule()
Definition: ww8par2.cxx:701
void Read_TabRowEnd(sal_uInt16, const sal_uInt8 *pData, short nLen)
Definition: ww8par2.cxx:3484
void Read_ANLevelNo(sal_uInt16, const sal_uInt8 *pData, short nLen)
Definition: ww8par2.cxx:720
static Color ExtractColour(const sal_uInt8 *&rpData, bool bVer67)
Definition: ww8par6.cxx:5093
bool m_bWasTabCellEnd
Definition: ww8par.hxx:1357
bool SetUpperSpacing(SwPaM &pMyPam, int nSpace)
Definition: ww8par.cxx:2602
std::unique_ptr< SwWW8FltControlStack > m_xCtrlStck
Definition: ww8par.hxx:1135
SwDoc & m_rDoc
Definition: ww8par.hxx:1131
std::unique_ptr< WW8SwFlyPara > m_xSFlyPara
Definition: ww8par.hxx:1280
void Read_TabCellEnd(sal_uInt16, const sal_uInt8 *pData, short nLen)
Definition: ww8par2.cxx:3478
bool m_bWasTabRowEnd
Definition: ww8par.hxx:1356
static bool ParseTabPos(WW8_TablePos *aTabPos, WW8PLCFx_Cp_FKP *pPap)
Definition: ww8par6.cxx:5378
SwTwips MoveOutsideFly(SwFrameFormat *pFlyFormat, const SwPosition &rPos, bool bTableJoin=true)
Definition: ww8par6.cxx:2354
std::shared_ptr< WW8Fib > m_xWwFib
Definition: ww8par.hxx:1257
wwSectionManager m_aSectionManager
Definition: ww8par.hxx:1188
sal_Int32 m_nIniFlyDy
Definition: ww8par.hxx:1321
void StopAnlToRestart(sal_uInt8 nType, bool bGoBack=true)
Definition: ww8par2.cxx:1028
bool SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums, sal_uInt16 nWhich)
Definition: ww8par6.cxx:3829
sal_uInt16 m_nCurrentColl
Definition: ww8par.hxx:1327
sw::util::InsertedTablesManager m_aInsertedTables
Definition: ww8par.hxx:1201
void SetNCurrentColl(sal_uInt16 nColl)
Definition: ww8par.hxx:1923
sw::util::CharStyleMapper m_aCharStyleMapper
Definition: ww8par.hxx:1222
std::unique_ptr< WW8Dop > m_xWDop
Definition: ww8par.hxx:1259
std::unique_ptr< WW8FlyPara > ConstructApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos)
Definition: ww8par6.cxx:2432
bool m_bReadNoTable
Definition: ww8par.hxx:1347
friend class WW8TabDesc
Definition: ww8par.hxx:1113
sal_uInt8 m_nListLevel
Definition: ww8par.hxx:1343
void StopTable()
Definition: ww8par2.cxx:3505
bool StartTable(WW8_CP nStartCp)
Definition: ww8par2.cxx:3350
std::unique_ptr< FrameDeleteWatch > m_xFormatOfJustInsertedApo
Definition: ww8par.hxx:1238
void SetAnld(SwNumRule *pNumR, WW8_ANLD const *pAD, sal_uInt8 nSwLevel, bool bOutLine)
Definition: ww8par2.cxx:681
sal_uInt16 StyleUsingLFO(sal_uInt16 nLFOIndex) const
Definition: ww8par2.cxx:3540
std::shared_ptr< WW8PLCFMan > m_xPlcxMan
Definition: ww8par.hxx:1262
std::stack< std::unique_ptr< WW8TabDesc > > m_aTableStack
Definition: ww8par.hxx:1284
bool SetLowerSpacing(SwPaM &rMyPam, int nSpace)
Definition: ww8par.cxx:2597
bool StyleExists(unsigned int nColl) const
Definition: ww8par.hxx:1422
static bool IsBorder(const WW8_BRCVer9 *pbrc, bool bChkBtwn=false)
Definition: ww8par6.cxx:1509
void NextAnlLine(const sal_uInt8 *pSprm13)
Definition: ww8par2.cxx:957
tools::Long Read_Footnote(WW8PLCFManResult *pRes)
Definition: ww8par2.cxx:300
short m_nCharFormat
Definition: ww8par.hxx:1331
void MoveInsideFly(const SwFrameFormat *pFlyFormat)
Definition: ww8par6.cxx:2340