tdf#147067 Jump to clicked spot if left mouse click with Option key
[LibreOffice.git] / sw / source / filter / ww8 / ww8par2.cxx
blob246f56ad9b84489858cfb7ac8e4279919eadb3b8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
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>
37 #include <editeng/hyphenzoneitem.hxx>
38 #include <editeng/frmdiritem.hxx>
39 #include <editeng/pgrditem.hxx>
40 #include <msfilter.hxx>
41 #include <pam.hxx>
42 #include <deletelistener.hxx>
43 #include <doc.hxx>
44 #include <IDocumentStylePoolAccess.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 <formatflysplit.hxx>
62 #include <numrule.hxx>
63 #include "sprmids.hxx"
64 #include <wwstyles.hxx>
65 #include "ww8struc.hxx"
66 #include "ww8par.hxx"
67 #include "ww8par2.hxx"
69 #include <frmatr.hxx>
70 #include <itabenum.hxx>
71 #include <unocrsr.hxx>
73 #include <iostream>
74 #include <memory>
76 using namespace ::com::sun::star;
78 WW8TabBandDesc::WW8TabBandDesc():
79 pNextBand(nullptr), nGapHalf(0), mnDefaultLeft(0), mnDefaultTop(0), mnDefaultRight(0),
80 mnDefaultBottom(0), mbHasSpacing(false), nLineHeight(0), nRows(0), nCenter{}, nWidth{},
81 nWwCols(0), nSwCols(0), bLEmptyCol(false), bREmptyCol(false), bCantSplit(false),
82 pTCs(nullptr), nOverrideSpacing{}, nOverrideValues{}, pSHDs(nullptr),
83 pNewSHDs(nullptr), bExist{}, nTransCell{}
85 for (sal_uInt16 & rn : maDirections)
86 rn = 4;
89 WW8TabBandDesc::~WW8TabBandDesc()
91 delete[] pTCs;
92 delete[] pSHDs;
93 delete[] pNewSHDs;
96 void sw::util::RedlineStack::close( const SwPosition& rPos,
97 RedlineType eType, WW8TabDesc* pTabDesc )
99 // If the redline type is not found in the redline stack, we have to check if there has been
100 // a tabledesc and to check its saved redline stack, too. (#136939, #i68139)
101 if( !close( rPos, eType ) )
103 if( pTabDesc && pTabDesc->getOldRedlineStack() )
105 bool const bResult =
106 pTabDesc->getOldRedlineStack()->close(rPos, eType);
107 OSL_ENSURE( bResult, "close without open!");
112 void wwSectionManager::SetCurrentSectionHasFootnote()
114 OSL_ENSURE(!maSegments.empty(),
115 "should not be possible, must be at least one segment");
116 if (!maSegments.empty())
117 maSegments.back().mbHasFootnote = true;
120 void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA)
122 OSL_ENSURE(!maSegments.empty(),
123 "should not be possible, must be at least one segment");
124 if ( !maSegments.empty() )
125 maSegments.back().mnVerticalAdjustment = nVA;
128 bool wwSectionManager::CurrentSectionIsVertical() const
130 OSL_ENSURE(!maSegments.empty(),
131 "should not be possible, must be at least one segment");
132 if (!maSegments.empty())
133 return maSegments.back().IsVertical();
134 return false;
137 bool wwSectionManager::CurrentSectionIsProtected() const
139 OSL_ENSURE(!maSegments.empty(),
140 "should not be possible, must be at least one segment");
141 if (!maSegments.empty())
142 return SectionIsProtected(maSegments.back());
143 return false;
146 sal_uInt32 wwSectionManager::GetPageLeft() const
148 return !maSegments.empty() ? maSegments.back().m_nPgLeft : 0;
151 sal_uInt32 wwSectionManager::GetPageRight() const
153 return !maSegments.empty() ? maSegments.back().m_nPgRight : 0;
156 sal_uInt32 wwSectionManager::GetPageWidth() const
158 return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0;
161 sal_uInt32 wwSectionManager::GetTextAreaWidth() const
163 return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0;
166 sal_uInt32 wwSectionManager::GetWWPageTopMargin() const
168 return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0;
171 namespace
173 bool IsInSplitFly(SwPaM& rPaM)
175 SwNode& rNode = rPaM.GetPoint()->GetNode();
176 SwNodeOffset nNodeIndex = rNode.GetIndex();
177 SwNodes& rNodes = rNode.GetNodes();
178 if (nNodeIndex >= rNodes.GetEndOfAutotext().GetIndex()
179 || nNodeIndex < rNodes.GetEndOfInserts().GetIndex())
181 return false;
184 SwFrameFormat* pFlyFormat = rNode.StartOfSectionNode()->GetFlyFormat();
185 if (!pFlyFormat)
187 return false;
190 return pFlyFormat->GetFlySplit().GetValue();
194 sal_uInt16 SwWW8ImplReader::End_Footnote()
197 Ignoring Footnote outside of the normal Text. People will put footnotes
198 into field results and field commands.
200 bool bSplitFly = IsInSplitFly(*m_pPaM);
201 if (m_bIgnoreText
202 || (m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras() && !bSplitFly))
204 return 0;
207 OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
208 if (m_aFootnoteStack.empty())
209 return 0;
211 bool bFtEdOk = false;
212 const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
214 //Get the footnote character and remove it from the txtnode. We'll
215 //replace it with the footnote
216 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
217 sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
219 OUString sChar;
220 SwTextFootnote* pFN = nullptr;
221 //There should have been a footnote char, we will replace this.
222 if (pText && nPos)
224 sChar += OUStringChar(pText->GetText()[--nPos]);
225 m_pPaM->SetMark();
226 m_pPaM->GetMark()->AdjustContent(-1);
227 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_oLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_oLastAnchorPos) : nullptr);
228 m_oLastAnchorPos.reset();
229 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
230 m_pPaM->DeleteMark();
231 if (xLastAnchorCursor)
232 m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
233 SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
234 pFN = static_cast<SwTextFootnote*>(pText->InsertItem(aFootnote, nPos, nPos));
236 OSL_ENSURE(pFN, "Problems creating the footnote text");
237 if (pFN)
239 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
240 WW8PLCFxSaveAll aSave;
241 m_xPlcxMan->SaveAllPLCFx( aSave );
242 std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan;
244 const SwNodeIndex* pSttIdx = pFN->GetStartNode();
245 assert(pSttIdx && "Problems creating footnote text");
247 pFN->SetSeqNo(m_rDoc.GetFootnoteIdxs().size());
249 bool bOld = m_bFootnoteEdn;
250 m_bFootnoteEdn = true;
252 SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr());
254 SvtDeleteListener aDeleteListener(rFormatFootnote.GetNotifier());
256 // read content of Ft-/End-Note
257 Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
259 m_bFootnoteEdn = bOld;
261 SAL_WARN_IF(aDeleteListener.WasDeleted(), "sw.ww8", "Footnode deleted during its import");
262 if (!aDeleteListener.WasDeleted())
264 bFtEdOk = true;
266 OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
267 "footnote autonumbering must be 0x02, and everything else must not be");
269 // If no automatic numbering use the following char from the main text
270 // as the footnote number
271 if (!rDesc.mbAutoNum)
272 pFN->SetNumber(0, 0, sChar);
275 Delete the footnote char from the footnote if it's at the beginning
276 as usual. Might not be if the user has already deleted it, e.g.
277 #i14737#
279 SwPosition& rPaMPointPos = *m_pPaM->GetPoint();
280 rPaMPointPos.Assign(pSttIdx->GetIndex() + 1);
281 SwTextNode* pTNd = rPaMPointPos.GetNode().GetTextNode();
282 if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
284 const OUString &rText = pTNd->GetText();
285 if (rText[0] == sChar[0])
287 // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
288 sal_Int32 nFirstLineIndent=0;
289 SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aSet(m_rDoc.GetAttrPool());
290 if ( pTNd->GetAttr(aSet) )
292 const SvxFirstLineIndentItem *const pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
293 if (pFirstLine)
295 nFirstLineIndent = pFirstLine->ResolveTextFirstLineOffset({});
299 rPaMPointPos.SetContent(0);
300 m_pPaM->SetMark();
301 // Strip out aesthetic tabs we may have inserted on export #i24762#
302 if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09)
303 m_pPaM->GetMark()->AdjustContent(1);
304 m_pPaM->GetMark()->AdjustContent(1);
305 m_xReffingStck->Delete(*m_pPaM);
306 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
307 m_pPaM->DeleteMark();
312 *m_pPaM->GetPoint() = std::move(aTmpPos); // restore Cursor
314 m_xPlcxMan = std::move(xOldPlcxMan); // Restore attributes
315 m_xPlcxMan->RestoreAllPLCFx( aSave );
318 if (bFtEdOk)
319 m_aSectionManager.SetCurrentSectionHasFootnote();
321 m_aFootnoteStack.pop_back();
322 return 0;
325 tools::Long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes)
328 Ignoring Footnote outside of the normal Text. People will put footnotes
329 into field results and field commands.
331 bool bSplitFly = IsInSplitFly(*m_pPaM);
332 if (m_bIgnoreText
333 || (m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras() && !bSplitFly))
335 return 0;
338 FootnoteDescriptor aDesc;
339 aDesc.mbAutoNum = true;
340 if (eEDN == pRes->nSprmId)
342 aDesc.meType = MAN_EDN;
343 WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn();
344 if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
345 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
347 else
349 aDesc.meType = MAN_FTN;
350 WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote();
351 if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
352 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
355 aDesc.mnStartCp = pRes->nCp2OrIdx;
356 aDesc.mnLen = pRes->nMemLen;
358 m_aFootnoteStack.push_back(aDesc);
360 return 0;
363 bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp,
364 int nLevel) const
366 WW8PLCFxDesc aRes;
367 aRes.pMemPos = nullptr;
368 aRes.nEndPos = rStartCp;
369 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
371 while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
373 if (pPap->Where() != WW8_CP_MAX)
375 SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel));
376 const sal_uInt8* pB = aSprmRes.pSprm;
377 if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1)
379 aSprmRes = pPap->HasSprm(0x6649);
380 const sal_uInt8 *pLevel = aSprmRes.pSprm;
381 if (pLevel && aSprmRes.nRemainingData >= 1)
383 if (nLevel + 1 == *pLevel)
384 return true;
386 else
388 OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
389 return true; // RowEnd found
394 aRes.nStartPos = aRes.nEndPos;
395 aRes.pMemPos = nullptr;
396 //Seek to our next block of properties
397 if (!(pPap->SeekPos(aRes.nStartPos)))
399 aRes.nEndPos = WW8_CP_MAX;
400 pPap->SetDirty(true);
402 pPap->GetSprms(&aRes);
403 pPap->SetDirty(false);
404 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
405 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
407 SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
408 break;
410 //Update our aRes to get the new starting point of the next properties
411 rStartCp = aRes.nEndPos;
414 return false;
417 ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
418 const WW8_TablePos *pTabPos)
420 const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
421 ApoTestResults aRet;
422 // Frame in Style Definition (word appears to ignore them if inside an
423 // text autoshape)
424 sal_uInt16 const nStyle(m_xPlcxMan->GetColl());
425 if (!m_bTxbxFlySection && nStyle < m_vColl.size())
426 aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
429 #i1140#
430 If I have a table and apply a style to one of its frames that should cause
431 a paragraph that it is applied to it to only exist as a separate floating
432 frame, then the behaviour depends on which cell that it has been applied
433 to. If it is the first cell of a row then the whole table row jumps into the
434 new frame, if it isn't then the paragraph attributes are applied
435 "except" for the floating frame stuff. i.e. it's ignored. So if there's a
436 table, and we're not in the first cell then we ignore the fact that the
437 paragraph style wants to be in a different frame.
439 This sort of mindbending inconsistency is surely why frames are deprecated
440 in word 97 onwards and hidden away from the user
442 #i1532# & #i5379#
443 If we are already a table in a frame then we must grab the para properties
444 to see if we are still in that frame.
447 aRet.m_bHasSprmPWr = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr; // sprmPWr
448 SprmResult aSrpmPPc = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B); // sprmPPc
449 const sal_uInt8 *pSrpmPPc = aSrpmPPc.pSprm;
450 aRet.m_bHasSprmPPc = pSrpmPPc != nullptr;
451 const sal_Int16 nTPc = aRet.mpStyleApo ? aRet.mpStyleApo->nTPc : 0;
452 aRet.m_nSprmPPc = (pSrpmPPc && aSrpmPPc.nRemainingData >= 1) ? *pSrpmPPc : nTPc;
454 // Is there some frame data here
455 bool bNowApo = aRet.HasFrame() || pTopLevelTable;
456 if (bNowApo)
458 if (!ConstructApo(aRet, pTabPos))
459 bNowApo = false;
462 bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
463 if (bTestAllowed)
465 //Test is allowed if there is no table.
466 //Otherwise only allowed if we are in the
467 //first paragraph of the first cell of a row.
468 //(And only if the row we are inside is at the
469 //same level as the previous row, think tables
470 //in tables)
471 if (nCellLevel == m_nInTable)
473 if (m_nInTable)
475 if (!m_xTableDesc)
477 OSL_ENSURE(m_xTableDesc, "What!");
478 bTestAllowed = false;
480 else
482 // #i39468#
483 // If current cell isn't valid, the test is allowed.
484 // The cell isn't valid, if e.g. there is a new row
485 // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()>
486 bTestAllowed =
487 m_xTableDesc->GetCurrentCol() == 0 &&
488 ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) ||
489 m_xTableDesc->InFirstParaInCell() );
495 if (!bTestAllowed)
496 return aRet;
498 aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
499 aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
501 //If it happens that we are in a table, then if it's not the first cell
502 //then any attributes that might otherwise cause the contents to jump
503 //into another frame don't matter, a table row sticks together as one
504 //unit no matter what else happens. So if we are not in a table at
505 //all, or if we are in the first cell then test that the last frame
506 //data is the same as the current one
507 if (bNowApo && InEqualApo(nCellLevel))
509 // two bordering each other
510 if (!TestSameApo(aRet, pTabPos))
511 aRet.mbStopApo = aRet.mbStartApo = true;
514 return aRet;
517 // helper methods for outline, numbering and bullets
519 static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
521 static const SvxNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
522 SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC,
523 SVX_NUM_ARABIC, SVX_NUM_ARABIC };
525 static const SvxAdjust eAdjA[4] = { SvxAdjust::Left,
526 SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left };
527 if (rAV.nfc < 8) {
528 rNum.SetNumberingType( eNumA[ rAV.nfc ] );
529 } else {
530 SvxNumType nType = SVX_NUM_ARABIC;
531 switch( rAV.nfc ) {
532 case 14:
533 case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
534 case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
535 case 31:nType = SVX_NUM_DI_ZI_ZH; break;
536 case 35:
537 case 36:
538 case 37:
539 case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
540 case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
541 case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
542 case 10:
543 case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
544 case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
545 case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
546 case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
547 case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
548 case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
549 case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
550 case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
551 //case 42:
552 //case 43:
553 case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
554 default:
555 nType= SVX_NUM_ARABIC;break;
557 rNum.SetNumberingType( nType );
560 if ((rAV.aBits1 & 0x4) >> 2)
562 rNum.SetIncludeUpperLevels(nSwLevel + 1);
564 rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) );
565 rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
567 rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) );
568 sal_Int16 nIndent = std::abs(SVBT16ToInt16(rAV.dxaIndent));
569 if( rAV.aBits1 & 0x08 ) //fHang
571 rNum.SetFirstLineOffset( -nIndent );
572 rNum.SetAbsLSpace( nIndent );
574 else
575 rNum.SetCharTextDistance( nIndent ); // width of number is missing
577 if( rAV.nfc == 5 || rAV.nfc == 7 )
579 OUString sP = "." + rNum.GetSuffix();
580 rNum.SetListFormat(u""_ustr, sP, nSwLevel); // ordinal number
582 else
583 rNum.SetListFormat(u""_ustr, u""_ustr, nSwLevel);
586 void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV,
587 const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
589 if (nStart > nElements)
590 return;
592 pText += nStart;
593 nElements -= nStart;
595 bool bInsert = false; // Default
596 rtl_TextEncoding eCharSet = m_eStructCharSet;
598 const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo
599 bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/...
601 sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
602 OUStringBuffer sText(static_cast<sal_Int32>(nLen));
603 if (m_bVer67)
605 if (nLen > nElements)
607 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
608 << nLen << " vs " << nElements << " max");
609 return;
611 sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
612 // ofz#23961 in case of multi-byte input encoding resulting in shorter
613 // output pad to full length with something semi-arbitrary
614 comphelper::string::padToLength(sText, nLen, cBulletChar);
616 else
618 if (nLen > nElements / 2)
620 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
621 << nLen << " vs " << nElements / 2 << " max");
622 return;
624 for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
626 sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText))));
630 if( bOutline )
631 { // outline
632 if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
633 || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
635 // if self defined digits
636 bInsert = true; // then apply character
638 // replace by simple Bullet ?
639 if( bListSymbol )
641 // use cBulletChar for correct mapping on MAC
642 sText.setLength(0);
643 comphelper::string::padToLength(sText, rAV.cbTextBefore
644 + rAV.cbTextAfter, cBulletChar);
648 else
649 { // numbering / bullets
650 bInsert = true;
651 if( bListSymbol )
653 FontFamily eFamily;
654 OUString aName;
655 FontPitch ePitch;
657 if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName,
658 ePitch, eCharSet ) ){
660 vcl::Font aFont;
661 aFont.SetFamilyName( aName );
662 aFont.SetFamily( eFamily );
664 aFont.SetCharSet( eCharSet );
665 rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
667 rNum.SetBulletFont( &aFont );
669 // take only the very first character
670 if (rAV.cbTextBefore || rAV.cbTextAfter)
672 rNum.SetBulletChar(
673 OUString::unacquired(sText).iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
675 else
676 rNum.SetBulletChar( 0x2190 );
680 if( !bInsert )
681 return;
683 OUString sPrefix;
684 OUString sSuffix;
685 if (rAV.cbTextBefore)
687 sPrefix = sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear();
689 if( rAV.cbTextAfter )
691 sSuffix = rNum.GetSuffix() + sText.subView( rAV.cbTextBefore, rAV.cbTextAfter);
694 rNum.SetListFormat(sPrefix, sSuffix, nLevel);
696 // The characters before and after multiple digits do not apply because
697 // those are handled differently by the writer and the result is in most
698 // cases worse than without.
701 // SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
702 // which are provided by pNumR. This is used for everything beside
703 // outline inside the text.
704 void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
705 bool bOutLine)
707 SwNumFormat aNF;
708 aNF.SetListFormat(u""_ustr, u""_ustr, nSwLevel);
709 if (pAD)
710 { // there is an Anld-Sprm
711 m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross;
712 WW8_ANLV const &rAV = pAD->eAnlv;
713 SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
714 SetAnlvStrings(aNF, nSwLevel, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
716 pNumR->Set(nSwLevel, aNF);
719 // chapter numbering and bullets
721 // Chapter numbering happens in the style definition.
722 // Sprm 13 provides the level, Sprm 12 the content.
724 SwNumRule* SwWW8ImplReader::GetStyRule()
726 if( m_xStyles->mpStyRule ) // Bullet-Style already present
727 return m_xStyles->mpStyRule;
729 static constexpr OUString aBaseName(u"WW8StyleNum"_ustr);
730 const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
732 // #i86652#
733 sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr,
734 SvxNumberFormat::LABEL_ALIGNMENT );
735 m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul];
736 // Auto == false-> numbering style
737 m_xStyles->mpStyRule->SetAutoRule(false);
739 return m_xStyles->mpStyRule;
742 // Sprm 13
743 void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
745 m_nSwNumLevel = 0xff; // Default: invalid
747 if( nLen <= 0 )
748 return;
750 // StyleDef ?
751 if( m_pCurrentColl )
753 // only for SwTextFormatColl, not CharFormat
754 // WW: 0 = no Numbering
755 SwWW8StyInf * pColl = GetStyle(m_nCurrentColl);
756 if (pColl != nullptr && pColl->m_bColl && *pData)
758 // Range WW:1..9 -> SW:0..8 no bullets / numbering
760 if (*pData <= 9)
762 m_nSwNumLevel = *pData - 1;
763 if (!m_bNoAttrImport)
764 static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
765 // For WW-NoNumbering also NO_NUMBERING could be used.
766 // ( For normal numberierung NO_NUM has to be used:
767 // NO_NUM : pauses numbering,
768 // NO_NUMBERING : no numbering at all )
771 else if( *pData == 10 || *pData == 11 )
773 // remember type, the rest happens at Sprm 12
774 m_xStyles->mnWwNumLevel = *pData;
778 else
780 //Not StyleDef
781 if (!m_bAnl)
782 StartAnl(pData); // begin of outline / bullets
783 NextAnlLine(pData);
787 void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
789 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
790 if( !m_pCurrentColl || nLen <= 0 // only for Styledef
791 || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
792 || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
794 m_nSwNumLevel = 0xff;
795 return;
798 if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD))
800 SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
801 m_nSwNumLevel = 0xff;
802 return;
805 if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8
808 // If NumRuleItems were set, either directly or through inheritance, disable them now
809 m_pCurrentColl->SetFormatAttr( SwNumRuleItem() );
811 static constexpr OUString aName(u"Outline"_ustr);
812 SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ),
813 SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
814 OUTLINE_RULE );
815 aNR = *m_rDoc.GetOutlineNumRule();
817 SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
819 // Missing Levels need not be replenished
820 m_rDoc.SetOutlineNumRule( aNR );
822 else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){
823 SwNumRule* pNR = GetStyRule();
824 SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
825 m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
827 pStyInf = GetStyle(m_nCurrentColl);
828 if (pStyInf != nullptr)
829 pStyInf->m_bHasStyNumRule = true;
833 // Numbering / Bullets
835 // SetNumOlst() carries the Numrules for this cell to SwNumFormat.
836 // For this the info is fetched from OLST and not from ANLD ( see later )
837 // ( only for outline inside text; Bullets / numbering use ANLDs )
838 void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel)
840 SwNumFormat aNF;
841 WW8_ANLV &rAV = pO->rganlv[nSwLevel];
842 SetBaseAnlv(aNF, rAV, nSwLevel);
843 // ... and then the Strings
844 int nTextOfs = 0;
845 sal_uInt8 i;
846 WW8_ANLV* pAV1; // search String-Positions
847 for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
848 nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
850 if (!m_bVer67)
851 nTextOfs *= 2;
852 SetAnlvStrings(aNF, nSwLevel, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
853 pNumR->Set(nSwLevel, aNF);
856 // The OLST is at the beginning of each section that contains outlines.
857 // The ANLDs that are connected to each outline-line contain only nonsense,
858 // so the OLSTs are remembered for the section to have usable information
859 // when outline-paragraphs occur.
860 void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
862 m_xNumOlst.reset();
863 if (nLen <= 0)
864 return;
866 if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST))
868 SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
869 return;
872 m_xNumOlst.reset(new WW8_OLST);
873 *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
876 WW8LvlType GetNumType(sal_uInt8 nWwLevelNo)
878 WW8LvlType nRet = WW8_None;
879 if( nWwLevelNo == 12 )
880 nRet = WW8_Pause;
881 else if( nWwLevelNo == 10 )
882 nRet = WW8_Numbering;
883 else if( nWwLevelNo == 11 )
884 nRet = WW8_Sequence;
885 else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
886 nRet = WW8_Outline;
887 return nRet;
890 SwNumRule *ANLDRuleMap::GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType)
892 const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
893 if (rNumRule.isEmpty())
894 return nullptr;
895 return rDoc.FindNumRulePtr(rNumRule);
898 void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
900 if (WW8_Numbering == nNumType)
901 msNumberingNumRule = rNumRule;
902 else
903 msOutlineNumRule = rNumRule;
906 // StartAnl is called at the beginning of a row area that contains
907 // outline / numbering / bullets
908 void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13)
910 m_bCurrentAND_fNumberAcross = false;
912 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
913 if (nT == WW8_Pause || nT == WW8_None)
914 return;
916 m_nWwNumType = nT;
917 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
919 // check for COL numbering:
920 SprmResult aS12; // sprmAnld
921 OUString sNumRule;
923 if (m_xTableDesc)
925 sNumRule = m_xTableDesc->GetNumRuleName();
926 if (!sNumRule.isEmpty())
928 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
929 if (!pNumRule)
930 sNumRule.clear();
931 else
933 // this is ROW numbering ?
934 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
935 if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
936 sNumRule.clear();
941 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
942 if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
944 sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
945 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
946 if (!pNumRule)
947 sNumRule.clear();
950 if (sNumRule.isEmpty())
952 if (!pNumRule)
954 // #i86652#
955 pNumRule = m_rDoc.GetNumRuleTable()[
956 m_rDoc.MakeNumRule( sNumRule, nullptr,
957 SvxNumberFormat::LABEL_ALIGNMENT ) ];
959 if (m_xTableDesc)
961 if (!aS12.pSprm)
962 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
963 if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
964 m_xTableDesc->SetNumRuleName(pNumRule->GetName());
968 m_bAnl = true;
970 sNumRule = pNumRule ? pNumRule->GetName() : OUString();
971 // set NumRules via stack
972 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(),
973 SfxStringItem(RES_FLTR_NUMRULE, sNumRule));
975 m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
978 // NextAnlLine() is called once for every row of a
979 // outline / numbering / bullet
980 void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13)
982 if (!m_bAnl)
983 return;
985 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
987 // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3
989 // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0
990 if (*pSprm13 == 10 || *pSprm13 == 11)
992 m_nSwNumLevel = 0;
993 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
995 // not defined yet
996 // sprmAnld o. 0
997 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
998 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
999 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1002 else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
1004 m_nSwNumLevel = *pSprm13 - 1; // outline
1005 // undefined
1006 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1008 if (m_xNumOlst) // there was a OLST
1010 //Assure upper levels are set, #i9556#
1011 for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
1013 if (!pNumRule->GetNumFormat(nI))
1014 SetNumOlst(pNumRule, m_xNumOlst.get(), nI);
1017 SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel);
1019 else // no Olst -> use Anld
1021 // sprmAnld
1022 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1023 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1024 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1028 else
1029 m_nSwNumLevel = 0xff; // no number
1031 SwTextNode* pNd = m_pPaM->GetPointNode().GetTextNode();
1032 if (!pNd)
1033 return;
1035 if (m_nSwNumLevel < MAXLEVEL)
1036 pNd->SetAttrListLevel( m_nSwNumLevel );
1037 else
1039 pNd->SetAttrListLevel(0);
1040 pNd->SetCountedInList( false );
1044 void SwWW8ImplReader::StopAllAnl(bool bGoBack)
1046 //Of course we're not restarting, but we'll make use of our knowledge
1047 //of the implementation to do it.
1048 StopAnlToRestart(WW8_None, bGoBack);
1051 void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack)
1053 if (bGoBack)
1055 SwPosition aTmpPos(*m_pPaM->GetPoint());
1056 m_pPaM->Move(fnMoveBackward, GoInContent);
1057 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1058 *m_pPaM->GetPoint() = std::move(aTmpPos);
1060 else
1061 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1063 m_aANLDRules.msNumberingNumRule.clear();
1065 #i18816#
1066 my take on this problem is that moving either way from an outline to a
1067 numbering doesn't halt the outline, while the numbering is always halted
1069 bool bNumberingNotStopOutline =
1070 (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
1071 ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
1072 if (!bNumberingNotStopOutline)
1073 m_aANLDRules.msOutlineNumRule.clear();
1075 m_nSwNumLevel = 0xff;
1076 m_nWwNumType = WW8_None;
1077 m_bAnl = false;
1080 WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc const & rBand )
1082 *this = rBand;
1083 if( rBand.pTCs )
1085 pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]);
1086 // create uninitialized
1087 memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
1089 if( rBand.pSHDs )
1091 pSHDs = new WW8_SHD[nWwCols];
1092 memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
1094 if( rBand.pNewSHDs )
1096 pNewSHDs = new Color[nWwCols];
1097 memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color));
1099 memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
1102 // ReadDef reads the cell position and the borders of a band
1103 void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen)
1105 --nLen; //reduce len by expected nCols arg
1106 if (nLen < 0)
1107 return;
1108 sal_uInt8 nCols = *pS; // number of cells
1110 if (nCols > MAX_COL)
1111 return;
1113 nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments
1114 if (nLen < 0)
1115 return;
1117 short nOldCols = nWwCols;
1118 nWwCols = nCols;
1120 const sal_uInt8* pT = &pS[1];
1121 for (int i = 0; i <= nCols; i++, pT+=2)
1122 nCenter[i] = SVBT16ToInt16(pT); // X-borders
1124 if( nCols != nOldCols ) // different column count
1126 delete[] pTCs;
1127 pTCs = nullptr;
1128 delete[] pSHDs;
1129 pSHDs = nullptr;
1130 delete[] pNewSHDs;
1131 pNewSHDs = nullptr;
1134 short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
1136 if (!pTCs && nCols)
1138 // create empty TCs
1139 pTCs = new WW8_TCell[nCols];
1142 short nColsToRead = std::min<short>(nFileCols, nCols);
1144 if (nColsToRead <= 0)
1145 return;
1147 // read TCs
1150 Attention: Beginning with Ver8 there is an extra ushort per TC
1151 added and the size of the border code is doubled.
1152 Because of this a simple copy (pTCs[i] = *pTc;)
1153 is not possible.
1155 Advantage: The work structure suits better.
1157 WW8_TCell* pCurrentTC = pTCs;
1158 if( bVer67 )
1160 WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
1161 for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc)
1163 // TC from file ?
1164 sal_uInt8 aBits1 = pTc->aBits1Ver6;
1165 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
1166 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
1167 pCurrentTC->rgbrc[ WW8_TOP ]
1168 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
1169 pCurrentTC->rgbrc[ WW8_LEFT ]
1170 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
1171 pCurrentTC->rgbrc[ WW8_BOT ]
1172 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
1173 pCurrentTC->rgbrc[ WW8_RIGHT ]
1174 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1175 if( ( pCurrentTC->bMerged )
1176 && ( i > 0 ) )
1178 // Cell merged -> remember
1179 //bWWMergedVer6[i] = true;
1180 pTCs[i-1].rgbrc[ WW8_RIGHT ]
1181 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1182 // apply right border to previous cell
1183 // bExist must not be set to false, because WW
1184 // does not count this cells in text boxes...
1188 else
1190 WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
1191 for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc )
1193 sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 );
1194 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
1195 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
1196 pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
1197 pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
1198 pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
1199 pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
1200 pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
1201 pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
1202 // note: in aBits1 there are 7 bits unused,
1203 // followed by another 16 unused bits
1205 pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
1206 pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
1207 pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
1208 pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
1212 // #i25071 In '97 text direction appears to be only set using TC properties
1213 // not with sprmTTextFlow so we need to cycle through the maDirections and
1214 // double check any non-default directions
1215 for (int k = 0; k < nCols; ++k)
1217 if(maDirections[k] == 4)
1219 if(pTCs[k].bVertical)
1221 if(pTCs[k].bBackward)
1222 maDirections[k] = 3;
1223 else
1224 maDirections[k] = 1;
1230 void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen)
1232 if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s)
1233 return;
1235 if (nParamsLen < 3)
1237 SAL_WARN("sw.ww8", "table border property is too short");
1238 return;
1241 sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
1242 sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
1243 sal_uInt8 nFlag = *(pParamsTSetBRC+2);
1245 if (nitcFirst >= nWwCols)
1246 return;
1248 if (nitcLim > nWwCols)
1249 nitcLim = nWwCols;
1251 bool bChangeRight = (nFlag & 0x08) != 0;
1252 bool bChangeBottom = (nFlag & 0x04) != 0;
1253 bool bChangeLeft = (nFlag & 0x02) != 0;
1254 bool bChangeTop = (nFlag & 0x01) != 0;
1256 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1257 WW8_BRCVer9 brcVer9;
1258 if( nBrcVer == 6 )
1260 if (nParamsLen < sizeof(WW8_BRCVer6) + 3)
1262 SAL_WARN("sw.ww8", "table border property is too short");
1263 return;
1265 brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
1267 else if( nBrcVer == 8 )
1269 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1270 if (nParamsLen < sizeof(WW8_BRC) + 3)
1272 SAL_WARN("sw.ww8", "table border property is too short");
1273 return;
1275 brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
1277 else
1279 if (nParamsLen < sizeof(WW8_BRCVer9) + 3)
1281 SAL_WARN("sw.ww8", "table border property is too short");
1282 return;
1284 brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
1287 for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC )
1289 if( bChangeTop )
1290 pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9;
1291 if( bChangeLeft )
1292 pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9;
1293 if( bChangeBottom )
1294 pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9;
1295 if( bChangeRight )
1296 pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9;
1300 void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen)
1302 // sprmTTableBorders
1303 if( nBrcVer == 6 )
1305 if (nParamsLen < sizeof(WW8_BRCVer6) * 6)
1307 SAL_WARN("sw.ww8", "table border property is too short");
1308 return;
1310 WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
1311 for (int i = 0; i < 6; ++i)
1312 aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
1314 else if ( nBrcVer == 8 )
1316 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1317 if (nParamsLen < sizeof(WW8_BRC) * 6)
1319 SAL_WARN("sw.ww8", "table border property is too short");
1320 return;
1322 for( int i = 0; i < 6; ++i )
1323 aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
1325 else
1327 if (nParamsLen < sizeof( aDefBrcs ))
1329 SAL_WARN("sw.ww8", "table border property is too short");
1330 return;
1332 memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
1336 void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol)
1338 // sprmTDxaCol (opcode 0x7623) changes the width of cells
1339 // whose index is within a certain range to be a certain value.
1341 if( !(nWwCols && pParamsTDxaCol) ) // set one or more cell length(s)
1342 return;
1344 sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
1345 sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
1346 short nDxaCol = SVBT16ToInt16(pParamsTDxaCol + 2);
1348 for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
1350 const short nOrgWidth = nCenter[i+1] - nCenter[i];
1351 const short nDelta = nDxaCol - nOrgWidth;
1352 for( int j = i+1; j <= nWwCols; j++ )
1354 nCenter[j] = nCenter[j] + nDelta;
1359 void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert)
1361 if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s)
1362 return;
1364 sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
1365 if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
1366 return;
1367 sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
1368 sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 );
1370 short nNewWwCols;
1371 if (nitcInsert > nWwCols)
1373 nNewWwCols = nitcInsert+nctc;
1374 //if new count would be outside max possible count, clip it, and calc a new replacement
1375 //legal nctc
1376 if (nNewWwCols > MAX_COL)
1378 nNewWwCols = MAX_COL;
1379 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
1382 else
1384 nNewWwCols = nWwCols+nctc;
1385 //if new count would be outside max possible count, clip it, and calc a new replacement
1386 //legal nctc
1387 if (nNewWwCols > MAX_COL)
1389 nNewWwCols = MAX_COL;
1390 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
1394 WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
1396 if (pTCs)
1398 memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
1399 delete[] pTCs;
1401 pTCs = pTC2s;
1403 //If we have to move some cells
1404 if (nitcInsert <= nWwCols)
1406 // adjust the left x-position of the dummy at the very end
1407 nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
1408 for( int i = nWwCols-1; i >= nitcInsert; i--)
1410 // adjust the left x-position
1411 nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
1413 // adjust the cell's borders
1414 pTCs[i + nctc] = pTCs[i];
1418 //if itcMac is larger than full size, fill in missing ones first
1419 for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
1420 nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
1422 //now add in our new cells
1423 for( int j = 0;j < nctc; j++)
1424 nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
1426 nWwCols = nNewWwCols;
1430 void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams)
1432 sal_uInt8 nStartCell = *pParams++;
1433 sal_uInt8 nEndCell = *pParams++;
1434 sal_uInt16 nCode = SVBT16ToUInt16(pParams);
1436 OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
1437 OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
1438 if (nStartCell > MAX_COL)
1439 return;
1440 if (nEndCell > MAX_COL + 1)
1441 nEndCell = MAX_COL + 1;
1443 for (;nStartCell < nEndCell; ++nStartCell)
1444 maDirections[nStartCell] = nCode;
1447 void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams)
1449 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1450 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1451 if (nLen != 6)
1452 return;
1453 mbHasSpacing=true;
1454 #if OSL_DEBUG_LEVEL > 0
1455 sal_uInt8 nWhichCell = *pParams;
1456 OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
1457 #endif
1458 ++pParams; //Skip which cell
1459 ++pParams; //unknown byte
1461 sal_uInt8 nSideBits = *pParams++;
1462 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1463 ++pParams; //unknown byte
1464 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1465 for (int i = wwTOP; i <= wwRIGHT; i++)
1467 switch (nSideBits & (1 << i))
1469 case 1 << wwTOP:
1470 mnDefaultTop = nValue;
1471 break;
1472 case 1 << wwLEFT:
1473 mnDefaultLeft = nValue;
1474 break;
1475 case 1 << wwBOTTOM:
1476 mnDefaultBottom = nValue;
1477 break;
1478 case 1 << wwRIGHT:
1479 mnDefaultRight = nValue;
1480 break;
1481 case 0:
1482 break;
1483 default:
1484 OSL_ENSURE(false, "Impossible");
1485 break;
1490 void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams)
1492 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1493 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1494 if (nLen != 6)
1495 return;
1497 const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to.
1498 const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins.
1499 OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings");
1500 if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 )
1501 return;
1503 sal_uInt8 nSideBits = *pParams++;
1504 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1506 const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin
1507 OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width");
1508 if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored
1509 return;
1511 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1513 for (int nCell = nStartCell; nCell < nEndCell; ++nCell)
1515 nOverrideSpacing[ nCell ] |= nSideBits;
1516 OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits");
1518 for (int i=0; i < 4; i++)
1520 if (nSideBits & (1 << i))
1521 nOverrideValues[ nCell ][ i ] = nValue;
1526 void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete)
1528 if( !(nWwCols && pParamsTDelete) ) // set one or more cell length(s)
1529 return;
1531 sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
1532 if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
1533 return;
1534 sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
1535 if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
1536 return;
1539 * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
1540 * greater than or equal to itcLim to be moved
1542 int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
1544 if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
1546 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1547 int i = 0;
1548 while( i < nShlCnt )
1550 // adjust the left x-position
1551 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1553 // adjust the cell's borders
1554 *pCurrentTC = pTCs[ nitcLim + i];
1556 ++i;
1557 ++pCurrentTC;
1559 // adjust the left x-position of the dummy at the very end
1560 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1563 short nCellsDeleted = nitcLim - nitcFirst;
1564 //clip delete request to available number of cells
1565 if (nCellsDeleted > nWwCols)
1566 nCellsDeleted = nWwCols;
1567 nWwCols -= nCellsDeleted;
1570 // ReadShd reads the background color of a cell
1571 // ReadDef must be called before
1572 void WW8TabBandDesc::ReadShd(const sal_uInt8* pS )
1574 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1575 if( !nLen )
1576 return;
1578 if( !pSHDs )
1580 pSHDs = new WW8_SHD[nWwCols];
1583 short nCount = nLen >> 1;
1584 if (nCount > nWwCols)
1585 nCount = nWwCols;
1587 SVBT16 const * pShd;
1588 int i;
1589 for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
1590 pSHDs[i].SetWWValue( *pShd );
1593 void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart)
1595 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1596 if (!nLen || nStart >= nWwCols)
1597 return;
1599 if (!pNewSHDs)
1600 pNewSHDs = new Color[nWwCols];
1602 short nCount = nLen / 10 + nStart; //10 bytes each
1603 if (nCount > nWwCols)
1604 nCount = nWwCols;
1606 int i=nStart;
1607 while (i < nCount)
1608 pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
1610 while (i < nWwCols)
1611 pNewSHDs[i++] = COL_AUTO;
1614 namespace
1616 SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
1618 if (bVer67)
1619 return pPap->HasSprm(24);
1620 SprmResult aRes = pPap->HasSprm(0x244B);
1621 if (aRes.pSprm == nullptr)
1622 aRes = pPap->HasSprm(0x2416);
1623 return aRes;
1627 namespace {
1629 enum wwTableSprm
1631 sprmNil,
1633 sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
1634 sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft,
1635 sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete,
1636 sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
1637 sprmTDefTableNewShd, sprmTDefTableNewShd2nd, sprmTDefTableNewShd3rd,
1638 sprmTCellPadding, sprmTCellPaddingDefault
1643 static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
1645 switch (eVer)
1647 case ww::eWW8:
1648 switch (nId)
1650 case NS_sprm::TTableWidth::val:
1651 return sprmTTableWidth;
1652 case NS_sprm::TTextFlow::val:
1653 return sprmTTextFlow;
1654 case NS_sprm::TTableHeader::val:
1655 return sprmTTableHeader;
1656 case NS_sprm::TFCantSplit::val:
1657 return sprmTFCantSplit;
1658 case NS_sprm::TJc90::val:
1659 return sprmTJc;
1660 case NS_sprm::TFBiDi::val:
1661 return sprmTFBiDi;
1662 case NS_sprm::TDelete::val:
1663 return sprmTDelete;
1664 case NS_sprm::TInsert::val:
1665 return sprmTInsert;
1666 case NS_sprm::TDxaCol::val:
1667 return sprmTDxaCol;
1668 case NS_sprm::TDyaRowHeight::val:
1669 return sprmTDyaRowHeight;
1670 case NS_sprm::TDxaLeft::val:
1671 return sprmTDxaLeft;
1672 case NS_sprm::TDxaGapHalf::val:
1673 return sprmTDxaGapHalf;
1674 case NS_sprm::TTableBorders80::val:
1675 return sprmTTableBorders;
1676 case NS_sprm::TDefTable::val:
1677 return sprmTDefTable;
1678 case NS_sprm::TDefTableShd80::val:
1679 return sprmTDefTableShd;
1680 case NS_sprm::TDefTableShd::val:
1681 return sprmTDefTableNewShd;
1682 case NS_sprm::TDefTableShd2nd::val:
1683 return sprmTDefTableNewShd2nd;
1684 case NS_sprm::TDefTableShd3rd::val:
1685 return sprmTDefTableNewShd3rd;
1686 case NS_sprm::TTableBorders::val:
1687 return sprmTTableBorders90;
1688 case NS_sprm::TSetBrc80::val:
1689 return sprmTSetBrc;
1690 case NS_sprm::TSetBrc::val:
1691 return sprmTSetBrc90;
1692 case NS_sprm::TCellPadding::val:
1693 return sprmTCellPadding;
1694 case NS_sprm::TCellPaddingDefault::val:
1695 return sprmTCellPaddingDefault;
1697 break;
1698 case ww::eWW7:
1699 case ww::eWW6:
1700 switch (nId)
1702 case 182:
1703 return sprmTJc;
1704 case 183:
1705 return sprmTDxaLeft;
1706 case 184:
1707 return sprmTDxaGapHalf;
1708 case 186:
1709 return sprmTTableHeader;
1710 case 187:
1711 return sprmTTableBorders;
1712 case 189:
1713 return sprmTDyaRowHeight;
1714 case 190:
1715 return sprmTDefTable;
1716 case 191:
1717 return sprmTDefTableShd;
1718 case 193:
1719 return sprmTSetBrc;
1720 case 194:
1721 return sprmTInsert;
1722 case 195:
1723 return sprmTDelete;
1724 case 196:
1725 return sprmTDxaCol;
1727 break;
1728 case ww::eWW1:
1729 case ww::eWW2:
1730 switch (nId)
1732 case 146:
1733 return sprmTJc;
1734 case 147:
1735 return sprmTDxaLeft;
1736 case 148:
1737 return sprmTDxaGapHalf;
1738 case 153:
1739 return sprmTDyaRowHeight;
1740 case 154:
1741 return sprmTDefTable;
1742 case 155:
1743 return sprmTDefTableShd;
1744 case 157:
1745 return sprmTSetBrc;
1746 case 158:
1747 return sprmTInsert;
1748 case 159:
1749 return sprmTDelete;
1750 case 160:
1751 return sprmTDxaCol;
1753 break;
1755 return sprmNil;
1758 WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) :
1759 m_pIo(pIoClass),
1760 m_pFirstBand(nullptr),
1761 m_pActBand(nullptr),
1762 m_pTableNd(nullptr),
1763 m_pTabLines(nullptr),
1764 m_pTabLine(nullptr),
1765 m_pTabBoxes(nullptr),
1766 m_pTabBox(nullptr),
1767 m_pCurrentWWCell(nullptr),
1768 m_nRows(0),
1769 m_nDefaultSwCols(0),
1770 m_nBands(0),
1771 m_nMinLeft(0),
1772 m_nMaxRight(0),
1773 m_nSwWidth(0),
1774 m_nPreferredWidth(0),
1775 m_nPercentWidth(0),
1776 m_bOk(true),
1777 m_bClaimLineFormat(false),
1778 m_eOri(text::HoriOrientation::LEFT),
1779 m_bIsBiDi(false),
1780 m_nCurrentRow(0),
1781 m_nCurrentBandRow(0),
1782 m_nCurrentCol(0),
1783 m_nRowsToRepeat(0),
1784 m_pTable(nullptr),
1785 m_pParentPos(nullptr),
1786 m_pFlyFormat(nullptr),
1787 m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>)
1789 m_pIo->m_bCurrentAND_fNumberAcross = false;
1791 static const sal_Int16 aOriArr[] =
1793 text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
1796 bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
1797 WW8_TablePos aTabPos;
1799 WW8PLCFxSave1 aSave;
1800 m_pIo->m_xPlcxMan->GetPap()->Save( aSave );
1802 WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF();
1804 WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
1806 wwSprmParser aSprmParser(m_pIo->GetFib());
1808 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
1810 // process pPap until end of table found
1813 short nTabeDxaNew = SHRT_MAX;
1814 bool bTabRowJustRead = false;
1815 const sal_uInt8* pShadeSprm = nullptr;
1816 const sal_uInt8* pNewShadeSprm[3] = {nullptr, nullptr, nullptr};
1817 const sal_uInt8* pTableBorders = nullptr;
1818 sal_uInt16 nTableBordersLen = 0;
1819 const sal_uInt8* pTableBorders90 = nullptr;
1820 sal_uInt16 nTableBorders90Len = 0;
1821 // params, len
1822 std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s;
1823 WW8_TablePos *pTabPos = nullptr;
1825 // search end of a tab row
1826 if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
1828 m_bOk = false;
1829 break;
1832 // Get the SPRM chains:
1833 // first from PAP and then from PCD (of the Piece Table)
1834 WW8PLCFxDesc aDesc;
1835 pPap->GetSprms( &aDesc );
1836 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
1838 for (int nLoop = 0; nLoop < 2; ++nLoop)
1840 const sal_uInt8* pParams;
1841 while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams()))
1843 sal_uInt16 nId = aSprmIter.GetCurrentId();
1844 sal_Int32 nFixedLen = aSprmParser.DistanceToData(nId);
1845 sal_Int32 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen());
1846 sal_Int32 nLen = nL - nFixedLen;
1847 wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
1848 switch (eSprm)
1850 case sprmTTableWidth:
1852 const sal_uInt8 b0 = pParams[0];
1853 const sal_uInt8 b1 = pParams[1];
1854 const sal_uInt8 b2 = pParams[2];
1855 if (b0 == 3) // Twips
1856 m_nPreferredWidth = b2 * 0x100 + b1;
1857 else if (b0 == 2) // percent in fiftieths of a percent
1859 m_nPercentWidth = (b2 * 0x100 + b1);
1860 // MS documentation: non-negative, and 600% max
1861 if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 )
1862 m_nPercentWidth *= .02;
1863 else
1864 m_nPercentWidth = 100;
1867 break;
1868 case sprmTTextFlow:
1869 pNewBand->ProcessDirection(pParams);
1870 break;
1871 case sprmTFCantSplit:
1872 pNewBand->bCantSplit = *pParams;
1873 m_bClaimLineFormat = true;
1874 break;
1875 case sprmTTableBorders:
1876 pTableBorders = pParams; // process at end
1877 nTableBordersLen = nLen;
1878 break;
1879 case sprmTTableBorders90:
1880 pTableBorders90 = pParams; // process at end
1881 nTableBorders90Len = nLen;
1882 break;
1883 case sprmTTableHeader:
1884 // tdf#105570
1885 if ( m_nRowsToRepeat == m_nRows )
1886 m_nRowsToRepeat = (m_nRows + 1);
1887 break;
1888 case sprmTJc:
1889 // sprmTJc - Justification Code
1890 if (m_nRows == 0)
1891 m_eOri = aOriArr[*pParams & 0x3];
1892 break;
1893 case sprmTFBiDi:
1894 m_bIsBiDi = SVBT16ToUInt16(pParams) != 0;
1895 break;
1896 case sprmTDxaGapHalf:
1897 pNewBand->nGapHalf = SVBT16ToInt16(pParams);
1898 break;
1899 case sprmTDyaRowHeight:
1900 pNewBand->nLineHeight = SVBT16ToInt16(pParams);
1901 m_bClaimLineFormat = true;
1902 break;
1903 case sprmTDefTable:
1904 pNewBand->ReadDef(bOldVer, pParams, nLen);
1905 bTabRowJustRead = true;
1906 break;
1907 case sprmTDefTableShd:
1908 pShadeSprm = pParams;
1909 break;
1910 case sprmTDefTableNewShd:
1911 pNewShadeSprm[0] = pParams;
1912 break;
1913 case sprmTDefTableNewShd2nd:
1914 pNewShadeSprm[1] = pParams;
1915 break;
1916 case sprmTDefTableNewShd3rd:
1917 pNewShadeSprm[2] = pParams;
1918 break;
1919 case sprmTDxaLeft:
1920 // our Writer cannot shift single table lines
1921 // horizontally so we have to find the smallest
1922 // parameter (meaning the left-most position) and then
1923 // shift the whole table to that margin (see below)
1925 short nDxaNew = SVBT16ToInt16(pParams);
1926 if( nDxaNew < nTabeDxaNew )
1927 nTabeDxaNew = nDxaNew;
1929 break;
1930 case sprmTSetBrc:
1931 aTSetBrcs.emplace_back(pParams, nLen); // process at end
1932 break;
1933 case sprmTSetBrc90:
1934 aTSetBrc90s.emplace_back(pParams, nLen); // process at end
1935 break;
1936 case sprmTDxaCol:
1937 pNewBand->ProcessSprmTDxaCol(pParams);
1938 break;
1939 case sprmTInsert:
1940 pNewBand->ProcessSprmTInsert(pParams);
1941 break;
1942 case sprmTDelete:
1943 pNewBand->ProcessSprmTDelete(pParams);
1944 break;
1945 case sprmTCellPaddingDefault:
1946 pNewBand->ProcessSpacing(pParams);
1947 break;
1948 case sprmTCellPadding:
1949 pNewBand->ProcessSpecificSpacing(pParams);
1950 break;
1951 default:
1954 aSprmIter.advance();
1957 if( !nLoop )
1959 pPap->GetPCDSprms( aDesc );
1960 aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
1964 // WW-Tables can contain Fly-changes. For this abort tables here
1965 // and start again. *pPap is still before TabRowEnd, so TestApo()
1966 // can be called with the last parameter set to false and therefore
1967 // take effect.
1969 if (bTabRowJustRead)
1971 // Some SPRMs need to be processed *after* ReadDef is called
1972 // so they were saved up until here
1973 if (pShadeSprm)
1974 pNewBand->ReadShd(pShadeSprm);
1975 if (pNewShadeSprm[0])
1976 pNewBand->ReadNewShd(pNewShadeSprm[0], bOldVer, /*nStart=*/0);
1977 if (pNewShadeSprm[1])
1978 pNewBand->ReadNewShd(pNewShadeSprm[1], bOldVer, /*nStart=*/22);
1979 if (pNewShadeSprm[2])
1980 pNewBand->ReadNewShd(pNewShadeSprm[2], bOldVer, /*nStart=*/44);
1981 if (pTableBorders90)
1982 pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len);
1983 else if (pTableBorders)
1984 pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
1985 pTableBorders, nTableBordersLen);
1986 for (const auto& a : aTSetBrcs)
1987 pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second);
1988 for (const auto& a : aTSetBrc90s)
1989 pNewBand->ProcessSprmTSetBRC(9, a.first, a.second);
1992 if( nTabeDxaNew < SHRT_MAX )
1994 short* pCenter = pNewBand->nCenter;
1995 short firstDxaCenter = *pCenter;
1996 for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
1998 // #i30298# Use sprmTDxaLeft to adjust the left indent
1999 // #i40461# Use dxaGapHalf during calculation
2000 *pCenter +=
2001 (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
2005 if (!m_pActBand)
2006 m_pActBand = m_pFirstBand = pNewBand;
2007 else
2009 m_pActBand->pNextBand = pNewBand;
2010 m_pActBand = pNewBand;
2012 m_nBands++;
2014 pNewBand = new WW8TabBandDesc;
2016 m_nRows++;
2017 m_pActBand->nRows++;
2019 //Seek our pap to its next block of properties
2020 WW8PLCFxDesc aRes;
2021 aRes.pMemPos = nullptr;
2022 aRes.nStartPos = nStartCp;
2024 if (!(pPap->SeekPos(aRes.nStartPos)))
2026 aRes.nEndPos = WW8_CP_MAX;
2027 pPap->SetDirty(true);
2029 pPap->GetSprms(&aRes);
2030 pPap->SetDirty(false);
2032 //Are we at the end of available properties
2033 if (
2034 !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
2035 aRes.nStartPos == WW8_CP_MAX
2038 m_bOk = false;
2039 break;
2042 //Are we still in a table cell
2043 SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer);
2044 const sal_uInt8* pParams = aParamsRes.pSprm;
2045 SprmResult aLevelRes = pPap->HasSprm(0x6649);
2046 const sal_uInt8 *pLevel = aLevelRes.pSprm;
2047 // InTable
2048 if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) ||
2049 (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable)))
2051 break;
2054 //Get the end of row new table positioning data
2055 WW8_CP nMyStartCp=nStartCp;
2056 if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
2057 if (SwWW8ImplReader::ParseTabPos(&aTabPos, pPap))
2058 pTabPos = &aTabPos;
2060 //Move back to this cell
2061 aRes.pMemPos = nullptr;
2062 aRes.nStartPos = nStartCp;
2064 // PlcxMan currently points too far ahead so we need to bring
2065 // it back to where we are trying to make a table
2066 m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
2067 m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
2068 if (!(pPap->SeekPos(aRes.nStartPos)))
2070 aRes.nEndPos = WW8_CP_MAX;
2071 pPap->SetDirty(true);
2073 pPap->GetSprms(&aRes);
2074 pPap->SetDirty(false);
2076 //Does this row match up with the last row closely enough to be
2077 //considered part of the same table
2078 ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
2081 ##513##, #79474# If this is not sufficient, then we should look at
2082 sprmPD{y|x}aAbs as our indicator that the following set of rows is not
2083 part of this table, but instead is an absolutely positioned table
2084 outside of this one
2086 if (aApo.mbStopApo)
2087 break;
2088 if (aApo.mbStartApo)
2090 //if there really is a fly here, and not a "null" fly then break.
2091 if (m_pIo->ConstructApo(aApo, pTabPos))
2092 break;
2095 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
2096 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
2098 SAL_WARN("sw.ww8", "WW8TabDesc, loop in paragraph property chain");
2099 break;
2101 nStartCp = aRes.nEndPos;
2103 while(true);
2105 if( m_bOk )
2107 if( m_pActBand->nRows > 1 )
2109 // last band has more than 1 cell
2110 delete pNewBand;
2111 pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
2112 m_pActBand->nRows--; // because of special treatment of border defaults
2113 pNewBand->nRows = 1;
2114 m_pActBand->pNextBand = pNewBand; // append at the end
2115 m_nBands++;
2116 pNewBand = nullptr; // do not delete
2118 CalcDefaults();
2120 delete pNewBand;
2122 m_pIo->m_xPlcxMan->GetPap()->Restore( aSave );
2125 WW8TabDesc::~WW8TabDesc()
2127 WW8TabBandDesc* pR = m_pFirstBand;
2128 while(pR)
2130 WW8TabBandDesc* pR2 = pR->pNextBand;
2131 delete pR;
2132 pR = pR2;
2135 delete m_pParentPos;
2138 void WW8TabDesc::CalcDefaults()
2140 short nMinCols = SHRT_MAX;
2141 WW8TabBandDesc* pR;
2143 m_nMinLeft = SHRT_MAX;
2144 m_nMaxRight = SHRT_MIN;
2147 If we are an honestly inline centered table, then the normal rules of
2148 engagement for left and right margins do not apply. The multiple rows are
2149 centered regardless of the actual placement of rows, so we cannot have
2150 mismatched rows as is possible in other configurations.
2152 e.g. change the example bugdoc in word from text wrapping of none (inline)
2153 to around (in frame (bApo)) and the table splits into two very disjoint
2154 rows as the beginning point of each row are very different
2156 if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
2158 for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
2159 for( short i = pR->nWwCols; i >= 0; --i)
2160 pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
2163 // First loop: find outermost L and R borders
2164 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2166 if( pR->nCenter[0] < m_nMinLeft )
2167 m_nMinLeft = pR->nCenter[0];
2169 // Following adjustment moves a border and then uses it to find width
2170 // of next cell, so collect current widths, to avoid situation when width
2171 // adjustment to too narrow cell makes next cell have negative width
2172 short nOrigWidth[MAX_COL + 1];
2173 for( short i = 0; i < pR->nWwCols; i++ )
2175 nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
2178 for( short i = 0; i < pR->nWwCols; i++ )
2181 If the margins are so large as to make the displayable
2182 area inside them smaller than the minimum allowed then adjust the
2183 width to fit. But only do it if the two cells are not the exact
2184 same value, if they are then the cell does not really exist and will
2185 be blended together into the same cell through the use of the
2186 nTrans(late) array.
2187 #i28333# If the nGapHalf is greater than the cell width best to ignore it
2189 int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
2190 if (nCellWidth != nOrigWidth[i])
2192 if (nOrigWidth[i] == 0)
2193 nCellWidth = 0; // restore zero-width "cell"
2194 else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
2195 nCellWidth = pR->nGapHalf + 1; // avoid false ignore
2196 else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
2197 nCellWidth = 1; // minimal non-zero width to minimize distortion
2199 if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
2201 nCellWidth = MINLAY + pR->nGapHalf * 2;
2203 pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
2206 if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
2207 m_nMaxRight = pR->nCenter[pR->nWwCols];
2209 m_nSwWidth = m_nMaxRight - m_nMinLeft;
2211 // If the table is right aligned we need to align all rows to the
2212 // row that has the furthest right point
2214 if(m_eOri == text::HoriOrientation::RIGHT)
2216 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2218 int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
2219 for( short i = 0; i < pR->nWwCols + 1; i++ )
2221 pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
2227 // 2. pass: Detect number of writer columns. This can exceed the count
2228 // of columns in WW by 2, because SW in contrast to WW does not provide
2229 // fringed left and right borders and has to fill with empty boxes.
2230 // Non existent cells can reduce the number of columns.
2232 // 3. pass: Replace border with defaults if needed
2233 for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
2235 if( !pR->pTCs )
2237 pR->pTCs = new WW8_TCell[ pR->nWwCols ];
2239 for (int k = 0; k < pR->nWwCols; ++k)
2241 WW8_TCell& rT = pR->pTCs[k];
2242 for (int i = 0; i < 4; ++i)
2244 if (rT.rgbrc[i].brcType()==0)
2246 // if shadow is set, its invalid
2247 int j = i;
2248 switch( i )
2250 case 0:
2251 // outer top / horizontally inside
2252 j = (pR == m_pFirstBand) ? 0 : 4;
2253 break;
2254 case 1:
2255 // outer left / vertically inside
2256 j = k ? 5 : 1;
2257 break;
2258 case 2:
2259 // outer bottom / horizontally inside
2260 j = pR->pNextBand ? 4 : 2;
2261 break;
2262 case 3:
2263 // outer right / vertically inside
2264 j = (k == pR->nWwCols - 1) ? 3 : 5;
2265 break;
2267 // merge with above defaults
2268 rT.rgbrc[i] = pR->aDefBrcs[j];
2274 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2276 pR->nSwCols = pR->nWwCols;
2277 pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
2278 pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols]) >= MINLAY;
2280 short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
2281 sal_uInt16 i;
2282 sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
2283 for (i = 0; i < pR->nWwCols; ++i)
2285 pR->nTransCell[i] = static_cast<sal_Int8>(j);
2286 if ( pR->nCenter[i] < pR->nCenter[i+1] )
2288 pR->bExist[i] = true;
2289 j++;
2291 else
2293 pR->bExist[i] = false;
2294 nAddCols--;
2298 OSL_ENSURE(i,"no columns in row ?");
2301 If the last cell was "false" then there is no valid cell following it,
2302 so the default mapping forward won't work. So map it (and
2303 contiguous invalid cells backwards to the last valid cell instead.)
2305 if (i && !pR->bExist[i-1])
2307 sal_uInt16 k=i-1;
2308 while (k && !pR->bExist[k])
2309 k--;
2310 for (sal_uInt16 n=k+1;n<i;n++)
2311 pR->nTransCell[n] = pR->nTransCell[k];
2314 pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other
2315 pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol
2317 pR->nSwCols = pR->nSwCols + nAddCols;
2318 if( pR->nSwCols < nMinCols )
2319 nMinCols = pR->nSwCols;
2322 if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
2323 (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
2324 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
2326 m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
2327 if( m_nDefaultSwCols == 0 )
2328 m_bOk = false;
2329 m_pActBand = m_pFirstBand;
2330 m_nCurrentBandRow = 0;
2331 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2334 void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat)
2336 SwFrameFormat* pApply = pFrameFormat;
2337 if (!pApply )
2338 pApply = m_pTable->GetFrameFormat();
2339 OSL_ENSURE(pApply,"No frame");
2340 pApply->SetFormatAttr(m_aItemSet);
2341 if (pFrameFormat)
2343 SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
2344 aSize.SetHeightSizeType(SwFrameSize::Minimum);
2345 aSize.SetHeight(MINLAY);
2346 pFrameFormat->SetFormatAttr(aSize);
2347 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
2351 void wwSectionManager::PrependedInlineNode(const SwPosition &rPos,
2352 const SwNode &rNode)
2354 OSL_ENSURE(!maSegments.empty(),
2355 "should not be possible, must be at least one segment");
2356 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
2357 maSegments.back().maStart.Assign(rNode);
2360 void WW8TabDesc::CreateSwTable()
2362 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
2364 // if there is already some content on the Node append new node to ensure
2365 // that this content remains ABOVE the table
2366 SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
2367 bool bInsNode = pPoint->GetContentIndex() != 0;
2368 bool bSetMinHeight = false;
2371 #i8062#
2372 Set fly anchor to its anchor pos, so that if a table starts immediately
2373 at this position a new node will be inserted before inserting the table.
2375 SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo)
2376 ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr;
2377 if (pFormat)
2379 const SwNode* pAnchorNode =
2380 pFormat->GetAnchor().GetAnchorNode();
2381 if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
2383 bInsNode = true;
2384 bSetMinHeight = true;
2386 SwFormatSurround aSur(pFormat->GetSurround());
2387 aSur.SetAnchorOnly(true);
2388 pFormat->SetFormatAttr(aSur);
2392 if (bSetMinHeight)
2394 // minimize Fontsize to minimize height growth of the header/footer
2395 // set font size to 1 point to minimize y-growth of Hd/Ft
2396 SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE);
2397 m_pIo->NewAttr( aSz );
2398 m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
2401 if (bInsNode)
2402 m_pIo->FinalizeTextNode(*pPoint);
2404 m_xTmpPos = m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pPaM->GetPoint());
2406 // Because SW cannot handle multi-page floating frames,
2407 // _any unnecessary_ floating tables have been converted to inline.
2408 tools::Long nLeft = 0;
2409 if (m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->GetFlyFormat())
2411 // Get the table orientation from the fly
2412 // Do we also need to check m_pIo->m_xSFlyPara->bTogglePos/IsPosToggle()? [Probably not - layout-only concern]
2413 const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos;
2414 const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT
2415 : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT;
2416 if ( bIsInsideMargin && bAdjustMargin )
2417 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH;
2418 else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE )
2419 m_eOri = m_pIo->m_xSFlyPara->eHAlign;
2420 if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH )
2422 nLeft = m_pIo->m_xSFlyPara->nXPos;
2423 if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME )
2425 if ( !m_bIsBiDi )
2426 nLeft -= m_pIo->m_aSectionManager.GetPageLeft();
2427 else
2428 nLeft += m_pIo->m_aSectionManager.GetPageRight();
2433 // The table is small: The number of columns is the lowest count of
2434 // columns of the origin, because inserting is faster than deleting.
2435 // The number of rows is the count of bands because (identically)
2436 // rows of a band can be duplicated easy.
2437 m_pTable = m_pIo->m_rDoc.InsertTable(
2438 SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 0 ),
2439 *m_xTmpPos->GetPoint(), m_nBands, m_nDefaultSwCols, m_eOri );
2441 OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
2442 if (!m_pTable || !m_pTable->GetFrameFormat())
2443 return;
2445 SwTableNode* pTableNode = m_pTable->GetTableNode();
2446 OSL_ENSURE(pTableNode, "no table node!");
2447 if (pTableNode)
2449 m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(),
2450 *pTableNode);
2453 // Check if the node into which the table should be inserted already
2454 // contains a Pagedesc. If so that Pagedesc would be moved to the
2455 // row after the table, that would be wrong. So delete and
2456 // set later to the table format.
2457 if (SwTextNode *const pNd = m_xTmpPos->GetPoint()->GetNode().GetTextNode())
2459 if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
2461 std::unique_ptr<SfxPoolItem> pSetAttr;
2463 if (const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet(RES_BREAK, false))
2465 pSetAttr.reset(new SvxFormatBreakItem( *pBreakItem ));
2466 pNd->ResetAttr( RES_BREAK );
2469 // eventually set the PageDesc/Break now to the table
2470 if (pSetAttr)
2472 m_aItemSet.Put(std::move(pSetAttr));
2477 // total width of table
2478 if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols )
2480 SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, m_nSwWidth);
2481 // Don't set relative width if the table is in a floating frame
2482 if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->GetFlyFormat()) )
2483 aFrameSize.SetWidthPercent(m_nPercentWidth);
2484 m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize);
2485 m_aItemSet.Put(aFrameSize);
2488 SvxFrameDirectionItem aDirection(
2489 m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
2490 m_pTable->GetFrameFormat()->SetFormatAttr(aDirection);
2492 if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
2494 if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_xSFlyPara &&
2495 m_pIo->m_xSFlyPara->GetFlyFormat() && GetMinLeft())
2497 //If we are inside a frame and we have a border, the frames
2498 //placement does not consider the tables border, which word
2499 //displays outside the frame, so adjust here.
2500 SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->GetFlyFormat()->GetHoriOrient());
2501 sal_Int16 eHori = aHori.GetHoriOrient();
2502 if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
2503 (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
2505 //With multiple table, use last table settings. Perhaps
2506 //the maximum is what word does ?
2507 aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft());
2508 aHori.SetHoriOrient(text::HoriOrientation::NONE);
2509 m_pIo->m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aHori);
2512 else // Not directly in a floating frame.
2514 //Historical note: If InLocalApo(), then this table is being placed in a floating
2515 //frame, and the frame matches the left and right *lines* of the
2516 //table, so the space to the left of the table isn't to be used
2517 //inside the frame, in word the dialog involved greys out the
2518 //ability to set the margin.
2519 SvxLRSpaceItem aL( RES_LR_SPACE );
2521 if (!m_bIsBiDi)
2522 nLeft += GetMinLeft();
2523 else
2525 const SwTwips nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth;
2526 nLeft += m_pIo->m_aSectionManager.GetTextAreaWidth();
2527 nLeft = nLeft - nTableWidth - GetMinLeft();
2529 aL.SetLeft(SvxIndentValue::twips(nLeft));
2531 m_aItemSet.Put(aL);
2535 mxOldRedlineStack = std::move(m_pIo->m_xRedlineStack);
2536 m_pIo->m_xRedlineStack.reset(new sw::util::RedlineStack(m_pIo->m_rDoc));
2539 void WW8TabDesc::UseSwTable()
2541 // init global Vars
2542 m_pTabLines = &m_pTable->GetTabLines();
2543 m_nCurrentRow = m_nCurrentCol = m_nCurrentBandRow = 0;
2545 m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
2546 GetSttNd()->FindTableNode());
2547 OSL_ENSURE( m_pTableNd, "Where is my table node" );
2549 // #i69519# - Restrict rows to repeat to a decent value
2550 if ( m_nRowsToRepeat == o3tl::narrowing<sal_uInt16>(m_nRows) )
2551 m_nRowsToRepeat = 1;
2553 m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat );
2554 // insert extra cells if needed and something like this
2555 AdjustNewBand();
2557 WW8DupProperties aDup(m_pIo->m_rDoc, m_pIo->m_xCtrlStck.get());
2558 m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
2560 // now set the correct PaM and prepare first merger group if any
2561 SetPamInCell(m_nCurrentCol, true);
2562 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2564 m_pIo->m_bWasTabRowEnd = false;
2565 m_pIo->m_bWasTabCellEnd = false;
2568 void WW8TabDesc::MergeCells()
2570 short nRow;
2572 for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand)
2574 // insert current box into merge group if appropriate.
2575 // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
2576 if( m_pActBand->pTCs )
2578 for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
2579 for( short i = 0; i < m_pActBand->nWwCols; i++ )
2581 WW8SelBoxInfo* pActMGroup = nullptr;
2583 // start a new merge group if appropriate
2585 OSL_ENSURE(nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()),
2586 "Too few lines, table ended early");
2587 if (nRow >= o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2588 return;
2589 m_pTabLine = (*m_pTabLines)[ nRow ];
2590 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2592 sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
2593 if (!m_pActBand->bExist[i])
2594 continue;
2595 OSL_ENSURE(nCol < m_pTabBoxes->size(),
2596 "Too few columns, table ended early");
2597 if (nCol >= m_pTabBoxes->size())
2598 return;
2599 m_pTabBox = (*m_pTabBoxes)[nCol];
2600 WW8_TCell& rCell = m_pActBand->pTCs[ i ];
2601 // is this the left upper cell of a merge group ?
2603 bool bMerge = false;
2604 if ( rCell.bVertRestart && !rCell.bMerged )
2605 bMerge = true;
2606 else if (rCell.bFirstMerged && m_pActBand->bExist[i])
2608 // Some tests to avoid merging cells which previously were
2609 // declared invalid because of sharing the exact same dimensions
2610 // as their previous cell
2612 //If there's anything underneath/above we're ok.
2613 if (rCell.bVertMerge || rCell.bVertRestart)
2614 bMerge = true;
2615 else
2617 //If it's a hori merge only, and the only things in
2618 //it are invalid cells then it's already taken care
2619 //of, so don't merge.
2620 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2621 if (m_pActBand->pTCs[ i2 ].bMerged &&
2622 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2624 if (m_pActBand->bExist[i2])
2626 bMerge = true;
2627 break;
2630 else
2631 break;
2635 // remove numbering from cells that will be disabled in the merge
2636 if( rCell.bVertMerge && !rCell.bVertRestart )
2638 SwPaM aPam( *m_pTabBox->GetSttNd(), 0 );
2639 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2640 SwTextNode* pNd = aPam.GetPointNode().GetTextNode();
2641 while( pNd )
2643 pNd->SetCountedInList( false );
2645 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2646 pNd = aPam.GetPointNode().GetTextNode();
2650 if (bMerge)
2652 short nX1 = m_pActBand->nCenter[ i ];
2653 short nWidth = m_pActBand->nWidth[ i ];
2655 // 2. create current merge group
2656 pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
2658 // determine size of new merge group
2659 // before inserted the new merge group.
2660 // Needed to correctly locked previously created merge groups.
2661 // Calculate total width and set
2662 short nSizCell = m_pActBand->nWidth[ i ];
2663 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2664 if (m_pActBand->pTCs[ i2 ].bMerged &&
2665 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2667 nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
2669 else
2670 break;
2671 pActMGroup->m_nGroupWidth = nSizCell;
2673 // locked previously created merge groups,
2674 // after determining the size for the new merge group.
2675 // 1. If necessary close old merge group(s) that overlap
2676 // the X-area of the new group
2677 for (;;)
2679 WW8SelBoxInfo* p = FindMergeGroup(
2680 nX1, pActMGroup->m_nGroupWidth, false );
2681 if (p == nullptr)
2683 break;
2685 p->m_bGroupLocked = true;
2688 // 3. push to group array
2689 m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
2692 // if necessary add the current box to a merge group
2693 // (that can be a newly created or another group)
2694 UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
2700 //There is a limbo area in word at the end of the row marker
2701 //where properties can live in word, there is no location in
2702 //writer equivalent, so try and park the cursor in the best
2703 //match, see #i23022#/#i18644#
2704 void WW8TabDesc::ParkPaM()
2706 SwTableBox *pTabBox2 = nullptr;
2707 short nRow = m_nCurrentRow + 1;
2708 if (nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2710 if (SwTableLine *pLine = (*m_pTabLines)[nRow])
2712 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
2713 pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
2717 if (!pTabBox2 || !pTabBox2->GetSttNd())
2719 MoveOutsideTable();
2720 return;
2723 SwNodeOffset nSttNd = pTabBox2->GetSttIdx() + 1,
2724 nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
2726 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2730 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2732 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2734 m_pIo->m_pPaM->GetPoint()->SetContent(0);
2735 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2739 void WW8TabDesc::MoveOutsideTable()
2741 OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored");
2742 if (m_xTmpPos && m_pIo)
2743 *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos->GetPoint();
2746 void WW8TabDesc::FinishSwTable()
2748 m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
2750 // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2751 // place, or somewhere close if that place got destroyed
2752 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_oLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_oLastAnchorPos) : nullptr);
2753 m_pIo->m_oLastAnchorPos.reset();
2755 SwTableNode* pTableNode = m_pTable->GetTableNode();
2756 SwDeleteListener aListener(*pTableNode);
2757 m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack);
2759 if (xLastAnchorCursor)
2760 m_pIo->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2762 WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get());
2763 m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
2765 MoveOutsideTable();
2766 m_xTmpPos.reset();
2768 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2770 m_pIo->m_bWasTabRowEnd = false;
2771 m_pIo->m_bWasTabCellEnd = false;
2773 m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM);
2775 if (aListener.WasDeleted())
2776 throw std::runtime_error("table unexpectedly destroyed by applying redlines");
2778 MergeCells();
2780 // if needed group cells together that should be merged
2781 if (m_MergeGroups.empty())
2782 return;
2784 // process all merge groups one by one
2785 for (auto const& groupIt : m_MergeGroups)
2787 if((1 < groupIt->size()) && groupIt->row(0)[0])
2789 SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
2790 pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->m_nGroupWidth, 0));
2791 const sal_uInt16 nRowSpan = groupIt->rowsCount();
2792 for (sal_uInt16 n = 0; n < nRowSpan; ++n)
2794 auto& rRow = groupIt->row(n);
2795 for (size_t i = 0; i<rRow.size(); ++i)
2797 const sal_Int32 nRowSpanSet = (n == 0) && (i == 0) ?
2798 nRowSpan :
2799 (-1 * (nRowSpan - n));
2800 SwTableBox* pCurrentBox = rRow[i];
2801 pCurrentBox->setRowSpan(nRowSpanSet);
2803 if (i == 0)
2804 pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
2805 else
2807 SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
2808 pFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 0, 0));
2814 m_pIo->m_xFormatOfJustInsertedApo.reset();
2815 m_MergeGroups.clear();
2818 // browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
2820 // Parameter: nXcenter = center position of asking box
2821 // nWidth = width of asking box
2822 // bExact = flag, if box has to fit into group
2823 // or only has to touch
2825 WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
2827 if (!m_MergeGroups.empty())
2829 // still valid area near the boundary
2830 const short nTolerance = 4;
2831 // box boundary
2832 short nX2 = nX1 + nWidth;
2833 // approximate group boundary
2834 short nGrX1;
2835 short nGrX2;
2837 // improvement: search backwards
2838 for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
2840 // the currently inspected group
2841 WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
2842 if (!rActGroup.m_bGroupLocked)
2844 // approximate group boundary with room (tolerance) to the *outside*
2845 nGrX1 = rActGroup.m_nGroupXStart - nTolerance;
2846 nGrX2 = rActGroup.m_nGroupXStart
2847 + rActGroup.m_nGroupWidth + nTolerance;
2849 // If box fits report success
2851 if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
2853 return &rActGroup;
2856 // does the box share areas with the group?
2858 if( !bExact )
2860 // successful if nX1 *or* nX2 are inside the group
2861 if( ( ( nX1 > nGrX1 )
2862 && ( nX1 < nGrX2 - 2*nTolerance ) )
2863 || ( ( nX2 > nGrX1 + 2*nTolerance )
2864 && ( nX2 < nGrX2 ) )
2865 // or nX1 and nX2 surround the group
2866 || ( ( nX1 <=nGrX1 )
2867 && ( nX2 >=nGrX2 ) ) )
2869 return &rActGroup;
2875 return nullptr;
2878 bool WW8TabDesc::IsValidCell(short nCol) const
2880 return (o3tl::make_unsigned(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) &&
2881 m_pActBand->bExist[nCol] &&
2882 o3tl::make_unsigned(m_nCurrentRow) < m_pTabLines->size();
2885 bool WW8TabDesc::InFirstParaInCell() const
2887 //e.g. #i19718#
2888 if (!m_pTabBox || !m_pTabBox->GetSttNd())
2890 OSL_FAIL("Problem with table");
2891 return false;
2894 if (!IsValidCell(GetCurrentCol()))
2895 return false;
2897 return m_pIo->m_pPaM->GetPoint()->GetNodeIndex() == m_pTabBox->GetSttIdx() + 1;
2900 void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
2902 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2903 if (!m_pActBand)
2904 return;
2906 sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
2908 if (o3tl::make_unsigned(m_nCurrentRow) >= m_pTabLines->size())
2910 OSL_ENSURE(false, "Actual row bigger than expected." );
2911 if (bPam)
2912 MoveOutsideTable();
2913 return;
2916 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
2917 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2919 if (nCol >= m_pTabBoxes->size())
2921 if (bPam)
2923 // The first paragraph in a cell with upper autospacing has upper
2924 // spacing set to 0
2925 if (
2926 m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara &&
2927 !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing
2930 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
2933 // The last paragraph in a cell with lower autospacing has lower
2934 // spacing set to 0
2935 if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2936 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
2938 ParkPaM();
2940 return;
2942 m_pTabBox = (*m_pTabBoxes)[nCol];
2943 if( !m_pTabBox->GetSttNd() )
2945 OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table");
2946 if (bPam)
2947 MoveOutsideTable();
2948 return;
2950 if (!bPam)
2951 return;
2953 m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ];
2955 // The first paragraph in a cell with upper autospacing has upper spacing set to 0
2956 if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2957 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
2959 // The last paragraph in a cell with lower autospacing has lower spacing set to 0
2960 if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2961 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
2963 //We need to set the pPaM on the first cell, invalid
2964 //or not so that we can collect paragraph properties over
2965 //all the cells, but in that case on the valid cell we do not
2966 //want to reset the fmt properties
2967 SwNodeOffset nSttNd = m_pTabBox->GetSttIdx() + 1,
2968 nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
2969 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2973 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2975 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2976 m_pIo->m_pPaM->GetPoint()->SetContent(0);
2977 // Precautionally set now, otherwise the style is not set for cells
2978 // that are inserted for margin balancing.
2979 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2980 // because this cells are invisible helper constructions only to simulate
2981 // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
2984 // Better to turn Snap to Grid off for all paragraphs in tables
2985 SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
2986 SwTextNode *pNd = pGridPos->GetNode().GetTextNode();
2987 if(!pNd)
2988 return;
2990 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
2991 const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
2993 if(!rSnapToGrid.GetValue())
2994 return;
2996 SvxParaGridItem aGridItem( rSnapToGrid );
2997 aGridItem.SetValue(false);
2999 const sal_Int32 nEnd = pGridPos->GetContentIndex();
3000 pGridPos->SetContent(0);
3001 m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem);
3002 pGridPos->SetContent(nEnd);
3003 m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
3006 void WW8TabDesc::InsertCells( short nIns )
3008 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
3009 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
3010 m_pTabBox = (*m_pTabBoxes)[0];
3012 m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, m_pTabBox->GetFrameFormat(),
3013 const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
3014 // The third parameter contains the FrameFormat of the boxes.
3015 // Here it is possible to optimize to save (reduce) FrameFormats.
3018 void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
3020 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3021 return; // faked cells -> no border
3023 SvxBoxItem aFormatBox( RES_BOX );
3024 if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
3026 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3027 if (SwWW8ImplReader::IsBorder(pT->rgbrc))
3028 SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
3031 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP))
3033 aFormatBox.SetDistance(
3034 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP],
3035 SvxBoxItemLine::TOP);
3037 else
3038 aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
3039 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM))
3041 aFormatBox.SetDistance(
3042 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM],
3043 SvxBoxItemLine::BOTTOM);
3045 else
3046 aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
3048 // nGapHalf for WW is a *horizontal* gap between table cell and content.
3049 short nLeftDist =
3050 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf;
3051 short nRightDist =
3052 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf;
3053 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT))
3055 aFormatBox.SetDistance(
3056 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT],
3057 SvxBoxItemLine::LEFT);
3059 else
3060 aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
3061 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT))
3063 aFormatBox.SetDistance(
3064 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT],
3065 SvxBoxItemLine::RIGHT);
3067 else
3068 aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
3070 pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
3073 void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
3075 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3076 return; // faked cells -> no color
3078 bool bFound=false;
3079 if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
3081 Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
3082 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND));
3083 bFound = true;
3086 //If there was no new shades, or no new shade setting
3087 if (m_pActBand->pSHDs && !bFound)
3089 WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
3090 if (!rSHD.GetValue()) // auto
3091 return;
3093 SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
3094 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.m_aColor, RES_BACKGROUND));
3098 static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
3100 SvxFrameDirection eDir = SvxFrameDirection::Environment;
3101 // 1: Asian layout with rotated CJK characters
3102 // 5: Asian layout
3103 // 3: Western layout rotated by 90 degrees
3104 // 4: Western layout
3105 switch (nCode)
3107 default:
3108 OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield");
3109 [[fallthrough]];
3110 case 3:
3111 eDir = SvxFrameDirection::Vertical_LR_BT;
3112 break;
3113 case 5:
3114 eDir = SvxFrameDirection::Vertical_RL_TB;
3115 break;
3116 case 1:
3117 eDir = SvxFrameDirection::Vertical_RL_TB;
3118 break;
3119 case 4:
3120 eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables
3121 break;
3123 return eDir;
3126 void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx)
3128 if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
3129 return;
3130 SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR);
3131 pBox->GetFrameFormat()->SetFormatAttr(aItem);
3134 void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
3136 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3137 return;
3139 sal_Int16 eVertOri=text::VertOrientation::TOP;
3141 if( m_pActBand->pTCs )
3143 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3144 switch (pT->nVertAlign)
3146 case 0:
3147 default:
3148 eVertOri = text::VertOrientation::TOP;
3149 break;
3150 case 1:
3151 eVertOri = text::VertOrientation::CENTER;
3152 break;
3153 case 2:
3154 eVertOri = text::VertOrientation::BOTTOM;
3155 break;
3159 pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
3162 void WW8TabDesc::AdjustNewBand()
3164 if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
3165 InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols );
3167 SetPamInCell( 0, false);
3168 OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == o3tl::narrowing<sal_uInt16>(m_pActBand->nSwCols),
3169 "Wrong column count in table" );
3171 if( m_bClaimLineFormat )
3173 m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
3174 SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default
3176 if (m_pActBand->nLineHeight == 0) // 0 = Auto
3177 aF.SetHeightSizeType( SwFrameSize::Variable );
3178 else
3180 if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
3182 aF.SetHeightSizeType(SwFrameSize::Fixed);
3183 m_pActBand->nLineHeight = -m_pActBand->nLineHeight;
3185 if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
3186 m_pActBand->nLineHeight = MINLAY;
3188 aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
3190 m_pTabLine->GetFrameFormat()->SetFormatAttr(aF);
3193 //Word stores 1 for bCantSplit if the row cannot be split, we set true if
3194 //we can split the row
3195 bool bSetCantSplit = m_pActBand->bCantSplit;
3196 m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit));
3198 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
3199 if (bSetCantSplit && m_pTabLines->size() == 1)
3200 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit(false));
3202 short i; // SW-Index
3203 short j; // WW-Index
3204 short nW; // Width
3205 SwFormatFrameSize aFS( SwFrameSize::Fixed );
3206 j = m_pActBand->bLEmptyCol ? -1 : 0;
3208 for( i = 0; i < m_pActBand->nSwCols; i++ )
3210 // set cell width
3211 if( j < 0 )
3212 nW = m_pActBand->nCenter[0] - m_nMinLeft;
3213 else
3215 //Set j to first non invalid cell
3216 while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
3217 j++;
3219 if( j < m_pActBand->nWwCols )
3220 nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3221 else
3222 nW = m_nMaxRight - m_pActBand->nCenter[j];
3223 m_pActBand->nWidth[ j ] = nW;
3226 SwTableBox* pBox = (*m_pTabBoxes)[i];
3227 // could be reduced further by intelligent moving of FrameFormats
3228 pBox->ClaimFrameFormat();
3230 SetTabBorders(pBox, j);
3232 SvxBoxItem aCurrentBox(pBox->GetFrameFormat()->GetFormatAttr(RES_BOX));
3233 pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
3235 SetTabVertAlign(pBox, j);
3236 SetTabDirection(pBox, j);
3237 if( m_pActBand->pSHDs || m_pActBand->pNewSHDs)
3238 SetTabShades(pBox, j);
3239 j++;
3241 aFS.SetWidth( nW );
3242 pBox->GetFrameFormat()->SetFormatAttr( aFS );
3244 // skip non existing cells
3245 while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
3247 m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3248 j++;
3253 void WW8TabDesc::TableCellEnd()
3255 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
3257 // new line/row
3258 if( m_pIo->m_bWasTabRowEnd )
3260 // bWasTabRowEnd will be deactivated in
3261 // SwWW8ImplReader::ProcessSpecial()
3263 sal_uInt16 iCol = GetLogicalWWCol();
3264 if (iCol < m_aNumRuleNames.size())
3266 m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
3267 m_aNumRuleNames.end());
3270 m_nCurrentCol = 0;
3271 m_nCurrentRow++;
3272 m_nCurrentBandRow++;
3273 OSL_ENSURE( m_pActBand , "pActBand is 0" );
3274 if( m_pActBand )
3276 if( m_nCurrentRow >= m_nRows ) // nothing to at end of table
3277 return;
3279 bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows;
3280 if( bNewBand )
3281 { // new band needed ?
3282 m_pActBand = m_pActBand->pNextBand;
3283 m_nCurrentBandRow = 0;
3284 OSL_ENSURE( m_pActBand, "pActBand is 0" );
3285 AdjustNewBand();
3287 else
3289 SwTableBox* pBox = (*m_pTabBoxes)[0];
3290 SwSelBoxes aBoxes;
3291 m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
3295 else
3296 { // new column ( cell )
3297 m_nCurrentCol++;
3299 SetPamInCell(m_nCurrentCol, true);
3301 // finish Annotated Level Numbering ?
3302 if (m_pIo->m_bAnl && !m_pIo->m_bCurrentAND_fNumberAcross && m_pActBand)
3303 m_pIo->StopAllAnl(IsValidCell(m_nCurrentCol));
3306 // if necessary register the box for the merge group for this column
3307 void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell const & rCell,
3308 WW8SelBoxInfo* pActGroup,
3309 SwTableBox* pActBox,
3310 sal_uInt16 nCol )
3312 // check if the box has to be merged
3313 // If cell is the first one to be merged, a new merge group has to be provided.
3314 // E.g., it could be that a cell is the first one to be merged, but no
3315 // new merge group is provided, because the potential other cell to be merged
3316 // doesn't exist - see method <WW8TabDesc::MergeCells>.
3317 if ( !(m_pActBand->bExist[ nCol ] &&
3318 ( ( rCell.bFirstMerged && pActGroup ) ||
3319 rCell.bMerged ||
3320 rCell.bVertMerge ||
3321 rCell.bVertRestart )) )
3322 return;
3324 // detect appropriate merge group
3325 WW8SelBoxInfo* pTheMergeGroup = nullptr;
3326 if( pActGroup )
3327 // assign group
3328 pTheMergeGroup = pActGroup;
3329 else
3331 // find group
3332 pTheMergeGroup = FindMergeGroup(
3333 m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
3335 if( pTheMergeGroup )
3337 // add current box to merge group
3338 pTheMergeGroup->push_back(pActBox);
3342 sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
3344 sal_uInt16 nCol = 0;
3345 if( m_pActBand && m_pActBand->pTCs)
3347 for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol )
3349 if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
3350 ++nCol;
3353 return nCol;
3356 // find name of numrule valid for current WW-COL
3357 const OUString & WW8TabDesc::GetNumRuleName() const
3359 sal_uInt16 nCol = GetLogicalWWCol();
3360 if (nCol < m_aNumRuleNames.size())
3361 return m_aNumRuleNames[nCol];
3362 return EMPTY_OUSTRING;
3365 void WW8TabDesc::SetNumRuleName( const OUString& rName )
3367 sal_uInt16 nCol = GetLogicalWWCol();
3368 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
3369 m_aNumRuleNames.emplace_back();
3370 m_aNumRuleNames[nCol] = rName;
3373 bool SwWW8ImplReader::StartTable(WW8_CP nStartCp)
3375 // Entering a table so make sure the FirstPara flag gets set
3376 m_bFirstPara = true;
3377 // no recursive table, not with InsertFile in table or foot note
3378 if (m_bReadNoTable)
3379 return false;
3381 if (m_xTableDesc)
3382 m_aTableStack.push(std::move(m_xTableDesc));
3384 // #i33818# - determine absolute position object attributes,
3385 // if possible. It's needed for nested tables.
3386 std::unique_ptr<WW8FlyPara> pTableWFlyPara;
3387 WW8SwFlyPara* pTableSFlyPara( nullptr );
3388 // #i45301# - anchor nested table inside Writer fly frame
3389 // only at-character, if absolute position object attributes are available.
3390 // Thus, default anchor type is as-character anchored.
3391 RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR );
3392 if ( m_nInTable )
3394 WW8_TablePos* pNestedTabPos( nullptr );
3395 WW8_TablePos aNestedTabPos;
3396 WW8PLCFxSave1 aSave;
3397 m_xPlcxMan->GetPap()->Save( aSave );
3398 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
3399 WW8_CP nMyStartCp = nStartCp;
3400 if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
3401 ParseTabPos( &aNestedTabPos, pPap ) )
3403 pNestedTabPos = &aNestedTabPos;
3405 m_xPlcxMan->GetPap()->Restore( aSave );
3406 if ( pNestedTabPos )
3408 ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
3409 pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
3410 if ( pTableWFlyPara )
3412 // <WW8SwFlyPara> constructor has changed - new 4th parameter
3413 // containing WW8 page top margin.
3414 pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
3415 m_aSectionManager.GetWWPageTopMargin(),
3416 m_aSectionManager.GetTextAreaWidth(),
3417 m_nIniFlyDx, m_nIniFlyDy);
3419 // #i45301# - anchor nested table Writer fly frame
3420 eAnchor = RndStdIds::FLY_AT_PARA;
3424 // if first paragraph in table has break-before-page, transfer that setting to the table itself.
3425 else if( StyleExists(m_nCurrentColl) )
3427 const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat;
3428 if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
3429 NewAttr( pStyleFormat->GetBreak() );
3432 m_xTableDesc.reset(new WW8TabDesc(this, nStartCp));
3434 if( m_xTableDesc->Ok() )
3436 int nNewInTable = m_nInTable + 1;
3438 if ((eAnchor == RndStdIds::FLY_AT_PARA)
3439 && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
3441 m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
3442 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aItemSet(m_rDoc.GetAttrPool());
3443 // #i33818# - anchor the Writer fly frame for the nested table at-character.
3444 // #i45301#
3445 SwFormatAnchor aAnchor( eAnchor );
3446 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3447 aItemSet.Put( aAnchor );
3448 m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
3449 m_xTableDesc->m_pParentPos, &aItemSet);
3450 OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
3451 "Not the anchor type requested!" );
3452 MoveInsideFly(m_xTableDesc->m_pFlyFormat);
3454 m_xTableDesc->CreateSwTable();
3455 if (m_xTableDesc->m_pFlyFormat)
3457 m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat);
3458 // #i33818# - Use absolute position object attributes,
3459 // if existing, and apply them to the created Writer fly frame.
3460 if ( pTableWFlyPara && pTableSFlyPara )
3462 WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false );
3463 // At-para, so it can split in the multi-page case.
3464 SwFormatAnchor aAnchor(RndStdIds::FLY_AT_PARA);
3465 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3466 aFlySet.Put( aAnchor );
3467 // Map a positioned inner table to a split fly.
3468 aFlySet.Put(SwFormatFlySplit(true));
3469 m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
3471 else
3473 SwFormatHoriOrient aHori =
3474 m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
3475 m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
3476 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) );
3478 // #i33818# - The nested table doesn't have to leave
3479 // the table cell. Thus, the Writer fly frame has to follow the text flow.
3480 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
3482 else
3483 m_xTableDesc->SetSizePosition(nullptr);
3484 m_xTableDesc->UseSwTable();
3486 else
3487 PopTableDesc();
3489 // #i33818#
3490 delete pTableSFlyPara;
3492 return m_xTableDesc != nullptr;
3495 void SwWW8ImplReader::TabCellEnd()
3497 FinalizeTextNode(*m_pPaM->GetPoint(), false);
3499 if (m_nInTable && m_xTableDesc)
3500 m_xTableDesc->TableCellEnd();
3502 m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
3503 m_bReadTable = false;
3506 void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
3508 if( ( nLen > 0 ) && ( *pData == 1 ) )
3509 m_bWasTabCellEnd = true;
3512 void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
3514 if( ( nLen > 0 ) && ( *pData == 1 ) )
3515 m_bWasTabRowEnd = true;
3518 void SwWW8ImplReader::PopTableDesc()
3520 if (m_xTableDesc && m_xTableDesc->m_pFlyFormat)
3522 MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos);
3525 m_xTableDesc.reset();
3526 if (!m_aTableStack.empty())
3528 m_xTableDesc = std::move(m_aTableStack.top());
3529 m_aTableStack.pop();
3533 void SwWW8ImplReader::StopTable()
3535 OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!");
3536 if (!m_xTableDesc)
3537 return;
3539 // We are leaving a table so make sure the next paragraph doesn't think
3540 // it's the first paragraph
3541 m_bFirstPara = false;
3543 m_xTableDesc->FinishSwTable();
3544 PopTableDesc();
3546 m_bReadTable = true;
3549 bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const
3551 if( !m_xTableDesc )
3552 return false;
3554 const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell();
3556 return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() )
3557 || ( pCell
3558 && ( !pCell->bFirstMerged
3559 && ( pCell->bMerged
3560 || ( pCell->bVertMerge
3561 && !pCell->bVertRestart
3568 sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
3570 sal_uInt16 nRes = USHRT_MAX;
3571 if( !m_vColl.empty() )
3573 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3574 if( m_vColl[ nI ].m_bValid
3575 && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
3576 nRes = nI;
3578 return nRes;
3581 const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( std::u16string_view rName ) const
3583 SwFormat* pRet = nullptr;
3584 if( !m_vColl.empty() )
3586 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3587 if( m_vColl[ nI ].m_bValid
3588 && (rName == m_vColl[ nI ].GetOrgWWName()) )
3590 pRet = m_vColl[ nI ].m_pFormat;
3591 break;
3594 return pRet;
3598 SprmResult WW8RStyle::HasParaSprm(sal_uInt16 nId) const
3600 if( !mpParaSprms || !mnSprmsLen )
3601 return SprmResult();
3603 return maSprmParser.findSprmData(nId, mpParaSprms, mnSprmsLen);
3606 void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
3608 if (!nLen)
3609 return;
3611 if( bPap )
3613 mpParaSprms = pSprms; // for HasParaSprms()
3614 mnSprmsLen = nLen;
3617 WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
3618 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
3620 mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
3621 aSprmIter.advance();
3624 mpParaSprms = nullptr;
3625 mnSprmsLen = 0;
3628 void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
3630 if (!nLen)
3631 return;
3633 if (checkSeek(*mpStStrm, nPosFc))
3635 std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
3636 nLen = mpStStrm->ReadBytes(pSprms.get(), nLen);
3637 ImportSprms(pSprms.get(), nLen, bPap);
3641 static short WW8SkipOdd(SvStream* pSt )
3643 if ( pSt->Tell() & 0x1 )
3645 sal_uInt8 c;
3646 return pSt->ReadBytes( &c, 1 );
3648 return 0;
3651 static short WW8SkipEven(SvStream* pSt )
3653 if (!(pSt->Tell() & 0x1))
3655 sal_uInt8 c;
3656 return pSt->ReadBytes( &c, 1 );
3658 return 0;
3661 short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
3663 if( 0 < nLen ) // Empty ?
3665 if (bOdd)
3666 nLen = nLen - WW8SkipEven( mpStStrm );
3667 else
3668 nLen = nLen - WW8SkipOdd( mpStStrm );
3670 sal_Int16 cbUPX(0);
3671 mpStStrm->ReadInt16( cbUPX );
3673 nLen-=2;
3675 if ( cbUPX > nLen )
3676 cbUPX = nLen; // shrink cbUPX to nLen
3678 if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
3680 if( bPAP )
3682 sal_uInt16 id;
3683 mpStStrm->ReadUInt16( id );
3685 cbUPX-= 2;
3686 nLen-= 2;
3689 if( 0 < cbUPX )
3691 sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong,
3692 // this should make it work again
3693 ImportSprms( nPos, cbUPX, bPAP );
3695 if ( mpStStrm->Tell() != nPos + cbUPX )
3696 mpStStrm->Seek( nPos+cbUPX );
3698 nLen = nLen - cbUPX;
3702 return nLen;
3705 void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
3707 if( nLen <= 0 )
3708 return;
3709 if (bOdd)
3710 nLen = nLen - WW8SkipEven( mpStStrm );
3711 else
3712 nLen = nLen - WW8SkipOdd( mpStStrm );
3714 if( bPara ) // Grupx.Papx
3715 nLen = ImportUPX(nLen, true, bOdd);
3716 ImportUPX(nLen, false, bOdd); // Grupx.Chpx
3719 WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI)
3720 : WW8Style(*pI->m_pTableStream, _rFib)
3721 , maSprmParser(_rFib)
3722 , mpIo(pI)
3723 , mpStStrm(pI->m_pTableStream)
3724 , mpStyRule(nullptr)
3725 , mpParaSprms(nullptr)
3726 , mnSprmsLen(0)
3727 , mnWwNumLevel(0)
3728 , mbTextColChanged(false)
3729 , mbFontChanged(false)
3730 , mbCJKFontChanged(false)
3731 , mbCTLFontChanged(false)
3732 , mbFSizeChanged(false)
3733 , mbFCTLSizeChanged(false)
3734 , mbWidowsChanged(false)
3735 , mbBidiChanged(false)
3737 mpIo->m_vColl.resize(m_cstd);
3740 void WW8RStyle::Set1StyleDefaults()
3742 // see #i25247#, #i25561#, #i48064#, #i92341# for default font
3743 if (!mbCJKFontChanged) // Style no CJK Font? set the default
3744 mpIo->SetNewFontAttr(m_ftcFE, true, RES_CHRATR_CJK_FONT);
3746 if (!mbCTLFontChanged) // Style no CTL Font? set the default
3747 mpIo->SetNewFontAttr(m_ftcBi, true, RES_CHRATR_CTL_FONT);
3749 // western 2nd to make western charset conversion the default
3750 if (!mbFontChanged) // Style has no Font? set the default,
3751 mpIo->SetNewFontAttr(m_ftcAsci, true, RES_CHRATR_FONT);
3753 if( mpIo->m_bNoAttrImport )
3754 return;
3756 // Style has no text color set, winword default is auto
3757 if ( !mbTextColChanged )
3758 mpIo->m_pCurrentColl->SetFormatAttr(SvxColorItem(COL_AUTO, RES_CHRATR_COLOR));
3760 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3761 if( !mbFSizeChanged )
3763 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3764 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3765 aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
3766 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3769 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3770 if( !mbFCTLSizeChanged )
3772 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3773 aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
3774 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3777 if( !mbWidowsChanged ) // Widows ?
3779 mpIo->m_pCurrentColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) );
3780 mpIo->m_pCurrentColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) );
3783 // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of
3784 // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr
3785 if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style
3787 mpIo->m_pCurrentColl->SetFormatAttr(
3788 SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
3792 bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle,
3793 sal_uInt16 nNextStyle,
3794 std::map<OUString, sal_Int32>& rParaCollisions,
3795 std::map<OUString, sal_Int32>& rCharCollisions)
3797 SwFormat* pColl;
3798 bool bStyExist;
3800 if (rSI.m_bColl)
3802 // Para-Style
3803 sw::util::ParaStyleMapper::StyleResult aResult =
3804 mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rParaCollisions);
3805 pColl = aResult.first;
3806 bStyExist = aResult.second;
3808 else
3810 // Char-Style
3811 sw::util::CharStyleMapper::StyleResult aResult =
3812 mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rCharCollisions);
3813 pColl = aResult.first;
3814 bStyExist = aResult.second;
3817 bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ?
3819 // Do not override character styles the list import code created earlier.
3820 if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
3821 bImport = false;
3823 bool bOldNoImp = mpIo->m_bNoAttrImport;
3824 rSI.m_bImportSkipped = !bImport;
3826 if( !bImport )
3827 mpIo->m_bNoAttrImport = true;
3828 else
3830 if (bStyExist)
3832 pColl->ResetAllFormatAttr(); // #i73790# - method renamed
3834 pColl->SetAuto(false); // suggested by JP
3835 } // but changes the UI
3836 mpIo->m_pCurrentColl = pColl;
3837 rSI.m_pFormat = pColl; // remember translation WW->SW
3838 rSI.m_bImportSkipped = !bImport;
3840 // Set Based on style
3841 sal_uInt16 j = rSI.m_nBase;
3842 if (j != nThisStyle && j < m_cstd )
3844 SwWW8StyInf* pj = &mpIo->m_vColl[j];
3845 if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
3847 rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
3848 rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet;
3849 rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet;
3850 rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet;
3851 rSI.m_n81Flags = pj->m_n81Flags;
3852 rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags;
3853 if (!rSI.IsWW8BuiltInHeadingStyle())
3855 rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel;
3857 rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore;
3858 rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter;
3860 if (pj->m_xWWFly)
3861 rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get());
3864 else if( mpIo->m_bNewDoc && bStyExist )
3865 rSI.m_pFormat->SetDerivedFrom();
3867 rSI.m_nFollow = nNextStyle; // remember Follow
3869 mpStyRule = nullptr; // recreate if necessary
3870 mbTextColChanged = mbFontChanged = mbCJKFontChanged = mbCTLFontChanged =
3871 mbFSizeChanged = mbFCTLSizeChanged = mbWidowsChanged = false;
3872 mpIo->SetNCurrentColl( nThisStyle );
3873 mpIo->m_bStyNormal = nThisStyle == 0;
3874 return bOldNoImp;
3877 void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp)
3879 // Reset attribute flags, because there are no style-ends.
3881 mpIo->m_bHasBorder = mpIo->m_bSpec = mpIo->m_bObj = mpIo->m_bSymbol = false;
3882 mpIo->m_nCharFormat = -1;
3884 // if style is based on nothing or base ignored
3885 if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
3887 // If Char-Styles does not work
3888 // -> set hard WW-Defaults
3889 Set1StyleDefaults();
3892 mpStyRule = nullptr; // to be on the safe side
3893 mpIo->m_bStyNormal = false;
3894 mpIo->SetNCurrentColl( 0 );
3895 mpIo->m_bNoAttrImport = bOldNoImp;
3896 // reset the list-remember-fields, if used when reading styles
3897 mpIo->m_nLFOPosition = USHRT_MAX;
3898 mpIo->m_nListLevel = MAXLEVEL;
3901 void WW8RStyle::Import1Style(sal_uInt16 nNr,
3902 std::map<OUString, sal_Int32>& rParaCollisions,
3903 std::map<OUString, sal_Int32>& rCharCollisions)
3905 if (nNr >= mpIo->m_vColl.size())
3906 return;
3908 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3910 if( rSI.m_bImported || !rSI.m_bValid )
3911 return;
3913 rSI.m_bImported = true; // set flag now to avoid endless loops
3915 // valid and not NUL and not yet imported
3917 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3918 Import1Style(rSI.m_nBase, rParaCollisions, rCharCollisions);
3920 mpStStrm->Seek( rSI.m_nFilePos );
3922 sal_uInt16 nSkip;
3923 OUString sName;
3925 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName)); // read Style
3927 if (xStd)
3928 rSI.SetOrgWWIdent( sName, xStd->sti );
3930 // either no Name or unused Slot or unknown Style
3932 if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
3934 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
3935 mpStStrm->Seek(mpStStrm->Tell() + nSkip);
3936 return;
3939 bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti),
3940 nNr, xStd->istdNext,
3941 rParaCollisions, rCharCollisions);
3943 // if something is interpreted wrong, this should make it work again
3944 sal_uInt64 nPos = mpStStrm->Tell();
3946 //Variable parts of the STD start at even byte offsets, but "inside
3947 //the STD", which I take to meaning even in relation to the starting
3948 //position of the STD, which matches findings in #89439#, generally it
3949 //doesn't matter as the STSHI starts off nearly always on an even
3950 //offset
3952 //Import of the Style Contents
3953 ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
3955 PostStyle(rSI, bOldNoImp);
3957 mpStStrm->Seek( nPos+nSkip );
3960 void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
3962 if (nNr >= mpIo->m_vColl.size())
3963 return;
3965 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3966 if( rSI.m_bImported || !rSI.m_bValid )
3967 return;
3969 rSI.m_bImported = true;
3971 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3972 RecursiveReg(rSI.m_nBase);
3974 mpIo->RegisterNumFormatOnStyle(nNr);
3979 After all styles are imported then we can recursively apply numbering
3980 styles to them, and change their tab stop settings if they turned out
3981 to have special first line indentation.
3983 void WW8RStyle::PostProcessStyles()
3985 sal_uInt16 i;
3987 Clear all imported flags so that we can recursively apply numbering
3988 formats and use it to mark handled ones
3990 for (i=0; i < m_cstd; ++i)
3991 mpIo->m_vColl[i].m_bImported = false;
3994 Register the num formats and tabstop changes on the styles recursively.
3998 In the same loop apply the tabstop changes required because we need to
3999 change their location if there's a special indentation for the first line,
4000 By avoiding making use of each styles margins during reading of their
4001 tabstops we don't get problems with doubly adjusting tabstops that
4002 are inherited.
4004 for (i=0; i < m_cstd; ++i)
4006 if (mpIo->m_vColl[i].m_bValid)
4008 RecursiveReg(i);
4013 void WW8RStyle::ScanStyles() // investigate style dependencies
4014 { // and detect Filepos for each Style
4015 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4017 SwWW8StyInf &rSI = mpIo->m_vColl[i];
4019 rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos
4020 sal_uInt16 nSkip;
4021 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD
4022 rSI.m_bValid = xStd != nullptr;
4023 if (rSI.m_bValid)
4025 rSI.m_nBase = xStd->istdBase; // remember Basis
4026 rSI.m_bColl = xStd->sgc == 1; // Para-Style
4028 else
4029 rSI = SwWW8StyInf();
4031 xStd.reset();
4032 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
4033 mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms
4037 std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
4039 std::vector<sal_uInt8> aRet
4042 static_cast< sal_uInt8 >(128 + rChpx.fBold),
4045 static_cast< sal_uInt8 >(128 + rChpx.fItalic),
4048 static_cast< sal_uInt8 >(128 + rChpx.fStrike),
4051 static_cast< sal_uInt8 >(128 + rChpx.fOutline),
4054 static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps),
4057 static_cast< sal_uInt8 >(128 + rChpx.fCaps),
4060 static_cast< sal_uInt8 >(128 + rChpx.fVanish)
4062 if (rChpx.fsFtc)
4064 aRet.push_back(68);
4065 SVBT16 a;
4066 ShortToSVBT16(rChpx.ftc, a);
4067 aRet.push_back(a[1]);
4068 aRet.push_back(a[0]);
4071 if (rChpx.fsKul)
4073 aRet.push_back(69);
4074 aRet.push_back(rChpx.kul);
4077 if (rChpx.fsLid)
4079 aRet.push_back(72);
4080 SVBT16 a;
4081 ShortToSVBT16(rChpx.lid, a);
4082 aRet.push_back(a[1]);
4083 aRet.push_back(a[0]);
4086 if (rChpx.fsIco)
4088 aRet.push_back(73);
4089 aRet.push_back(rChpx.ico);
4092 if (rChpx.fsHps)
4094 aRet.push_back(74);
4096 SVBT16 a;
4097 ShortToSVBT16(rChpx.hps, a);
4098 aRet.push_back(a[0]);
4101 if (rChpx.fsPos)
4103 aRet.push_back(76);
4104 aRet.push_back(rChpx.hpsPos);
4107 aRet.push_back(80);
4108 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
4110 aRet.push_back(81);
4111 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
4113 if (rChpx.fsFtcBi)
4115 aRet.push_back(82);
4116 SVBT16 a;
4117 ShortToSVBT16(rChpx.fsFtcBi, a);
4118 aRet.push_back(a[1]);
4119 aRet.push_back(a[0]);
4122 if (rChpx.fsLidBi)
4124 aRet.push_back(83);
4125 SVBT16 a;
4126 ShortToSVBT16(rChpx.lidBi, a);
4127 aRet.push_back(a[1]);
4128 aRet.push_back(a[0]);
4131 if (rChpx.fsIcoBi)
4133 aRet.push_back(84);
4134 aRet.push_back(rChpx.icoBi);
4137 if (rChpx.fsHpsBi)
4139 aRet.push_back(85);
4140 SVBT16 a;
4141 ShortToSVBT16(rChpx.hpsBi, a);
4142 aRet.push_back(a[1]);
4143 aRet.push_back(a[0]);
4146 return aRet;
4149 Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
4151 Word2CHPX aChpx;
4153 if (!nSize || !checkSeek(rSt, nOffset))
4154 return aChpx;
4156 const size_t nMaxByteCount = rSt.remainingSize();
4157 if (!nMaxByteCount)
4158 return aChpx;
4160 if (nSize > nMaxByteCount)
4162 SAL_WARN("sw.ww8", "ReadWord2Chpx: truncating out of range "
4163 << nSize << " to " << nMaxByteCount);
4164 nSize = nMaxByteCount;
4167 sal_uInt8 nCount=0;
4169 while (true)
4171 sal_uInt8 nFlags8;
4172 rSt.ReadUChar( nFlags8 );
4173 nCount++;
4175 if (!rSt.good())
4176 break;
4178 aChpx.fBold = nFlags8 & 0x01;
4179 aChpx.fItalic = (nFlags8 & 0x02) >> 1;
4180 aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
4181 aChpx.fOutline = (nFlags8 & 0x08) >> 3;
4182 aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
4183 aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
4184 aChpx.fCaps = (nFlags8 & 0x40) >> 6;
4185 aChpx.fVanish = (nFlags8 & 0x80) >> 7;
4187 if (nCount >= nSize) break;
4188 rSt.ReadUChar( nFlags8 );
4189 nCount++;
4191 if (!rSt.good())
4192 break;
4194 aChpx.fRMark = nFlags8 & 0x01;
4195 aChpx.fSpec = (nFlags8 & 0x02) >> 1;
4196 aChpx.fStrike = (nFlags8 & 0x04) >> 2;
4197 aChpx.fObj = (nFlags8 & 0x08) >> 3;
4198 aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
4199 aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
4200 aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
4201 aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
4203 if (nCount >= nSize) break;
4204 rSt.ReadUChar( nFlags8 );
4205 nCount++;
4207 if (!rSt.good())
4208 break;
4210 aChpx.fsIco = nFlags8 & 0x01;
4211 aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
4212 aChpx.fsHps = (nFlags8 & 0x04) >> 2;
4213 aChpx.fsKul = (nFlags8 & 0x08) >> 3;
4214 aChpx.fsPos = (nFlags8 & 0x10) >> 4;
4215 aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
4216 aChpx.fsLid = (nFlags8 & 0x40) >> 6;
4217 aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
4219 if (nCount >= nSize) break;
4220 rSt.ReadUChar( nFlags8 );
4221 nCount++;
4223 if (!rSt.good())
4224 break;
4226 aChpx.fsFtcBi = nFlags8 & 0x01;
4227 aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
4228 aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
4230 if (nCount >= nSize) break;
4231 rSt.ReadUInt16( aChpx.ftc );
4232 nCount+=2;
4234 if (nCount >= nSize) break;
4235 rSt.ReadUInt16( aChpx.hps );
4236 nCount+=2;
4238 if (nCount >= nSize) break;
4239 rSt.ReadUChar( nFlags8 );
4240 nCount++;
4242 if (!rSt.good())
4243 break;
4245 aChpx.qpsSpace = nFlags8 & 0x3F;
4246 aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
4247 aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
4249 if (nCount >= nSize) break;
4250 rSt.ReadUChar( nFlags8 );
4251 nCount++;
4253 if (!rSt.good())
4254 break;
4256 aChpx.ico = nFlags8 & 0x1F;
4257 aChpx.kul = (nFlags8 & 0xE0) >> 5;
4259 if (nCount >= nSize) break;
4260 rSt.ReadUChar( aChpx.hpsPos );
4261 nCount++;
4263 if (nCount >= nSize) break;
4264 rSt.ReadUChar( aChpx.icoBi );
4265 nCount++;
4267 if (nCount >= nSize) break;
4268 rSt.ReadUInt16( aChpx.lid );
4269 nCount+=2;
4271 if (nCount >= nSize) break;
4272 rSt.ReadUInt16( aChpx.ftcBi );
4273 nCount+=2;
4275 if (nCount >= nSize) break;
4276 rSt.ReadUInt16( aChpx.hpsBi );
4277 nCount+=2;
4279 if (nCount >= nSize) break;
4280 rSt.ReadUInt16( aChpx.lidBi );
4281 nCount+=2;
4283 if (nCount >= nSize) break;
4284 rSt.ReadUInt32( aChpx.fcPic );
4285 nCount+=4;
4287 break;
4290 rSt.SeekRel(nSize-nCount);
4291 return aChpx;
4294 namespace
4296 struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
4299 void WW8RStyle::ImportOldFormatStyles()
4301 for (sal_uInt16 i=0; i < m_cstd; ++i)
4303 mpIo->m_vColl[i].m_bColl = true;
4304 //every chain must end eventually at the null style (style code 222)
4305 mpIo->m_vColl[i].m_nBase = 222;
4308 rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
4309 mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid);
4311 sal_uInt16 cstcStd(0);
4312 m_rStream.ReadUInt16( cstcStd );
4314 size_t nMaxByteCount = m_rStream.remainingSize();
4315 sal_uInt16 cbName(0);
4316 m_rStream.ReadUInt16(cbName);
4317 if (cbName > nMaxByteCount)
4319 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4320 << cbName << " to " << nMaxByteCount);
4321 cbName = nMaxByteCount;
4323 sal_uInt16 nByteCount = 2;
4324 sal_uInt16 stcp=0;
4325 while (nByteCount < cbName)
4327 sal_uInt8 nCount(0);
4328 m_rStream.ReadUChar( nCount );
4329 nByteCount++;
4331 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4332 if (stc >=mpIo->m_vColl.size())
4333 continue;
4335 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4336 OUString sName;
4338 if (nCount != 0xFF) // undefined style
4340 if (nCount != 0) // user style
4342 OString aTmp = read_uInt8s_ToOString(m_rStream, nCount);
4343 nByteCount += aTmp.getLength();
4344 sName = OStringToOUString(aTmp, eStructChrSet);
4346 rSI.m_bImported = true;
4349 if (sName.isEmpty())
4351 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4352 if (const char *pStr = GetEnglishNameFromSti(eSti))
4353 sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
4356 if (sName.isEmpty())
4357 sName = "Unknown Style: " + OUString::number(stc);
4359 rSI.SetOrgWWIdent(sName, stc);
4360 stcp++;
4363 sal_uInt16 nStyles=stcp;
4365 std::vector<pxoffset> aCHPXOffsets(stcp);
4366 nMaxByteCount = m_rStream.remainingSize();
4367 sal_uInt16 cbChpx(0);
4368 m_rStream.ReadUInt16(cbChpx);
4369 if (cbChpx > nMaxByteCount)
4371 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4372 << cbChpx << " to " << nMaxByteCount);
4373 cbChpx = nMaxByteCount;
4375 nByteCount = 2;
4376 stcp=0;
4377 std::vector< std::vector<sal_uInt8> > aConvertedChpx;
4378 while (nByteCount < cbChpx)
4380 if (stcp == aCHPXOffsets.size())
4382 //more data than style slots, skip remainder
4383 m_rStream.SeekRel(cbChpx-nByteCount);
4384 break;
4387 sal_uInt8 cb(0);
4388 m_rStream.ReadUChar( cb );
4389 nByteCount++;
4391 aCHPXOffsets[stcp].mnSize = 0;
4393 if (cb != 0xFF)
4395 sal_uInt8 nRemainder = cb;
4397 aCHPXOffsets[stcp].mnOffset = m_rStream.Tell();
4398 aCHPXOffsets[stcp].mnSize = nRemainder;
4400 Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset,
4401 aCHPXOffsets[stcp].mnSize);
4402 aConvertedChpx.push_back( ChpxToSprms(aChpx) );
4404 nByteCount += nRemainder;
4406 else
4407 aConvertedChpx.emplace_back( );
4409 ++stcp;
4412 std::vector<pxoffset> aPAPXOffsets(stcp);
4413 nMaxByteCount = m_rStream.remainingSize();
4414 sal_uInt16 cbPapx(0);
4415 m_rStream.ReadUInt16(cbPapx);
4416 if (cbPapx > nMaxByteCount)
4418 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4419 << cbPapx << " to " << nMaxByteCount);
4420 cbPapx = nMaxByteCount;
4422 nByteCount = 2;
4423 stcp=0;
4424 while (nByteCount < cbPapx)
4426 if (stcp == aPAPXOffsets.size())
4428 m_rStream.SeekRel(cbPapx-nByteCount);
4429 break;
4432 sal_uInt8 cb(0);
4433 m_rStream.ReadUChar( cb );
4434 nByteCount++;
4436 aPAPXOffsets[stcp].mnSize = 0;
4438 if (cb != 0xFF)
4440 sal_uInt8 nRemainder;
4441 if (cb < 7)
4443 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: expected byte count: "
4444 << static_cast<int>(cb) << " to be >= 7");
4445 m_rStream.SeekRel(cb);
4446 nByteCount += cb;
4447 nRemainder = 0;
4449 else
4451 sal_uInt8 stc2(0);
4452 m_rStream.ReadUChar(stc2);
4453 m_rStream.SeekRel(6);
4454 nByteCount += 7;
4455 nRemainder = cb-7;
4458 aPAPXOffsets[stcp].mnOffset = m_rStream.Tell();
4459 aPAPXOffsets[stcp].mnSize = nRemainder;
4461 m_rStream.SeekRel(nRemainder);
4462 nByteCount += nRemainder;
4465 ++stcp;
4468 sal_uInt16 iMac(0);
4469 m_rStream.ReadUInt16( iMac );
4471 if (iMac > nStyles) iMac = nStyles;
4473 std::map<OUString, sal_Int32> aParaCollisions;
4474 std::map<OUString, sal_Int32> aCharCollisions;
4476 for (stcp = 0; stcp < iMac; ++stcp)
4478 sal_uInt8 stcNext(0), stcBase(0);
4479 m_rStream.ReadUChar( stcNext );
4480 m_rStream.ReadUChar( stcBase );
4482 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4485 #i64557# style based on itself
4486 every chain must end eventually at the null style (style code 222)
4488 if (stc == stcBase)
4489 stcBase = 222;
4491 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4492 rSI.m_nBase = stcBase;
4494 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4496 if (eSti == ww::stiNil)
4497 continue;
4499 if (stcp >= aPAPXOffsets.size())
4500 continue;
4502 rSI.m_bValid = true;
4504 if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
4505 mpIo->m_vColl[stc].m_bColl = false;
4507 bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext,
4508 aParaCollisions,
4509 aCharCollisions);
4511 ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
4512 true);
4514 if (!aConvertedChpx[stcp].empty())
4515 ImportSprms(aConvertedChpx[stcp].data(),
4516 static_cast< short >(aConvertedChpx[stcp].size()),
4517 false);
4519 PostStyle(rSI, bOldNoImp);
4523 void WW8RStyle::ImportNewFormatStyles()
4525 ScanStyles(); // Scan Based On
4527 std::map<OUString, sal_Int32> aParaCollisions;
4528 std::map<OUString, sal_Int32> aCharCollisions;
4530 for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles
4531 if (mpIo->m_vColl[i].m_bValid)
4532 Import1Style(i, aParaCollisions, aCharCollisions);
4535 void WW8RStyle::Import()
4537 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4538 mpIo->m_pStandardFormatColl =
4539 mpIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
4541 if( mpIo->m_nIniFlags & WW8FL_NO_STYLES )
4542 return;
4544 if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2)
4545 ImportOldFormatStyles();
4546 else
4547 ImportNewFormatStyles();
4549 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4551 // Follow chain
4552 SwWW8StyInf* pi = &mpIo->m_vColl[i];
4553 sal_uInt16 j = pi->m_nFollow;
4554 if( j < m_cstd )
4556 SwWW8StyInf* pj = &mpIo->m_vColl[j];
4557 if ( j != i // rational Index ?
4558 && pi->m_pFormat // Format ok ?
4559 && pj->m_pFormat // Derived-Format ok ?
4560 && pi->m_bColl // only possible for paragraph templates (WW)
4561 && pj->m_bColl ){ // identical Type ?
4562 static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
4563 *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
4568 // Missing special handling for default character template
4569 // "Absatz-Standardschriftart" ( Style-ID 65 ).
4570 // That is empty by default ( WW6 dt and US ) and not changeable
4571 // via WW-UI so this does not matter.
4572 // This could be done by:
4573 // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
4575 // for e.g. tables an always valid Std-Style is necessary
4577 if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() &&
4578 mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid )
4579 mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat);
4580 else
4581 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4583 // set Hyphenation flag on BASIC para-style
4584 if (mpIo->m_bNewDoc && mpIo->m_pStandardFormatColl)
4586 if (mpIo->m_xWDop->fAutoHyphen
4587 && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState(
4588 RES_PARATR_HYPHENZONE, false) )
4590 SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE);
4591 aAttr.GetMinLead() = 2;
4592 aAttr.GetMinTrail() = 2;
4593 aAttr.GetMaxHyphens() = 0;
4595 mpIo->m_pStandardFormatColl->SetFormatAttr( aAttr );
4599 // we do not read styles anymore:
4600 mpIo->m_pCurrentColl = nullptr;
4603 rtl_TextEncoding SwWW8StyInf::GetCharSet() const
4605 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4606 return m_eRTLFontSrcCharSet;
4607 return m_eLTRFontSrcCharSet;
4610 rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
4612 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4613 return m_eRTLFontSrcCharSet;
4614 return m_eCJKFontSrcCharSet;
4617 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */