android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / ww8par2.cxx
blob26b6ee1f8de68c4780d51259f8209ab8c896b3f8
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 <numrule.hxx>
62 #include "sprmids.hxx"
63 #include <wwstyles.hxx>
64 #include "ww8struc.hxx"
65 #include "ww8par.hxx"
66 #include "ww8par2.hxx"
68 #include <frmatr.hxx>
69 #include <itabenum.hxx>
70 #include <unocrsr.hxx>
72 #include <iostream>
73 #include <memory>
75 using namespace ::com::sun::star;
77 WW8TabBandDesc::WW8TabBandDesc():
78 pNextBand(nullptr), nGapHalf(0), mnDefaultLeft(0), mnDefaultTop(0), mnDefaultRight(0),
79 mnDefaultBottom(0), mbHasSpacing(false), nLineHeight(0), nRows(0), nCenter{}, nWidth{},
80 nWwCols(0), nSwCols(0), bLEmptyCol(false), bREmptyCol(false), bCantSplit(false),
81 bCantSplit90(false), pTCs(nullptr), nOverrideSpacing{}, nOverrideValues{}, pSHDs(nullptr),
82 pNewSHDs(nullptr), bExist{}, nTransCell{}
84 for (sal_uInt16 & rn : maDirections)
85 rn = 4;
88 WW8TabBandDesc::~WW8TabBandDesc()
90 delete[] pTCs;
91 delete[] pSHDs;
92 delete[] pNewSHDs;
95 void sw::util::RedlineStack::close( const SwPosition& rPos,
96 RedlineType eType, WW8TabDesc* pTabDesc )
98 // If the redline type is not found in the redline stack, we have to check if there has been
99 // a tabledesc and to check its saved redline stack, too. (#136939, #i68139)
100 if( !close( rPos, eType ) )
102 if( pTabDesc && pTabDesc->getOldRedlineStack() )
104 bool const bResult =
105 pTabDesc->getOldRedlineStack()->close(rPos, eType);
106 OSL_ENSURE( bResult, "close without open!");
111 void wwSectionManager::SetCurrentSectionHasFootnote()
113 OSL_ENSURE(!maSegments.empty(),
114 "should not be possible, must be at least one segment");
115 if (!maSegments.empty())
116 maSegments.back().mbHasFootnote = true;
119 void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA)
121 OSL_ENSURE(!maSegments.empty(),
122 "should not be possible, must be at least one segment");
123 if ( !maSegments.empty() )
124 maSegments.back().mnVerticalAdjustment = nVA;
127 bool wwSectionManager::CurrentSectionIsVertical() const
129 OSL_ENSURE(!maSegments.empty(),
130 "should not be possible, must be at least one segment");
131 if (!maSegments.empty())
132 return maSegments.back().IsVertical();
133 return false;
136 bool wwSectionManager::CurrentSectionIsProtected() const
138 OSL_ENSURE(!maSegments.empty(),
139 "should not be possible, must be at least one segment");
140 if (!maSegments.empty())
141 return SectionIsProtected(maSegments.back());
142 return false;
145 sal_uInt32 wwSectionManager::GetPageLeft() const
147 return !maSegments.empty() ? maSegments.back().m_nPgLeft : 0;
150 sal_uInt32 wwSectionManager::GetPageRight() const
152 return !maSegments.empty() ? maSegments.back().m_nPgRight : 0;
155 sal_uInt32 wwSectionManager::GetPageWidth() const
157 return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0;
160 sal_uInt32 wwSectionManager::GetTextAreaWidth() const
162 return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0;
165 sal_uInt32 wwSectionManager::GetWWPageTopMargin() const
167 return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0;
170 sal_uInt16 SwWW8ImplReader::End_Footnote()
173 Ignoring Footnote outside of the normal Text. People will put footnotes
174 into field results and field commands.
176 if (m_bIgnoreText ||
177 m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras())
179 return 0;
182 OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
183 if (m_aFootnoteStack.empty())
184 return 0;
186 bool bFtEdOk = false;
187 const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
189 //Get the footnote character and remove it from the txtnode. We'll
190 //replace it with the footnote
191 SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
192 sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
194 OUString sChar;
195 SwTextFootnote* pFN = nullptr;
196 //There should have been a footnote char, we will replace this.
197 if (pText && nPos)
199 sChar += OUStringChar(pText->GetText()[--nPos]);
200 m_pPaM->SetMark();
201 m_pPaM->GetMark()->AdjustContent(-1);
202 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_oLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_oLastAnchorPos) : nullptr);
203 m_oLastAnchorPos.reset();
204 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
205 m_pPaM->DeleteMark();
206 if (xLastAnchorCursor)
207 m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
208 SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
209 pFN = static_cast<SwTextFootnote*>(pText->InsertItem(aFootnote, nPos, nPos));
211 OSL_ENSURE(pFN, "Problems creating the footnote text");
212 if (pFN)
214 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
215 WW8PLCFxSaveAll aSave;
216 m_xPlcxMan->SaveAllPLCFx( aSave );
217 std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan;
219 const SwNodeIndex* pSttIdx = pFN->GetStartNode();
220 assert(pSttIdx && "Problems creating footnote text");
222 pFN->SetSeqNo(m_rDoc.GetFootnoteIdxs().size());
224 bool bOld = m_bFootnoteEdn;
225 m_bFootnoteEdn = true;
227 SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr());
229 SvtDeleteListener aDeleteListener(rFormatFootnote.GetNotifier());
231 // read content of Ft-/End-Note
232 Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
234 m_bFootnoteEdn = bOld;
236 SAL_WARN_IF(aDeleteListener.WasDeleted(), "sw.ww8", "Footnode deleted during its import");
237 if (!aDeleteListener.WasDeleted())
239 bFtEdOk = true;
241 OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
242 "footnote autonumbering must be 0x02, and everything else must not be");
244 // If no automatic numbering use the following char from the main text
245 // as the footnote number
246 if (!rDesc.mbAutoNum)
247 pFN->SetNumber(0, 0, sChar);
250 Delete the footnote char from the footnote if it's at the beginning
251 as usual. Might not be if the user has already deleted it, e.g.
252 #i14737#
254 SwPosition& rPaMPointPos = *m_pPaM->GetPoint();
255 rPaMPointPos.Assign(pSttIdx->GetIndex() + 1);
256 SwTextNode* pTNd = rPaMPointPos.GetNode().GetTextNode();
257 if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
259 const OUString &rText = pTNd->GetText();
260 if (rText[0] == sChar[0])
262 // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
263 sal_Int32 nFirstLineIndent=0;
264 SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aSet(m_rDoc.GetAttrPool());
265 if ( pTNd->GetAttr(aSet) )
267 const SvxFirstLineIndentItem *const pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
268 if (pFirstLine)
270 nFirstLineIndent = pFirstLine->GetTextFirstLineOffset();
274 rPaMPointPos.SetContent(0);
275 m_pPaM->SetMark();
276 // Strip out aesthetic tabs we may have inserted on export #i24762#
277 if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09)
278 m_pPaM->GetMark()->AdjustContent(1);
279 m_pPaM->GetMark()->AdjustContent(1);
280 m_xReffingStck->Delete(*m_pPaM);
281 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
282 m_pPaM->DeleteMark();
287 *m_pPaM->GetPoint() = aTmpPos; // restore Cursor
289 m_xPlcxMan = xOldPlcxMan; // Restore attributes
290 m_xPlcxMan->RestoreAllPLCFx( aSave );
293 if (bFtEdOk)
294 m_aSectionManager.SetCurrentSectionHasFootnote();
296 m_aFootnoteStack.pop_back();
297 return 0;
300 tools::Long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes)
303 Ignoring Footnote outside of the normal Text. People will put footnotes
304 into field results and field commands.
306 if (m_bIgnoreText ||
307 m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras())
309 return 0;
312 FootnoteDescriptor aDesc;
313 aDesc.mbAutoNum = true;
314 if (eEDN == pRes->nSprmId)
316 aDesc.meType = MAN_EDN;
317 WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn();
318 if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
319 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
321 else
323 aDesc.meType = MAN_FTN;
324 WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote();
325 if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
326 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
329 aDesc.mnStartCp = pRes->nCp2OrIdx;
330 aDesc.mnLen = pRes->nMemLen;
332 m_aFootnoteStack.push_back(aDesc);
334 return 0;
337 bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp,
338 int nLevel) const
340 WW8PLCFxDesc aRes;
341 aRes.pMemPos = nullptr;
342 aRes.nEndPos = rStartCp;
343 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
345 while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
347 if (pPap->Where() != WW8_CP_MAX)
349 SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel));
350 const sal_uInt8* pB = aSprmRes.pSprm;
351 if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1)
353 aSprmRes = pPap->HasSprm(0x6649);
354 const sal_uInt8 *pLevel = aSprmRes.pSprm;
355 if (pLevel && aSprmRes.nRemainingData >= 1)
357 if (nLevel + 1 == *pLevel)
358 return true;
360 else
362 OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
363 return true; // RowEnd found
368 aRes.nStartPos = aRes.nEndPos;
369 aRes.pMemPos = nullptr;
370 //Seek to our next block of properties
371 if (!(pPap->SeekPos(aRes.nStartPos)))
373 aRes.nEndPos = WW8_CP_MAX;
374 pPap->SetDirty(true);
376 pPap->GetSprms(&aRes);
377 pPap->SetDirty(false);
378 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
379 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
381 SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
382 break;
384 //Update our aRes to get the new starting point of the next properties
385 rStartCp = aRes.nEndPos;
388 return false;
391 bool SwWW8ImplReader::SearchTableEnd(WW8PLCFx_Cp_FKP* pPap) const
393 if (m_bVer67)
394 // The below SPRM is for WW8 only.
395 return false;
397 WW8PLCFxDesc aRes;
398 aRes.pMemPos = nullptr;
399 aRes.nEndPos = pPap->Where();
400 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
402 while (pPap->HasFkp() && pPap->Where() != WW8_CP_MAX)
404 // See if the current pap is outside the table.
405 SprmResult aSprmRes = pPap->HasSprm(NS_sprm::PFInTable::val);
406 const sal_uInt8* pB = aSprmRes.pSprm;
407 if (!pB || aSprmRes.nRemainingData < 1 || *pB != 1)
408 // Yes, this is the position after the end of the table.
409 return true;
411 // It is, so seek to the next pap.
412 aRes.nStartPos = aRes.nEndPos;
413 aRes.pMemPos = nullptr;
414 if (!pPap->SeekPos(aRes.nStartPos))
415 return false;
417 // Read the sprms and make sure we moved forward to avoid infinite loops.
418 pPap->GetSprms(&aRes);
419 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
420 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
422 SAL_WARN("sw.ww8", "SearchTableEnd, loop in paragraph property chain");
423 break;
427 return false;
430 ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
431 const WW8_TablePos *pTabPos)
433 const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
434 ApoTestResults aRet;
435 // Frame in Style Definition (word appears to ignore them if inside an
436 // text autoshape)
437 sal_uInt16 const nStyle(m_xPlcxMan->GetColl());
438 if (!m_bTxbxFlySection && nStyle < m_vColl.size())
439 aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
442 #i1140#
443 If I have a table and apply a style to one of its frames that should cause
444 a paragraph that it is applied to it to only exist as a separate floating
445 frame, then the behaviour depends on which cell that it has been applied
446 to. If it is the first cell of a row then the whole table row jumps into the
447 new frame, if it isn't then the paragraph attributes are applied
448 "except" for the floating frame stuff. i.e. it's ignored. So if there's a
449 table, and we're not in the first cell then we ignore the fact that the
450 paragraph style wants to be in a different frame.
452 This sort of mindbending inconsistency is surely why frames are deprecated
453 in word 97 onwards and hidden away from the user
455 #i1532# & #i5379#
456 If we are already a table in a frame then we must grab the para properties
457 to see if we are still in that frame.
460 aRet.m_bHasSprm37 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr; // sprmPWr
461 SprmResult aSrpm29 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B); // sprmPPc
462 const sal_uInt8 *pSrpm29 = aSrpm29.pSprm;
463 aRet.m_bHasSprm29 = pSrpm29 != nullptr;
464 const sal_Int16 nTPc = aRet.mpStyleApo ? aRet.mpStyleApo->nTPc : 0;
465 aRet.m_nSprm29 = (pSrpm29 && aSrpm29.nRemainingData >= 1) ? *pSrpm29 : nTPc;
467 // Is there some frame data here
468 bool bNowApo = aRet.HasFrame() || pTopLevelTable;
469 if (bNowApo)
471 if (!ConstructApo(aRet, pTabPos))
472 bNowApo = false;
475 bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
476 if (bTestAllowed)
478 //Test is allowed if there is no table.
479 //Otherwise only allowed if we are in the
480 //first paragraph of the first cell of a row.
481 //(And only if the row we are inside is at the
482 //same level as the previous row, think tables
483 //in tables)
484 if (nCellLevel == m_nInTable)
487 if (!m_nInTable)
488 bTestAllowed = true;
489 else
491 if (!m_xTableDesc)
493 OSL_ENSURE(m_xTableDesc, "What!");
494 bTestAllowed = false;
496 else
498 // #i39468#
499 // If current cell isn't valid, the test is allowed.
500 // The cell isn't valid, if e.g. there is a new row
501 // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()>
502 bTestAllowed =
503 m_xTableDesc->GetCurrentCol() == 0 &&
504 ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) ||
505 m_xTableDesc->InFirstParaInCell() );
511 if (!bTestAllowed)
512 return aRet;
514 aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
515 aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
517 //If it happens that we are in a table, then if it's not the first cell
518 //then any attributes that might otherwise cause the contents to jump
519 //into another frame don't matter, a table row sticks together as one
520 //unit no matter what else happens. So if we are not in a table at
521 //all, or if we are in the first cell then test that the last frame
522 //data is the same as the current one
523 if (bNowApo && InEqualApo(nCellLevel))
525 // two bordering each other
526 if (!TestSameApo(aRet, pTabPos))
527 aRet.mbStopApo = aRet.mbStartApo = true;
530 return aRet;
533 // helper methods for outline, numbering and bullets
535 static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
537 static const SvxNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
538 SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC,
539 SVX_NUM_ARABIC, SVX_NUM_ARABIC };
541 static const SvxAdjust eAdjA[4] = { SvxAdjust::Left,
542 SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left };
543 if (rAV.nfc < 8) {
544 rNum.SetNumberingType( eNumA[ rAV.nfc ] );
545 } else {
546 SvxNumType nType = SVX_NUM_ARABIC;
547 switch( rAV.nfc ) {
548 case 14:
549 case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
550 case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
551 case 31:nType = SVX_NUM_DI_ZI_ZH; break;
552 case 35:
553 case 36:
554 case 37:
555 case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
556 case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
557 case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
558 case 10:
559 case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
560 case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
561 case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
562 case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
563 case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
564 case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
565 case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
566 case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
567 //case 42:
568 //case 43:
569 case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
570 default:
571 nType= SVX_NUM_ARABIC;break;
573 rNum.SetNumberingType( nType );
576 if ((rAV.aBits1 & 0x4) >> 2)
578 rNum.SetIncludeUpperLevels(nSwLevel + 1);
580 rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) );
581 rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
583 rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) );
584 sal_Int16 nIndent = std::abs(static_cast<sal_Int16>(SVBT16ToUInt16( rAV.dxaIndent )));
585 if( rAV.aBits1 & 0x08 ) //fHang
587 rNum.SetFirstLineOffset( -nIndent );
588 rNum.SetAbsLSpace( nIndent );
590 else
591 rNum.SetCharTextDistance( nIndent ); // width of number is missing
593 if( rAV.nfc == 5 || rAV.nfc == 7 )
595 OUString sP = "." + rNum.GetSuffix();
596 rNum.SetListFormat("", sP, nSwLevel); // ordinal number
598 else
599 rNum.SetListFormat("", "", nSwLevel);
602 void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV,
603 const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
605 if (nStart > nElements)
606 return;
608 pText += nStart;
609 nElements -= nStart;
611 bool bInsert = false; // Default
612 rtl_TextEncoding eCharSet = m_eStructCharSet;
614 const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo
615 bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/...
617 sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
618 OUStringBuffer sText(static_cast<sal_Int32>(nLen));
619 if (m_bVer67)
621 if (nLen > nElements)
623 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
624 << nLen << " vs " << nElements << " max");
625 return;
627 sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
628 // ofz#23961 in case of multi-byte input encoding resulting in shorter
629 // output pad to full length with something semi-arbitrary
630 comphelper::string::padToLength(sText, nLen, cBulletChar);
632 else
634 if (nLen > nElements / 2)
636 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
637 << nLen << " vs " << nElements / 2 << " max");
638 return;
640 for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
642 sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText))));
646 if( bOutline )
647 { // outline
648 if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
649 || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
651 // if self defined digits
652 bInsert = true; // then apply character
654 // replace by simple Bullet ?
655 if( bListSymbol )
657 // use cBulletChar for correct mapping on MAC
658 sText.setLength(0);
659 comphelper::string::padToLength(sText, rAV.cbTextBefore
660 + rAV.cbTextAfter, cBulletChar);
664 else
665 { // numbering / bullets
666 bInsert = true;
667 if( bListSymbol )
669 FontFamily eFamily;
670 OUString aName;
671 FontPitch ePitch;
673 if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName,
674 ePitch, eCharSet ) ){
676 vcl::Font aFont;
677 aFont.SetFamilyName( aName );
678 aFont.SetFamily( eFamily );
680 aFont.SetCharSet( eCharSet );
681 rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
683 rNum.SetBulletFont( &aFont );
685 // take only the very first character
686 if (rAV.cbTextBefore || rAV.cbTextAfter)
688 rNum.SetBulletChar(
689 OUString::unacquired(sText).iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
691 else
692 rNum.SetBulletChar( 0x2190 );
696 if( !bInsert )
697 return;
699 OUString sPrefix;
700 OUString sSuffix;
701 if (rAV.cbTextBefore)
703 sPrefix = sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear();
705 if( rAV.cbTextAfter )
707 sSuffix = rNum.GetSuffix() + sText.subView( rAV.cbTextBefore, rAV.cbTextAfter);
710 rNum.SetListFormat(sPrefix, sSuffix, nLevel);
712 // The characters before and after multiple digits do not apply because
713 // those are handled differently by the writer and the result is in most
714 // cases worse than without.
717 // SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
718 // which are provided by pNumR. This is used for everything beside
719 // outline inside the text.
720 void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
721 bool bOutLine)
723 SwNumFormat aNF;
724 aNF.SetListFormat("", "", nSwLevel);
725 if (pAD)
726 { // there is an Anld-Sprm
727 m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross;
728 WW8_ANLV const &rAV = pAD->eAnlv;
729 SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
730 SetAnlvStrings(aNF, nSwLevel, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
732 pNumR->Set(nSwLevel, aNF);
735 // chapter numbering and bullets
737 // Chapter numbering happens in the style definition.
738 // Sprm 13 provides the level, Sprm 12 the content.
740 SwNumRule* SwWW8ImplReader::GetStyRule()
742 if( m_xStyles->mpStyRule ) // Bullet-Style already present
743 return m_xStyles->mpStyRule;
745 const OUString aBaseName("WW8StyleNum");
746 const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
748 // #i86652#
749 sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false,
750 SvxNumberFormat::LABEL_ALIGNMENT );
751 m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul];
752 // Auto == false-> numbering style
753 m_xStyles->mpStyRule->SetAutoRule(false);
755 return m_xStyles->mpStyRule;
758 // Sprm 13
759 void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
761 m_nSwNumLevel = 0xff; // Default: invalid
763 if( nLen <= 0 )
764 return;
766 // StyleDef ?
767 if( m_pCurrentColl )
769 // only for SwTextFormatColl, not CharFormat
770 // WW: 0 = no Numbering
771 SwWW8StyInf * pColl = GetStyle(m_nCurrentColl);
772 if (pColl != nullptr && pColl->m_bColl && *pData)
774 // Range WW:1..9 -> SW:0..8 no bullets / numbering
776 if (*pData <= 9)
778 m_nSwNumLevel = *pData - 1;
779 if (!m_bNoAttrImport)
780 static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
781 // For WW-NoNumbering also NO_NUMBERING could be used.
782 // ( For normal numberierung NO_NUM has to be used:
783 // NO_NUM : pauses numbering,
784 // NO_NUMBERING : no numbering at all )
787 else if( *pData == 10 || *pData == 11 )
789 // remember type, the rest happens at Sprm 12
790 m_xStyles->mnWwNumLevel = *pData;
794 else
796 //Not StyleDef
797 if (!m_bAnl)
798 StartAnl(pData); // begin of outline / bullets
799 NextAnlLine(pData);
803 void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
805 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
806 if( !m_pCurrentColl || nLen <= 0 // only for Styledef
807 || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
808 || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
810 m_nSwNumLevel = 0xff;
811 return;
814 if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD))
816 SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
817 m_nSwNumLevel = 0xff;
818 return;
821 if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8
824 // If NumRuleItems were set, either directly or through inheritance, disable them now
825 m_pCurrentColl->SetFormatAttr( SwNumRuleItem() );
827 const OUString aName("Outline");
828 SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ),
829 SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
830 OUTLINE_RULE );
831 aNR = *m_rDoc.GetOutlineNumRule();
833 SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
835 // Missing Levels need not be replenished
836 m_rDoc.SetOutlineNumRule( aNR );
838 else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){
839 SwNumRule* pNR = GetStyRule();
840 SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
841 m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
843 pStyInf = GetStyle(m_nCurrentColl);
844 if (pStyInf != nullptr)
845 pStyInf->m_bHasStyNumRule = true;
849 // Numbering / Bullets
851 // SetNumOlst() carries the Numrules for this cell to SwNumFormat.
852 // For this the info is fetched from OLST and not from ANLD ( see later )
853 // ( only for outline inside text; Bullets / numbering use ANLDs )
854 void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel)
856 SwNumFormat aNF;
857 WW8_ANLV &rAV = pO->rganlv[nSwLevel];
858 SetBaseAnlv(aNF, rAV, nSwLevel);
859 // ... and then the Strings
860 int nTextOfs = 0;
861 sal_uInt8 i;
862 WW8_ANLV* pAV1; // search String-Positions
863 for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
864 nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
866 if (!m_bVer67)
867 nTextOfs *= 2;
868 SetAnlvStrings(aNF, nSwLevel, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
869 pNumR->Set(nSwLevel, aNF);
872 // The OLST is at the beginning of each section that contains outlines.
873 // The ANLDs that are connected to each outline-line contain only nonsense,
874 // so the OLSTs are remembered for the section to have usable information
875 // when outline-paragraphs occur.
876 void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
878 m_xNumOlst.reset();
879 if (nLen <= 0)
880 return;
882 if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST))
884 SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
885 return;
888 m_xNumOlst.reset(new WW8_OLST);
889 *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
892 WW8LvlType GetNumType(sal_uInt8 nWwLevelNo)
894 WW8LvlType nRet = WW8_None;
895 if( nWwLevelNo == 12 )
896 nRet = WW8_Pause;
897 else if( nWwLevelNo == 10 )
898 nRet = WW8_Numbering;
899 else if( nWwLevelNo == 11 )
900 nRet = WW8_Sequence;
901 else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
902 nRet = WW8_Outline;
903 return nRet;
906 SwNumRule *ANLDRuleMap::GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType)
908 const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
909 if (rNumRule.isEmpty())
910 return nullptr;
911 return rDoc.FindNumRulePtr(rNumRule);
914 void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
916 if (WW8_Numbering == nNumType)
917 msNumberingNumRule = rNumRule;
918 else
919 msOutlineNumRule = rNumRule;
922 // StartAnl is called at the beginning of a row area that contains
923 // outline / numbering / bullets
924 void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13)
926 m_bCurrentAND_fNumberAcross = false;
928 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
929 if (nT == WW8_Pause || nT == WW8_None)
930 return;
932 m_nWwNumType = nT;
933 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
935 // check for COL numbering:
936 SprmResult aS12; // sprmAnld
937 OUString sNumRule;
939 if (m_xTableDesc)
941 sNumRule = m_xTableDesc->GetNumRuleName();
942 if (!sNumRule.isEmpty())
944 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
945 if (!pNumRule)
946 sNumRule.clear();
947 else
949 // this is ROW numbering ?
950 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
951 if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
952 sNumRule.clear();
957 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
958 if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
960 sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
961 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
962 if (!pNumRule)
963 sNumRule.clear();
966 if (sNumRule.isEmpty())
968 if (!pNumRule)
970 // #i86652#
971 pNumRule = m_rDoc.GetNumRuleTable()[
972 m_rDoc.MakeNumRule( sNumRule, nullptr, false,
973 SvxNumberFormat::LABEL_ALIGNMENT ) ];
975 if (m_xTableDesc)
977 if (!aS12.pSprm)
978 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
979 if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
980 m_xTableDesc->SetNumRuleName(pNumRule->GetName());
984 m_bAnl = true;
986 sNumRule = pNumRule ? pNumRule->GetName() : OUString();
987 // set NumRules via stack
988 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(),
989 SfxStringItem(RES_FLTR_NUMRULE, sNumRule));
991 m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
994 // NextAnlLine() is called once for every row of a
995 // outline / numbering / bullet
996 void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13)
998 if (!m_bAnl)
999 return;
1001 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
1003 // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3
1005 // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0
1006 if (*pSprm13 == 10 || *pSprm13 == 11)
1008 m_nSwNumLevel = 0;
1009 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1011 // not defined yet
1012 // sprmAnld o. 0
1013 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1014 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1015 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1018 else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
1020 m_nSwNumLevel = *pSprm13 - 1; // outline
1021 // undefined
1022 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1024 if (m_xNumOlst) // there was a OLST
1026 //Assure upper levels are set, #i9556#
1027 for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
1029 if (!pNumRule->GetNumFormat(nI))
1030 SetNumOlst(pNumRule, m_xNumOlst.get(), nI);
1033 SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel);
1035 else // no Olst -> use Anld
1037 // sprmAnld
1038 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1039 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1040 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1044 else
1045 m_nSwNumLevel = 0xff; // no number
1047 SwTextNode* pNd = m_pPaM->GetPointNode().GetTextNode();
1048 if (!pNd)
1049 return;
1051 if (m_nSwNumLevel < MAXLEVEL)
1052 pNd->SetAttrListLevel( m_nSwNumLevel );
1053 else
1055 pNd->SetAttrListLevel(0);
1056 pNd->SetCountedInList( false );
1060 void SwWW8ImplReader::StopAllAnl(bool bGoBack)
1062 //Of course we're not restarting, but we'll make use of our knowledge
1063 //of the implementation to do it.
1064 StopAnlToRestart(WW8_None, bGoBack);
1067 void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack)
1069 if (bGoBack)
1071 SwPosition aTmpPos(*m_pPaM->GetPoint());
1072 m_pPaM->Move(fnMoveBackward, GoInContent);
1073 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1074 *m_pPaM->GetPoint() = aTmpPos;
1076 else
1077 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1079 m_aANLDRules.msNumberingNumRule.clear();
1081 #i18816#
1082 my take on this problem is that moving either way from an outline to a
1083 numbering doesn't halt the outline, while the numbering is always halted
1085 bool bNumberingNotStopOutline =
1086 (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
1087 ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
1088 if (!bNumberingNotStopOutline)
1089 m_aANLDRules.msOutlineNumRule.clear();
1091 m_nSwNumLevel = 0xff;
1092 m_nWwNumType = WW8_None;
1093 m_bAnl = false;
1096 WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc const & rBand )
1098 *this = rBand;
1099 if( rBand.pTCs )
1101 pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]);
1102 // create uninitialized
1103 memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
1105 if( rBand.pSHDs )
1107 pSHDs = new WW8_SHD[nWwCols];
1108 memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
1110 if( rBand.pNewSHDs )
1112 pNewSHDs = new Color[nWwCols];
1113 memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color));
1115 memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
1118 // ReadDef reads the cell position and the borders of a band
1119 void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen)
1121 --nLen; //reduce len by expected nCols arg
1122 if (nLen < 0)
1123 return;
1124 sal_uInt8 nCols = *pS; // number of cells
1126 if (nCols > MAX_COL)
1127 return;
1129 nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments
1130 if (nLen < 0)
1131 return;
1133 short nOldCols = nWwCols;
1134 nWwCols = nCols;
1136 const sal_uInt8* pT = &pS[1];
1137 for (int i = 0; i <= nCols; i++, pT+=2)
1138 nCenter[i] = static_cast<sal_Int16>(SVBT16ToUInt16( pT )); // X-borders
1140 if( nCols != nOldCols ) // different column count
1142 delete[] pTCs;
1143 pTCs = nullptr;
1144 delete[] pSHDs;
1145 pSHDs = nullptr;
1146 delete[] pNewSHDs;
1147 pNewSHDs = nullptr;
1150 short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
1152 if (!pTCs && nCols)
1154 // create empty TCs
1155 pTCs = new WW8_TCell[nCols];
1158 short nColsToRead = std::min<short>(nFileCols, nCols);
1160 if (nColsToRead <= 0)
1161 return;
1163 // read TCs
1166 Attention: Beginning with Ver8 there is an extra ushort per TC
1167 added and the size of the border code is doubled.
1168 Because of this a simple copy (pTCs[i] = *pTc;)
1169 is not possible.
1171 Advantage: The work structure suits better.
1173 WW8_TCell* pCurrentTC = pTCs;
1174 if( bVer67 )
1176 WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
1177 for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc)
1179 // TC from file ?
1180 sal_uInt8 aBits1 = pTc->aBits1Ver6;
1181 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
1182 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
1183 pCurrentTC->rgbrc[ WW8_TOP ]
1184 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
1185 pCurrentTC->rgbrc[ WW8_LEFT ]
1186 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
1187 pCurrentTC->rgbrc[ WW8_BOT ]
1188 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
1189 pCurrentTC->rgbrc[ WW8_RIGHT ]
1190 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1191 if( ( pCurrentTC->bMerged )
1192 && ( i > 0 ) )
1194 // Cell merged -> remember
1195 //bWWMergedVer6[i] = true;
1196 pTCs[i-1].rgbrc[ WW8_RIGHT ]
1197 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1198 // apply right border to previous cell
1199 // bExist must not be set to false, because WW
1200 // does not count this cells in text boxes...
1204 else
1206 WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
1207 for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc )
1209 sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 );
1210 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
1211 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
1212 pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
1213 pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
1214 pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
1215 pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
1216 pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
1217 pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
1218 // note: in aBits1 there are 7 bits unused,
1219 // followed by another 16 unused bits
1221 pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
1222 pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
1223 pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
1224 pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
1228 // #i25071 In '97 text direction appears to be only set using TC properties
1229 // not with sprmTTextFlow so we need to cycle through the maDirections and
1230 // double check any non-default directions
1231 for (int k = 0; k < nCols; ++k)
1233 if(maDirections[k] == 4)
1235 if(pTCs[k].bVertical)
1237 if(pTCs[k].bBackward)
1238 maDirections[k] = 3;
1239 else
1240 maDirections[k] = 1;
1246 void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen)
1248 if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s)
1249 return;
1251 if (nParamsLen < 3)
1253 SAL_WARN("sw.ww8", "table border property is too short");
1254 return;
1257 sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
1258 sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
1259 sal_uInt8 nFlag = *(pParamsTSetBRC+2);
1261 if (nitcFirst >= nWwCols)
1262 return;
1264 if (nitcLim > nWwCols)
1265 nitcLim = nWwCols;
1267 bool bChangeRight = (nFlag & 0x08) != 0;
1268 bool bChangeBottom = (nFlag & 0x04) != 0;
1269 bool bChangeLeft = (nFlag & 0x02) != 0;
1270 bool bChangeTop = (nFlag & 0x01) != 0;
1272 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1273 WW8_BRCVer9 brcVer9;
1274 if( nBrcVer == 6 )
1276 if (nParamsLen < sizeof(WW8_BRCVer6) + 3)
1278 SAL_WARN("sw.ww8", "table border property is too short");
1279 return;
1281 brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
1283 else if( nBrcVer == 8 )
1285 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1286 if (nParamsLen < sizeof(WW8_BRC) + 3)
1288 SAL_WARN("sw.ww8", "table border property is too short");
1289 return;
1291 brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
1293 else
1295 if (nParamsLen < sizeof(WW8_BRCVer9) + 3)
1297 SAL_WARN("sw.ww8", "table border property is too short");
1298 return;
1300 brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
1303 for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC )
1305 if( bChangeTop )
1306 pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9;
1307 if( bChangeLeft )
1308 pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9;
1309 if( bChangeBottom )
1310 pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9;
1311 if( bChangeRight )
1312 pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9;
1316 void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen)
1318 // sprmTTableBorders
1319 if( nBrcVer == 6 )
1321 if (nParamsLen < sizeof(WW8_BRCVer6) * 6)
1323 SAL_WARN("sw.ww8", "table border property is too short");
1324 return;
1326 WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
1327 for (int i = 0; i < 6; ++i)
1328 aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
1330 else if ( nBrcVer == 8 )
1332 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1333 if (nParamsLen < sizeof(WW8_BRC) * 6)
1335 SAL_WARN("sw.ww8", "table border property is too short");
1336 return;
1338 for( int i = 0; i < 6; ++i )
1339 aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
1341 else
1343 if (nParamsLen < sizeof( aDefBrcs ))
1345 SAL_WARN("sw.ww8", "table border property is too short");
1346 return;
1348 memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
1352 void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol)
1354 // sprmTDxaCol (opcode 0x7623) changes the width of cells
1355 // whose index is within a certain range to be a certain value.
1357 if( !(nWwCols && pParamsTDxaCol) ) // set one or more cell length(s)
1358 return;
1360 sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
1361 sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
1362 short nDxaCol = static_cast<sal_Int16>(SVBT16ToUInt16( pParamsTDxaCol + 2 ));
1364 for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
1366 const short nOrgWidth = nCenter[i+1] - nCenter[i];
1367 const short nDelta = nDxaCol - nOrgWidth;
1368 for( int j = i+1; j <= nWwCols; j++ )
1370 nCenter[j] = nCenter[j] + nDelta;
1375 void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert)
1377 if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s)
1378 return;
1380 sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
1381 if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
1382 return;
1383 sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
1384 sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 );
1386 short nNewWwCols;
1387 if (nitcInsert > nWwCols)
1389 nNewWwCols = nitcInsert+nctc;
1390 //if new count would be outside max possible count, clip it, and calc a new replacement
1391 //legal nctc
1392 if (nNewWwCols > MAX_COL)
1394 nNewWwCols = MAX_COL;
1395 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
1398 else
1400 nNewWwCols = nWwCols+nctc;
1401 //if new count would be outside max possible count, clip it, and calc a new replacement
1402 //legal nctc
1403 if (nNewWwCols > MAX_COL)
1405 nNewWwCols = MAX_COL;
1406 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
1410 WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
1412 if (pTCs)
1414 memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
1415 delete[] pTCs;
1417 pTCs = pTC2s;
1419 //If we have to move some cells
1420 if (nitcInsert <= nWwCols)
1422 // adjust the left x-position of the dummy at the very end
1423 nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
1424 for( int i = nWwCols-1; i >= nitcInsert; i--)
1426 // adjust the left x-position
1427 nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
1429 // adjust the cell's borders
1430 pTCs[i + nctc] = pTCs[i];
1434 //if itcMac is larger than full size, fill in missing ones first
1435 for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
1436 nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
1438 //now add in our new cells
1439 for( int j = 0;j < nctc; j++)
1440 nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
1442 nWwCols = nNewWwCols;
1446 void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams)
1448 sal_uInt8 nStartCell = *pParams++;
1449 sal_uInt8 nEndCell = *pParams++;
1450 sal_uInt16 nCode = SVBT16ToUInt16(pParams);
1452 OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
1453 OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
1454 if (nStartCell > MAX_COL)
1455 return;
1456 if (nEndCell > MAX_COL + 1)
1457 nEndCell = MAX_COL + 1;
1459 for (;nStartCell < nEndCell; ++nStartCell)
1460 maDirections[nStartCell] = nCode;
1463 void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams)
1465 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1466 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1467 if (nLen != 6)
1468 return;
1469 mbHasSpacing=true;
1470 #if OSL_DEBUG_LEVEL > 0
1471 sal_uInt8 nWhichCell = *pParams;
1472 OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
1473 #endif
1474 ++pParams; //Skip which cell
1475 ++pParams; //unknown byte
1477 sal_uInt8 nSideBits = *pParams++;
1478 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1479 ++pParams; //unknown byte
1480 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1481 for (int i = wwTOP; i <= wwRIGHT; i++)
1483 switch (nSideBits & (1 << i))
1485 case 1 << wwTOP:
1486 mnDefaultTop = nValue;
1487 break;
1488 case 1 << wwLEFT:
1489 mnDefaultLeft = nValue;
1490 break;
1491 case 1 << wwBOTTOM:
1492 mnDefaultBottom = nValue;
1493 break;
1494 case 1 << wwRIGHT:
1495 mnDefaultRight = nValue;
1496 break;
1497 case 0:
1498 break;
1499 default:
1500 OSL_ENSURE(false, "Impossible");
1501 break;
1506 void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams)
1508 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1509 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1510 if (nLen != 6)
1511 return;
1513 const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to.
1514 const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins.
1515 OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings");
1516 if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 )
1517 return;
1519 sal_uInt8 nSideBits = *pParams++;
1520 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1522 const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin
1523 OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width");
1524 if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored
1525 return;
1527 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1529 for (int nCell = nStartCell; nCell < nEndCell; ++nCell)
1531 nOverrideSpacing[ nCell ] |= nSideBits;
1532 OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits");
1534 for (int i=0; i < 4; i++)
1536 if (nSideBits & (1 << i))
1537 nOverrideValues[ nCell ][ i ] = nValue;
1542 void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete)
1544 if( !(nWwCols && pParamsTDelete) ) // set one or more cell length(s)
1545 return;
1547 sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
1548 if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
1549 return;
1550 sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
1551 if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
1552 return;
1555 * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
1556 * greater than or equal to itcLim to be moved
1558 int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
1560 if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
1562 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1563 int i = 0;
1564 while( i < nShlCnt )
1566 // adjust the left x-position
1567 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1569 // adjust the cell's borders
1570 *pCurrentTC = pTCs[ nitcLim + i];
1572 ++i;
1573 ++pCurrentTC;
1575 // adjust the left x-position of the dummy at the very end
1576 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1579 short nCellsDeleted = nitcLim - nitcFirst;
1580 //clip delete request to available number of cells
1581 if (nCellsDeleted > nWwCols)
1582 nCellsDeleted = nWwCols;
1583 nWwCols -= nCellsDeleted;
1586 // ReadShd reads the background color of a cell
1587 // ReadDef must be called before
1588 void WW8TabBandDesc::ReadShd(const sal_uInt8* pS )
1590 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1591 if( !nLen )
1592 return;
1594 if( !pSHDs )
1596 pSHDs = new WW8_SHD[nWwCols];
1599 short nCount = nLen >> 1;
1600 if (nCount > nWwCols)
1601 nCount = nWwCols;
1603 SVBT16 const * pShd;
1604 int i;
1605 for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
1606 pSHDs[i].SetWWValue( *pShd );
1609 void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart)
1611 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1612 if (!nLen || nStart >= nWwCols)
1613 return;
1615 if (!pNewSHDs)
1616 pNewSHDs = new Color[nWwCols];
1618 short nCount = nLen / 10 + nStart; //10 bytes each
1619 if (nCount > nWwCols)
1620 nCount = nWwCols;
1622 int i=nStart;
1623 while (i < nCount)
1624 pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
1626 while (i < nWwCols)
1627 pNewSHDs[i++] = COL_AUTO;
1630 namespace
1632 SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
1634 if (bVer67)
1635 return pPap->HasSprm(24);
1636 SprmResult aRes = pPap->HasSprm(0x244B);
1637 if (aRes.pSprm == nullptr)
1638 aRes = pPap->HasSprm(0x2416);
1639 return aRes;
1643 namespace {
1645 enum wwTableSprm
1647 sprmNil,
1649 sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
1650 sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft,
1651 sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete,
1652 sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
1653 sprmTDefTableNewShd, sprmTDefTableNewShd2nd, sprmTDefTableNewShd3rd,
1654 sprmTCellPadding, sprmTCellPaddingDefault
1659 static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
1661 switch (eVer)
1663 case ww::eWW8:
1664 switch (nId)
1666 case NS_sprm::TTableWidth::val:
1667 return sprmTTableWidth;
1668 case NS_sprm::TTextFlow::val:
1669 return sprmTTextFlow;
1670 case NS_sprm::TTableHeader::val:
1671 return sprmTTableHeader;
1672 case NS_sprm::TFCantSplit::val:
1673 return sprmTFCantSplit;
1674 case NS_sprm::TJc90::val:
1675 return sprmTJc;
1676 case NS_sprm::TFBiDi::val:
1677 return sprmTFBiDi;
1678 case NS_sprm::TDelete::val:
1679 return sprmTDelete;
1680 case NS_sprm::TInsert::val:
1681 return sprmTInsert;
1682 case NS_sprm::TDxaCol::val:
1683 return sprmTDxaCol;
1684 case NS_sprm::TDyaRowHeight::val:
1685 return sprmTDyaRowHeight;
1686 case NS_sprm::TDxaLeft::val:
1687 return sprmTDxaLeft;
1688 case NS_sprm::TDxaGapHalf::val:
1689 return sprmTDxaGapHalf;
1690 case NS_sprm::TTableBorders80::val:
1691 return sprmTTableBorders;
1692 case NS_sprm::TDefTable::val:
1693 return sprmTDefTable;
1694 case NS_sprm::TDefTableShd80::val:
1695 return sprmTDefTableShd;
1696 case NS_sprm::TDefTableShd::val:
1697 return sprmTDefTableNewShd;
1698 case NS_sprm::TDefTableShd2nd::val:
1699 return sprmTDefTableNewShd2nd;
1700 case NS_sprm::TDefTableShd3rd::val:
1701 return sprmTDefTableNewShd3rd;
1702 case NS_sprm::TTableBorders::val:
1703 return sprmTTableBorders90;
1704 case NS_sprm::TSetBrc80::val:
1705 return sprmTSetBrc;
1706 case NS_sprm::TSetBrc::val:
1707 return sprmTSetBrc90;
1708 case NS_sprm::TCellPadding::val:
1709 return sprmTCellPadding;
1710 case NS_sprm::TCellPaddingDefault::val:
1711 return sprmTCellPaddingDefault;
1713 break;
1714 case ww::eWW7:
1715 case ww::eWW6:
1716 switch (nId)
1718 case 182:
1719 return sprmTJc;
1720 case 183:
1721 return sprmTDxaLeft;
1722 case 184:
1723 return sprmTDxaGapHalf;
1724 case 186:
1725 return sprmTTableHeader;
1726 case 187:
1727 return sprmTTableBorders;
1728 case 189:
1729 return sprmTDyaRowHeight;
1730 case 190:
1731 return sprmTDefTable;
1732 case 191:
1733 return sprmTDefTableShd;
1734 case 193:
1735 return sprmTSetBrc;
1736 case 194:
1737 return sprmTInsert;
1738 case 195:
1739 return sprmTDelete;
1740 case 196:
1741 return sprmTDxaCol;
1743 break;
1744 case ww::eWW1:
1745 case ww::eWW2:
1746 switch (nId)
1748 case 146:
1749 return sprmTJc;
1750 case 147:
1751 return sprmTDxaLeft;
1752 case 148:
1753 return sprmTDxaGapHalf;
1754 case 153:
1755 return sprmTDyaRowHeight;
1756 case 154:
1757 return sprmTDefTable;
1758 case 155:
1759 return sprmTDefTableShd;
1760 case 157:
1761 return sprmTSetBrc;
1762 case 158:
1763 return sprmTInsert;
1764 case 159:
1765 return sprmTDelete;
1766 case 160:
1767 return sprmTDxaCol;
1769 break;
1771 return sprmNil;
1774 WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) :
1775 m_pIo(pIoClass),
1776 m_pFirstBand(nullptr),
1777 m_pActBand(nullptr),
1778 m_pTableNd(nullptr),
1779 m_pTabLines(nullptr),
1780 m_pTabLine(nullptr),
1781 m_pTabBoxes(nullptr),
1782 m_pTabBox(nullptr),
1783 m_pCurrentWWCell(nullptr),
1784 m_nRows(0),
1785 m_nDefaultSwCols(0),
1786 m_nBands(0),
1787 m_nMinLeft(0),
1788 m_nMaxRight(0),
1789 m_nSwWidth(0),
1790 m_nPreferredWidth(0),
1791 m_nPercentWidth(0),
1792 m_bOk(true),
1793 m_bClaimLineFormat(false),
1794 m_eOri(text::HoriOrientation::LEFT),
1795 m_bIsBiDi(false),
1796 m_nCurrentRow(0),
1797 m_nCurrentBandRow(0),
1798 m_nCurrentCol(0),
1799 m_nRowsToRepeat(0),
1800 m_pTable(nullptr),
1801 m_pParentPos(nullptr),
1802 m_pFlyFormat(nullptr),
1803 m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>)
1805 m_pIo->m_bCurrentAND_fNumberAcross = false;
1807 static const sal_Int16 aOriArr[] =
1809 text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
1812 bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
1813 WW8_TablePos aTabPos;
1815 WW8PLCFxSave1 aSave;
1816 m_pIo->m_xPlcxMan->GetPap()->Save( aSave );
1818 WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF();
1820 WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
1822 wwSprmParser aSprmParser(m_pIo->GetFib());
1824 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
1826 // process pPap until end of table found
1829 short nTabeDxaNew = SHRT_MAX;
1830 bool bTabRowJustRead = false;
1831 const sal_uInt8* pShadeSprm = nullptr;
1832 const sal_uInt8* pNewShadeSprm[3] = {nullptr, nullptr, nullptr};
1833 const sal_uInt8* pTableBorders = nullptr;
1834 sal_uInt16 nTableBordersLen = 0;
1835 const sal_uInt8* pTableBorders90 = nullptr;
1836 sal_uInt16 nTableBorders90Len = 0;
1837 // params, len
1838 std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s;
1839 WW8_TablePos *pTabPos = nullptr;
1841 // search end of a tab row
1842 if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
1844 m_bOk = false;
1845 break;
1848 // Get the SPRM chains:
1849 // first from PAP and then from PCD (of the Piece Table)
1850 WW8PLCFxDesc aDesc;
1851 pPap->GetSprms( &aDesc );
1852 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
1854 for (int nLoop = 0; nLoop < 2; ++nLoop)
1856 const sal_uInt8* pParams;
1857 while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams()))
1859 sal_uInt16 nId = aSprmIter.GetCurrentId();
1860 sal_Int32 nFixedLen = aSprmParser.DistanceToData(nId);
1861 sal_Int32 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen());
1862 sal_Int32 nLen = nL - nFixedLen;
1863 wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
1864 switch (eSprm)
1866 case sprmTTableWidth:
1868 const sal_uInt8 b0 = pParams[0];
1869 const sal_uInt8 b1 = pParams[1];
1870 const sal_uInt8 b2 = pParams[2];
1871 if (b0 == 3) // Twips
1872 m_nPreferredWidth = b2 * 0x100 + b1;
1873 else if (b0 == 2) // percent in fiftieths of a percent
1875 m_nPercentWidth = (b2 * 0x100 + b1);
1876 // MS documentation: non-negative, and 600% max
1877 if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 )
1878 m_nPercentWidth *= .02;
1879 else
1880 m_nPercentWidth = 100;
1883 break;
1884 case sprmTTextFlow:
1885 pNewBand->ProcessDirection(pParams);
1886 break;
1887 case sprmTFCantSplit:
1888 pNewBand->bCantSplit = *pParams;
1889 m_bClaimLineFormat = true;
1890 break;
1891 case sprmTTableBorders:
1892 pTableBorders = pParams; // process at end
1893 nTableBordersLen = nLen;
1894 break;
1895 case sprmTTableBorders90:
1896 pTableBorders90 = pParams; // process at end
1897 nTableBorders90Len = nLen;
1898 break;
1899 case sprmTTableHeader:
1900 // tdf#105570
1901 if ( m_nRowsToRepeat == m_nRows )
1902 m_nRowsToRepeat = (m_nRows + 1);
1903 break;
1904 case sprmTJc:
1905 // sprmTJc - Justification Code
1906 if (m_nRows == 0)
1907 m_eOri = aOriArr[*pParams & 0x3];
1908 break;
1909 case sprmTFBiDi:
1910 m_bIsBiDi = SVBT16ToUInt16(pParams) != 0;
1911 break;
1912 case sprmTDxaGapHalf:
1913 pNewBand->nGapHalf = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1914 break;
1915 case sprmTDyaRowHeight:
1916 pNewBand->nLineHeight = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1917 m_bClaimLineFormat = true;
1918 break;
1919 case sprmTDefTable:
1920 pNewBand->ReadDef(bOldVer, pParams, nLen);
1921 bTabRowJustRead = true;
1922 break;
1923 case sprmTDefTableShd:
1924 pShadeSprm = pParams;
1925 break;
1926 case sprmTDefTableNewShd:
1927 pNewShadeSprm[0] = pParams;
1928 break;
1929 case sprmTDefTableNewShd2nd:
1930 pNewShadeSprm[1] = pParams;
1931 break;
1932 case sprmTDefTableNewShd3rd:
1933 pNewShadeSprm[2] = pParams;
1934 break;
1935 case sprmTDxaLeft:
1936 // our Writer cannot shift single table lines
1937 // horizontally so we have to find the smallest
1938 // parameter (meaning the left-most position) and then
1939 // shift the whole table to that margin (see below)
1941 short nDxaNew = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1942 if( nDxaNew < nTabeDxaNew )
1943 nTabeDxaNew = nDxaNew;
1945 break;
1946 case sprmTSetBrc:
1947 aTSetBrcs.emplace_back(pParams, nLen); // process at end
1948 break;
1949 case sprmTSetBrc90:
1950 aTSetBrc90s.emplace_back(pParams, nLen); // process at end
1951 break;
1952 case sprmTDxaCol:
1953 pNewBand->ProcessSprmTDxaCol(pParams);
1954 break;
1955 case sprmTInsert:
1956 pNewBand->ProcessSprmTInsert(pParams);
1957 break;
1958 case sprmTDelete:
1959 pNewBand->ProcessSprmTDelete(pParams);
1960 break;
1961 case sprmTCellPaddingDefault:
1962 pNewBand->ProcessSpacing(pParams);
1963 break;
1964 case sprmTCellPadding:
1965 pNewBand->ProcessSpecificSpacing(pParams);
1966 break;
1967 default:
1970 aSprmIter.advance();
1973 if( !nLoop )
1975 pPap->GetPCDSprms( aDesc );
1976 aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
1980 // WW-Tables can contain Fly-changes. For this abort tables here
1981 // and start again. *pPap is still before TabRowEnd, so TestApo()
1982 // can be called with the last parameter set to false and therefore
1983 // take effect.
1985 if (bTabRowJustRead)
1987 // Some SPRMs need to be processed *after* ReadDef is called
1988 // so they were saved up until here
1989 if (pShadeSprm)
1990 pNewBand->ReadShd(pShadeSprm);
1991 if (pNewShadeSprm[0])
1992 pNewBand->ReadNewShd(pNewShadeSprm[0], bOldVer, /*nStart=*/0);
1993 if (pNewShadeSprm[1])
1994 pNewBand->ReadNewShd(pNewShadeSprm[1], bOldVer, /*nStart=*/22);
1995 if (pNewShadeSprm[2])
1996 pNewBand->ReadNewShd(pNewShadeSprm[2], bOldVer, /*nStart=*/44);
1997 if (pTableBorders90)
1998 pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len);
1999 else if (pTableBorders)
2000 pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
2001 pTableBorders, nTableBordersLen);
2002 for (const auto& a : aTSetBrcs)
2003 pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second);
2004 for (const auto& a : aTSetBrc90s)
2005 pNewBand->ProcessSprmTSetBRC(9, a.first, a.second);
2008 if( nTabeDxaNew < SHRT_MAX )
2010 short* pCenter = pNewBand->nCenter;
2011 short firstDxaCenter = *pCenter;
2012 for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
2014 // #i30298# Use sprmTDxaLeft to adjust the left indent
2015 // #i40461# Use dxaGapHalf during calculation
2016 *pCenter +=
2017 (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
2021 if (!m_pActBand)
2022 m_pActBand = m_pFirstBand = pNewBand;
2023 else
2025 m_pActBand->pNextBand = pNewBand;
2026 m_pActBand = pNewBand;
2028 m_nBands++;
2030 pNewBand = new WW8TabBandDesc;
2032 m_nRows++;
2033 m_pActBand->nRows++;
2035 //Seek our pap to its next block of properties
2036 WW8PLCFxDesc aRes;
2037 aRes.pMemPos = nullptr;
2038 aRes.nStartPos = nStartCp;
2040 if (!(pPap->SeekPos(aRes.nStartPos)))
2042 aRes.nEndPos = WW8_CP_MAX;
2043 pPap->SetDirty(true);
2045 pPap->GetSprms(&aRes);
2046 pPap->SetDirty(false);
2048 //Are we at the end of available properties
2049 if (
2050 !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
2051 aRes.nStartPos == WW8_CP_MAX
2054 m_bOk = false;
2055 break;
2058 //Are we still in a table cell
2059 SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer);
2060 const sal_uInt8* pParams = aParamsRes.pSprm;
2061 SprmResult aLevelRes = pPap->HasSprm(0x6649);
2062 const sal_uInt8 *pLevel = aLevelRes.pSprm;
2063 // InTable
2064 if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) ||
2065 (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable)))
2067 break;
2070 //Get the end of row new table positioning data
2071 WW8_CP nMyStartCp=nStartCp;
2072 if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
2073 if (SwWW8ImplReader::ParseTabPos(&aTabPos, pPap))
2074 pTabPos = &aTabPos;
2076 //Move back to this cell
2077 aRes.pMemPos = nullptr;
2078 aRes.nStartPos = nStartCp;
2080 // PlcxMan currently points too far ahead so we need to bring
2081 // it back to where we are trying to make a table
2082 m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
2083 m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
2084 if (!(pPap->SeekPos(aRes.nStartPos)))
2086 aRes.nEndPos = WW8_CP_MAX;
2087 pPap->SetDirty(true);
2089 pPap->GetSprms(&aRes);
2090 pPap->SetDirty(false);
2092 //Does this row match up with the last row closely enough to be
2093 //considered part of the same table
2094 ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
2097 ##513##, #79474# If this is not sufficient, then we should look at
2098 sprmPD{y|x}aAbs as our indicator that the following set of rows is not
2099 part of this table, but instead is an absolutely positioned table
2100 outside of this one
2102 if (aApo.mbStopApo)
2103 break;
2104 if (aApo.mbStartApo)
2106 //if there really is a fly here, and not a "null" fly then break.
2107 if (m_pIo->ConstructApo(aApo, pTabPos))
2108 break;
2111 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
2112 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
2114 SAL_WARN("sw.ww8", "WW8TabDesc, loop in paragraph property chain");
2115 break;
2117 nStartCp = aRes.nEndPos;
2119 while(true);
2121 if( m_bOk )
2123 if( m_pActBand->nRows > 1 )
2125 // last band has more than 1 cell
2126 delete pNewBand;
2127 pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
2128 m_pActBand->nRows--; // because of special treatment of border defaults
2129 pNewBand->nRows = 1;
2130 m_pActBand->pNextBand = pNewBand; // append at the end
2131 m_nBands++;
2132 pNewBand = nullptr; // do not delete
2134 CalcDefaults();
2136 delete pNewBand;
2138 m_pIo->m_xPlcxMan->GetPap()->Restore( aSave );
2141 WW8TabDesc::~WW8TabDesc()
2143 WW8TabBandDesc* pR = m_pFirstBand;
2144 while(pR)
2146 WW8TabBandDesc* pR2 = pR->pNextBand;
2147 delete pR;
2148 pR = pR2;
2151 delete m_pParentPos;
2154 void WW8TabDesc::CalcDefaults()
2156 short nMinCols = SHRT_MAX;
2157 WW8TabBandDesc* pR;
2159 m_nMinLeft = SHRT_MAX;
2160 m_nMaxRight = SHRT_MIN;
2163 If we are an honestly inline centered table, then the normal rules of
2164 engagement for left and right margins do not apply. The multiple rows are
2165 centered regardless of the actual placement of rows, so we cannot have
2166 mismatched rows as is possible in other configurations.
2168 e.g. change the example bugdoc in word from text wrapping of none (inline)
2169 to around (in frame (bApo)) and the table splits into two very disjoint
2170 rows as the beginning point of each row are very different
2172 if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
2174 for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
2175 for( short i = pR->nWwCols; i >= 0; --i)
2176 pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
2179 // First loop: find outermost L and R borders
2180 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2182 if( pR->nCenter[0] < m_nMinLeft )
2183 m_nMinLeft = pR->nCenter[0];
2185 // Following adjustment moves a border and then uses it to find width
2186 // of next cell, so collect current widths, to avoid situation when width
2187 // adjustment to too narrow cell makes next cell have negative width
2188 short nOrigWidth[MAX_COL + 1];
2189 for( short i = 0; i < pR->nWwCols; i++ )
2191 nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
2194 for( short i = 0; i < pR->nWwCols; i++ )
2197 If the margins are so large as to make the displayable
2198 area inside them smaller than the minimum allowed then adjust the
2199 width to fit. But only do it if the two cells are not the exact
2200 same value, if they are then the cell does not really exist and will
2201 be blended together into the same cell through the use of the
2202 nTrans(late) array.
2203 #i28333# If the nGapHalf is greater than the cell width best to ignore it
2205 int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
2206 if (nCellWidth != nOrigWidth[i])
2208 if (nOrigWidth[i] == 0)
2209 nCellWidth = 0; // restore zero-width "cell"
2210 else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
2211 nCellWidth = pR->nGapHalf + 1; // avoid false ignore
2212 else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
2213 nCellWidth = 1; // minimal non-zero width to minimize distortion
2215 if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
2217 nCellWidth = MINLAY + pR->nGapHalf * 2;
2219 pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
2222 if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
2223 m_nMaxRight = pR->nCenter[pR->nWwCols];
2225 m_nSwWidth = m_nMaxRight - m_nMinLeft;
2227 // If the table is right aligned we need to align all rows to the
2228 // row that has the furthest right point
2230 if(m_eOri == text::HoriOrientation::RIGHT)
2232 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2234 int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
2235 for( short i = 0; i < pR->nWwCols + 1; i++ )
2237 pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
2243 // 2. pass: Detect number of writer columns. This can exceed the count
2244 // of columns in WW by 2, because SW in contrast to WW does not provide
2245 // fringed left and right borders and has to fill with empty boxes.
2246 // Non existent cells can reduce the number of columns.
2248 // 3. pass: Replace border with defaults if needed
2249 for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
2251 if( !pR->pTCs )
2253 pR->pTCs = new WW8_TCell[ pR->nWwCols ];
2255 for (int k = 0; k < pR->nWwCols; ++k)
2257 WW8_TCell& rT = pR->pTCs[k];
2258 for (int i = 0; i < 4; ++i)
2260 if (rT.rgbrc[i].brcType()==0)
2262 // if shadow is set, its invalid
2263 int j = i;
2264 switch( i )
2266 case 0:
2267 // outer top / horizontally inside
2268 j = (pR == m_pFirstBand) ? 0 : 4;
2269 break;
2270 case 1:
2271 // outer left / vertically inside
2272 j = k ? 5 : 1;
2273 break;
2274 case 2:
2275 // outer bottom / horizontally inside
2276 j = pR->pNextBand ? 4 : 2;
2277 break;
2278 case 3:
2279 // outer right / vertically inside
2280 j = (k == pR->nWwCols - 1) ? 3 : 5;
2281 break;
2283 // merge with above defaults
2284 rT.rgbrc[i] = pR->aDefBrcs[j];
2290 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2292 pR->nSwCols = pR->nWwCols;
2293 pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
2294 pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols]) >= MINLAY;
2296 short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
2297 sal_uInt16 i;
2298 sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
2299 for (i = 0; i < pR->nWwCols; ++i)
2301 pR->nTransCell[i] = static_cast<sal_Int8>(j);
2302 if ( pR->nCenter[i] < pR->nCenter[i+1] )
2304 pR->bExist[i] = true;
2305 j++;
2307 else
2309 pR->bExist[i] = false;
2310 nAddCols--;
2314 OSL_ENSURE(i,"no columns in row ?");
2317 If the last cell was "false" then there is no valid cell following it,
2318 so the default mapping forward won't work. So map it (and
2319 contiguous invalid cells backwards to the last valid cell instead.)
2321 if (i && !pR->bExist[i-1])
2323 sal_uInt16 k=i-1;
2324 while (k && !pR->bExist[k])
2325 k--;
2326 for (sal_uInt16 n=k+1;n<i;n++)
2327 pR->nTransCell[n] = pR->nTransCell[k];
2330 pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other
2331 pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol
2333 pR->nSwCols = pR->nSwCols + nAddCols;
2334 if( pR->nSwCols < nMinCols )
2335 nMinCols = pR->nSwCols;
2338 if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
2339 (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
2340 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
2342 m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
2343 if( m_nDefaultSwCols == 0 )
2344 m_bOk = false;
2345 m_pActBand = m_pFirstBand;
2346 m_nCurrentBandRow = 0;
2347 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2350 void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat)
2352 SwFrameFormat* pApply = pFrameFormat;
2353 if (!pApply )
2354 pApply = m_pTable->GetFrameFormat();
2355 OSL_ENSURE(pApply,"No frame");
2356 pApply->SetFormatAttr(m_aItemSet);
2357 if (pFrameFormat)
2359 SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
2360 aSize.SetHeightSizeType(SwFrameSize::Minimum);
2361 aSize.SetHeight(MINLAY);
2362 pFrameFormat->SetFormatAttr(aSize);
2363 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
2367 void wwSectionManager::PrependedInlineNode(const SwPosition &rPos,
2368 const SwNode &rNode)
2370 OSL_ENSURE(!maSegments.empty(),
2371 "should not be possible, must be at least one segment");
2372 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
2373 maSegments.back().maStart.Assign(rNode);
2376 void WW8TabDesc::CreateSwTable()
2378 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
2380 // if there is already some content on the Node append new node to ensure
2381 // that this content remains ABOVE the table
2382 SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
2383 bool bInsNode = pPoint->GetContentIndex() != 0;
2384 bool bSetMinHeight = false;
2387 #i8062#
2388 Set fly anchor to its anchor pos, so that if a table starts immediately
2389 at this position a new node will be inserted before inserting the table.
2391 SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo)
2392 ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr;
2393 if (pFormat)
2395 const SwNode* pAnchorNode =
2396 pFormat->GetAnchor().GetAnchorNode();
2397 if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
2399 bInsNode = true;
2400 bSetMinHeight = true;
2402 SwFormatSurround aSur(pFormat->GetSurround());
2403 aSur.SetAnchorOnly(true);
2404 pFormat->SetFormatAttr(aSur);
2408 if (bSetMinHeight)
2410 // minimize Fontsize to minimize height growth of the header/footer
2411 // set font size to 1 point to minimize y-growth of Hd/Ft
2412 SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE);
2413 m_pIo->NewAttr( aSz );
2414 m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
2417 if (bInsNode)
2418 m_pIo->AppendTextNode(*pPoint);
2420 m_xTmpPos = m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pPaM->GetPoint());
2422 // Because SW cannot handle multi-page floating frames,
2423 // _any unnecessary_ floating tables have been converted to inline.
2424 tools::Long nLeft = 0;
2425 if (m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->GetFlyFormat())
2427 // Get the table orientation from the fly
2428 // Do we also need to check m_pIo->m_xSFlyPara->bTogglePos/IsPosToggle()? [Probably not - layout-only concern]
2429 const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos;
2430 const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT
2431 : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT;
2432 if ( bIsInsideMargin && bAdjustMargin )
2433 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH;
2434 else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE )
2435 m_eOri = m_pIo->m_xSFlyPara->eHAlign;
2436 if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH )
2438 nLeft = m_pIo->m_xSFlyPara->nXPos;
2439 if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME )
2441 if ( !m_bIsBiDi )
2442 nLeft -= m_pIo->m_aSectionManager.GetPageLeft();
2443 else
2444 nLeft += m_pIo->m_aSectionManager.GetPageRight();
2449 // The table is small: The number of columns is the lowest count of
2450 // columns of the origin, because inserting is faster than deleting.
2451 // The number of rows is the count of bands because (identically)
2452 // rows of a band can be duplicated easy.
2453 m_pTable = m_pIo->m_rDoc.InsertTable(
2454 SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 0 ),
2455 *m_xTmpPos->GetPoint(), m_nBands, m_nDefaultSwCols, m_eOri );
2457 OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
2458 if (!m_pTable || !m_pTable->GetFrameFormat())
2459 return;
2461 SwTableNode* pTableNode = m_pTable->GetTableNode();
2462 OSL_ENSURE(pTableNode, "no table node!");
2463 if (pTableNode)
2465 m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(),
2466 *pTableNode);
2469 // Check if the node into which the table should be inserted already
2470 // contains a Pagedesc. If so that Pagedesc would be moved to the
2471 // row after the table, that would be wrong. So delete and
2472 // set later to the table format.
2473 if (SwTextNode *const pNd = m_xTmpPos->GetPoint()->GetNode().GetTextNode())
2475 if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
2477 std::unique_ptr<SfxPoolItem> pSetAttr;
2479 if (const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet(RES_BREAK, false))
2481 pSetAttr.reset(new SvxFormatBreakItem( *pBreakItem ));
2482 pNd->ResetAttr( RES_BREAK );
2485 // eventually set the PageDesc/Break now to the table
2486 if (pSetAttr)
2488 m_aItemSet.Put(std::move(pSetAttr));
2493 // total width of table
2494 if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols )
2496 SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, m_nSwWidth);
2497 // Don't set relative width if the table is in a floating frame
2498 if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->GetFlyFormat()) )
2499 aFrameSize.SetWidthPercent(m_nPercentWidth);
2500 m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize);
2501 m_aItemSet.Put(aFrameSize);
2504 SvxFrameDirectionItem aDirection(
2505 m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
2506 m_pTable->GetFrameFormat()->SetFormatAttr(aDirection);
2508 if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
2510 if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_xSFlyPara &&
2511 m_pIo->m_xSFlyPara->GetFlyFormat() && GetMinLeft())
2513 //If we are inside a frame and we have a border, the frames
2514 //placement does not consider the tables border, which word
2515 //displays outside the frame, so adjust here.
2516 SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->GetFlyFormat()->GetHoriOrient());
2517 sal_Int16 eHori = aHori.GetHoriOrient();
2518 if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
2519 (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
2521 //With multiple table, use last table settings. Perhaps
2522 //the maximum is what word does ?
2523 aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft());
2524 aHori.SetHoriOrient(text::HoriOrientation::NONE);
2525 m_pIo->m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aHori);
2528 else // Not directly in a floating frame.
2530 //Historical note: If InLocalApo(), then this table is being placed in a floating
2531 //frame, and the frame matches the left and right *lines* of the
2532 //table, so the space to the left of the table isn't to be used
2533 //inside the frame, in word the dialog involved greys out the
2534 //ability to set the margin.
2535 SvxLRSpaceItem aL( RES_LR_SPACE );
2537 if (!m_bIsBiDi)
2538 nLeft += GetMinLeft();
2539 else
2541 const short nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth;
2542 nLeft += m_pIo->m_aSectionManager.GetTextAreaWidth();
2543 nLeft = nLeft - nTableWidth - GetMinLeft();
2545 aL.SetLeft(nLeft);
2547 m_aItemSet.Put(aL);
2551 mxOldRedlineStack = std::move(m_pIo->m_xRedlineStack);
2552 m_pIo->m_xRedlineStack.reset(new sw::util::RedlineStack(m_pIo->m_rDoc));
2555 void WW8TabDesc::UseSwTable()
2557 // init global Vars
2558 m_pTabLines = &m_pTable->GetTabLines();
2559 m_nCurrentRow = m_nCurrentCol = m_nCurrentBandRow = 0;
2561 m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
2562 GetSttNd()->FindTableNode());
2563 OSL_ENSURE( m_pTableNd, "Where is my table node" );
2565 // #i69519# - Restrict rows to repeat to a decent value
2566 if ( m_nRowsToRepeat == o3tl::narrowing<sal_uInt16>(m_nRows) )
2567 m_nRowsToRepeat = 1;
2569 m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat );
2570 // insert extra cells if needed and something like this
2571 AdjustNewBand();
2573 WW8DupProperties aDup(m_pIo->m_rDoc, m_pIo->m_xCtrlStck.get());
2574 m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
2576 // now set the correct PaM and prepare first merger group if any
2577 SetPamInCell(m_nCurrentCol, true);
2578 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2580 m_pIo->m_bWasTabRowEnd = false;
2581 m_pIo->m_bWasTabCellEnd = false;
2584 void WW8TabDesc::MergeCells()
2586 short nRow;
2588 for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand)
2590 // insert current box into merge group if appropriate.
2591 // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
2592 if( m_pActBand->pTCs )
2594 for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
2595 for( short i = 0; i < m_pActBand->nWwCols; i++ )
2597 WW8SelBoxInfo* pActMGroup = nullptr;
2599 // start a new merge group if appropriate
2601 OSL_ENSURE(nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()),
2602 "Too few lines, table ended early");
2603 if (nRow >= o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2604 return;
2605 m_pTabLine = (*m_pTabLines)[ nRow ];
2606 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2608 sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
2609 if (!m_pActBand->bExist[i])
2610 continue;
2611 OSL_ENSURE(nCol < m_pTabBoxes->size(),
2612 "Too few columns, table ended early");
2613 if (nCol >= m_pTabBoxes->size())
2614 return;
2615 m_pTabBox = (*m_pTabBoxes)[nCol];
2616 WW8_TCell& rCell = m_pActBand->pTCs[ i ];
2617 // is this the left upper cell of a merge group ?
2619 bool bMerge = false;
2620 if ( rCell.bVertRestart && !rCell.bMerged )
2621 bMerge = true;
2622 else if (rCell.bFirstMerged && m_pActBand->bExist[i])
2624 // Some tests to avoid merging cells which previously were
2625 // declared invalid because of sharing the exact same dimensions
2626 // as their previous cell
2628 //If there's anything underneath/above we're ok.
2629 if (rCell.bVertMerge || rCell.bVertRestart)
2630 bMerge = true;
2631 else
2633 //If it's a hori merge only, and the only things in
2634 //it are invalid cells then it's already taken care
2635 //of, so don't merge.
2636 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2637 if (m_pActBand->pTCs[ i2 ].bMerged &&
2638 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2640 if (m_pActBand->bExist[i2])
2642 bMerge = true;
2643 break;
2646 else
2647 break;
2651 // remove numbering from cells that will be disabled in the merge
2652 if( rCell.bVertMerge && !rCell.bVertRestart )
2654 SwPaM aPam( *m_pTabBox->GetSttNd(), 0 );
2655 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2656 SwTextNode* pNd = aPam.GetPointNode().GetTextNode();
2657 while( pNd )
2659 pNd->SetCountedInList( false );
2661 aPam.GetPoint()->Adjust(SwNodeOffset(1));
2662 pNd = aPam.GetPointNode().GetTextNode();
2666 if (bMerge)
2668 short nX1 = m_pActBand->nCenter[ i ];
2669 short nWidth = m_pActBand->nWidth[ i ];
2671 // 2. create current merge group
2672 pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
2674 // determine size of new merge group
2675 // before inserted the new merge group.
2676 // Needed to correctly locked previously created merge groups.
2677 // Calculate total width and set
2678 short nSizCell = m_pActBand->nWidth[ i ];
2679 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2680 if (m_pActBand->pTCs[ i2 ].bMerged &&
2681 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2683 nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
2685 else
2686 break;
2687 pActMGroup->m_nGroupWidth = nSizCell;
2689 // locked previously created merge groups,
2690 // after determining the size for the new merge group.
2691 // 1. If necessary close old merge group(s) that overlap
2692 // the X-area of the new group
2693 for (;;)
2695 WW8SelBoxInfo* p = FindMergeGroup(
2696 nX1, pActMGroup->m_nGroupWidth, false );
2697 if (p == nullptr)
2699 break;
2701 p->m_bGroupLocked = true;
2704 // 3. push to group array
2705 m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
2708 // if necessary add the current box to a merge group
2709 // (that can be a newly created or another group)
2710 UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
2716 //There is a limbo area in word at the end of the row marker
2717 //where properties can live in word, there is no location in
2718 //writer equivalent, so try and park the cursor in the best
2719 //match, see #i23022#/#i18644#
2720 void WW8TabDesc::ParkPaM()
2722 SwTableBox *pTabBox2 = nullptr;
2723 short nRow = m_nCurrentRow + 1;
2724 if (nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2726 if (SwTableLine *pLine = (*m_pTabLines)[nRow])
2728 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
2729 pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
2733 if (!pTabBox2 || !pTabBox2->GetSttNd())
2735 MoveOutsideTable();
2736 return;
2739 SwNodeOffset nSttNd = pTabBox2->GetSttIdx() + 1,
2740 nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
2742 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2746 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2748 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2750 m_pIo->m_pPaM->GetPoint()->SetContent(0);
2751 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2755 void WW8TabDesc::MoveOutsideTable()
2757 OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored");
2758 if (m_xTmpPos && m_pIo)
2759 *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos->GetPoint();
2762 void WW8TabDesc::FinishSwTable()
2764 m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
2766 // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2767 // place, or somewhere close if that place got destroyed
2768 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_oLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_oLastAnchorPos) : nullptr);
2769 m_pIo->m_oLastAnchorPos.reset();
2771 SwTableNode* pTableNode = m_pTable->GetTableNode();
2772 SwDeleteListener aListener(*pTableNode);
2773 m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack);
2775 if (xLastAnchorCursor)
2776 m_pIo->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
2778 WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get());
2779 m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
2781 MoveOutsideTable();
2782 m_xTmpPos.reset();
2784 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2786 m_pIo->m_bWasTabRowEnd = false;
2787 m_pIo->m_bWasTabCellEnd = false;
2789 m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM);
2791 if (aListener.WasDeleted())
2792 throw std::runtime_error("table unexpectedly destroyed by applying redlines");
2794 MergeCells();
2796 // if needed group cells together that should be merged
2797 if (m_MergeGroups.empty())
2798 return;
2800 // process all merge groups one by one
2801 for (auto const& groupIt : m_MergeGroups)
2803 if((1 < groupIt->size()) && groupIt->row(0)[0])
2805 SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
2806 pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->m_nGroupWidth, 0));
2807 const sal_uInt16 nRowSpan = groupIt->rowsCount();
2808 for (sal_uInt16 n = 0; n < nRowSpan; ++n)
2810 auto& rRow = groupIt->row(n);
2811 for (size_t i = 0; i<rRow.size(); ++i)
2813 const sal_Int32 nRowSpanSet = (n == 0) && (i == 0) ?
2814 nRowSpan :
2815 (-1 * (nRowSpan - n));
2816 SwTableBox* pCurrentBox = rRow[i];
2817 pCurrentBox->setRowSpan(nRowSpanSet);
2819 if (i == 0)
2820 pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
2821 else
2823 SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
2824 pFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 0, 0));
2830 m_pIo->m_xFormatOfJustInsertedApo.reset();
2831 m_MergeGroups.clear();
2834 // browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
2836 // Parameter: nXcenter = center position of asking box
2837 // nWidth = width of asking box
2838 // bExact = flag, if box has to fit into group
2839 // or only has to touch
2841 WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
2843 if (!m_MergeGroups.empty())
2845 // still valid area near the boundary
2846 const short nTolerance = 4;
2847 // box boundary
2848 short nX2 = nX1 + nWidth;
2849 // approximate group boundary
2850 short nGrX1;
2851 short nGrX2;
2853 // improvement: search backwards
2854 for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
2856 // the currently inspected group
2857 WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
2858 if (!rActGroup.m_bGroupLocked)
2860 // approximate group boundary with room (tolerance) to the *outside*
2861 nGrX1 = rActGroup.m_nGroupXStart - nTolerance;
2862 nGrX2 = rActGroup.m_nGroupXStart
2863 + rActGroup.m_nGroupWidth + nTolerance;
2865 // If box fits report success
2867 if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
2869 return &rActGroup;
2872 // does the box share areas with the group?
2874 if( !bExact )
2876 // successful if nX1 *or* nX2 are inside the group
2877 if( ( ( nX1 > nGrX1 )
2878 && ( nX1 < nGrX2 - 2*nTolerance ) )
2879 || ( ( nX2 > nGrX1 + 2*nTolerance )
2880 && ( nX2 < nGrX2 ) )
2881 // or nX1 and nX2 surround the group
2882 || ( ( nX1 <=nGrX1 )
2883 && ( nX2 >=nGrX2 ) ) )
2885 return &rActGroup;
2891 return nullptr;
2894 bool WW8TabDesc::IsValidCell(short nCol) const
2896 return (o3tl::make_unsigned(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) &&
2897 m_pActBand->bExist[nCol] &&
2898 o3tl::make_unsigned(m_nCurrentRow) < m_pTabLines->size();
2901 bool WW8TabDesc::InFirstParaInCell() const
2903 //e.g. #i19718#
2904 if (!m_pTabBox || !m_pTabBox->GetSttNd())
2906 OSL_FAIL("Problem with table");
2907 return false;
2910 if (!IsValidCell(GetCurrentCol()))
2911 return false;
2913 return m_pIo->m_pPaM->GetPoint()->GetNodeIndex() == m_pTabBox->GetSttIdx() + 1;
2916 void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
2918 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2919 if (!m_pActBand)
2920 return;
2922 sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
2924 if (o3tl::make_unsigned(m_nCurrentRow) >= m_pTabLines->size())
2926 OSL_ENSURE(false, "Actual row bigger than expected." );
2927 if (bPam)
2928 MoveOutsideTable();
2929 return;
2932 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
2933 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2935 if (nCol >= m_pTabBoxes->size())
2937 if (bPam)
2939 // The first paragraph in a cell with upper autospacing has upper
2940 // spacing set to 0
2941 if (
2942 m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara &&
2943 !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing
2946 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
2949 // The last paragraph in a cell with lower autospacing has lower
2950 // spacing set to 0
2951 if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2952 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
2954 ParkPaM();
2956 return;
2958 m_pTabBox = (*m_pTabBoxes)[nCol];
2959 if( !m_pTabBox->GetSttNd() )
2961 OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table");
2962 if (bPam)
2963 MoveOutsideTable();
2964 return;
2966 if (!bPam)
2967 return;
2969 m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ];
2971 // The first paragraph in a cell with upper autospacing has upper spacing set to 0
2972 if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2973 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
2975 // The last paragraph in a cell with lower autospacing has lower spacing set to 0
2976 if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2977 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
2979 //We need to set the pPaM on the first cell, invalid
2980 //or not so that we can collect paragraph properties over
2981 //all the cells, but in that case on the valid cell we do not
2982 //want to reset the fmt properties
2983 SwNodeOffset nSttNd = m_pTabBox->GetSttIdx() + 1,
2984 nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
2985 if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
2989 m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
2991 while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2992 m_pIo->m_pPaM->GetPoint()->SetContent(0);
2993 // Precautionally set now, otherwise the style is not set for cells
2994 // that are inserted for margin balancing.
2995 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2996 // because this cells are invisible helper constructions only to simulate
2997 // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
3000 // Better to turn Snap to Grid off for all paragraphs in tables
3001 SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
3002 SwTextNode *pNd = pGridPos->GetNode().GetTextNode();
3003 if(!pNd)
3004 return;
3006 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
3007 const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
3009 if(!rSnapToGrid.GetValue())
3010 return;
3012 SvxParaGridItem aGridItem( rSnapToGrid );
3013 aGridItem.SetValue(false);
3015 const sal_Int32 nEnd = pGridPos->GetContentIndex();
3016 pGridPos->SetContent(0);
3017 m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem);
3018 pGridPos->SetContent(nEnd);
3019 m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
3022 void WW8TabDesc::InsertCells( short nIns )
3024 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
3025 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
3026 m_pTabBox = (*m_pTabBoxes)[0];
3028 m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, static_cast<SwTableBoxFormat*>(m_pTabBox->GetFrameFormat()),
3029 const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
3030 // The third parameter contains the FrameFormat of the boxes.
3031 // Here it is possible to optimize to save (reduce) FrameFormats.
3034 void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
3036 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3037 return; // faked cells -> no border
3039 SvxBoxItem aFormatBox( RES_BOX );
3040 if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
3042 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3043 if (SwWW8ImplReader::IsBorder(pT->rgbrc))
3044 SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
3047 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP))
3049 aFormatBox.SetDistance(
3050 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP],
3051 SvxBoxItemLine::TOP);
3053 else
3054 aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
3055 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM))
3057 aFormatBox.SetDistance(
3058 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM],
3059 SvxBoxItemLine::BOTTOM);
3061 else
3062 aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
3064 // nGapHalf for WW is a *horizontal* gap between table cell and content.
3065 short nLeftDist =
3066 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf;
3067 short nRightDist =
3068 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf;
3069 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT))
3071 aFormatBox.SetDistance(
3072 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT],
3073 SvxBoxItemLine::LEFT);
3075 else
3076 aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
3077 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT))
3079 aFormatBox.SetDistance(
3080 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT],
3081 SvxBoxItemLine::RIGHT);
3083 else
3084 aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
3086 pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
3089 void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
3091 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3092 return; // faked cells -> no color
3094 bool bFound=false;
3095 if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
3097 Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
3098 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND));
3099 bFound = true;
3102 //If there was no new shades, or no new shade setting
3103 if (m_pActBand->pSHDs && !bFound)
3105 WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
3106 if (!rSHD.GetValue()) // auto
3107 return;
3109 SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
3110 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.m_aColor, RES_BACKGROUND));
3114 static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
3116 SvxFrameDirection eDir = SvxFrameDirection::Environment;
3117 // 1: Asian layout with rotated CJK characters
3118 // 5: Asian layout
3119 // 3: Western layout rotated by 90 degrees
3120 // 4: Western layout
3121 switch (nCode)
3123 default:
3124 OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield");
3125 [[fallthrough]];
3126 case 3:
3127 eDir = SvxFrameDirection::Vertical_LR_BT;
3128 break;
3129 case 5:
3130 eDir = SvxFrameDirection::Vertical_RL_TB;
3131 break;
3132 case 1:
3133 eDir = SvxFrameDirection::Vertical_RL_TB;
3134 break;
3135 case 4:
3136 eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables
3137 break;
3139 return eDir;
3142 void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx)
3144 if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
3145 return;
3146 SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR);
3147 pBox->GetFrameFormat()->SetFormatAttr(aItem);
3150 void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
3152 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3153 return;
3155 sal_Int16 eVertOri=text::VertOrientation::TOP;
3157 if( m_pActBand->pTCs )
3159 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3160 switch (pT->nVertAlign)
3162 case 0:
3163 default:
3164 eVertOri = text::VertOrientation::TOP;
3165 break;
3166 case 1:
3167 eVertOri = text::VertOrientation::CENTER;
3168 break;
3169 case 2:
3170 eVertOri = text::VertOrientation::BOTTOM;
3171 break;
3175 pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
3178 void WW8TabDesc::AdjustNewBand()
3180 if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
3181 InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols );
3183 SetPamInCell( 0, false);
3184 OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == o3tl::narrowing<sal_uInt16>(m_pActBand->nSwCols),
3185 "Wrong column count in table" );
3187 if( m_bClaimLineFormat )
3189 m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
3190 SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default
3192 if (m_pActBand->nLineHeight == 0) // 0 = Auto
3193 aF.SetHeightSizeType( SwFrameSize::Variable );
3194 else
3196 if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
3198 aF.SetHeightSizeType(SwFrameSize::Fixed);
3199 m_pActBand->nLineHeight = -m_pActBand->nLineHeight;
3201 if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
3202 m_pActBand->nLineHeight = MINLAY;
3204 aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
3206 m_pTabLine->GetFrameFormat()->SetFormatAttr(aF);
3209 //Word stores 1 for bCantSplit if the row cannot be split, we set true if
3210 //we can split the row
3211 bool bSetCantSplit = m_pActBand->bCantSplit;
3212 m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit));
3214 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
3215 if (bSetCantSplit && m_pTabLines->size() == 1)
3216 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit(false));
3218 short i; // SW-Index
3219 short j; // WW-Index
3220 short nW; // Width
3221 SwFormatFrameSize aFS( SwFrameSize::Fixed );
3222 j = m_pActBand->bLEmptyCol ? -1 : 0;
3224 for( i = 0; i < m_pActBand->nSwCols; i++ )
3226 // set cell width
3227 if( j < 0 )
3228 nW = m_pActBand->nCenter[0] - m_nMinLeft;
3229 else
3231 //Set j to first non invalid cell
3232 while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
3233 j++;
3235 if( j < m_pActBand->nWwCols )
3236 nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3237 else
3238 nW = m_nMaxRight - m_pActBand->nCenter[j];
3239 m_pActBand->nWidth[ j ] = nW;
3242 SwTableBox* pBox = (*m_pTabBoxes)[i];
3243 // could be reduced further by intelligent moving of FrameFormats
3244 pBox->ClaimFrameFormat();
3246 SetTabBorders(pBox, j);
3248 SvxBoxItem aCurrentBox(pBox->GetFrameFormat()->GetFormatAttr(RES_BOX));
3249 pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
3251 SetTabVertAlign(pBox, j);
3252 SetTabDirection(pBox, j);
3253 if( m_pActBand->pSHDs || m_pActBand->pNewSHDs)
3254 SetTabShades(pBox, j);
3255 j++;
3257 aFS.SetWidth( nW );
3258 pBox->GetFrameFormat()->SetFormatAttr( aFS );
3260 // skip non existing cells
3261 while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
3263 m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3264 j++;
3269 void WW8TabDesc::TableCellEnd()
3271 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
3273 // new line/row
3274 if( m_pIo->m_bWasTabRowEnd )
3276 // bWasTabRowEnd will be deactivated in
3277 // SwWW8ImplReader::ProcessSpecial()
3279 sal_uInt16 iCol = GetLogicalWWCol();
3280 if (iCol < m_aNumRuleNames.size())
3282 m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
3283 m_aNumRuleNames.end());
3286 m_nCurrentCol = 0;
3287 m_nCurrentRow++;
3288 m_nCurrentBandRow++;
3289 OSL_ENSURE( m_pActBand , "pActBand is 0" );
3290 if( m_pActBand )
3292 if( m_nCurrentRow >= m_nRows ) // nothing to at end of table
3293 return;
3295 bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows;
3296 if( bNewBand )
3297 { // new band needed ?
3298 m_pActBand = m_pActBand->pNextBand;
3299 m_nCurrentBandRow = 0;
3300 OSL_ENSURE( m_pActBand, "pActBand is 0" );
3301 AdjustNewBand();
3303 else
3305 SwTableBox* pBox = (*m_pTabBoxes)[0];
3306 SwSelBoxes aBoxes;
3307 m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
3311 else
3312 { // new column ( cell )
3313 m_nCurrentCol++;
3315 SetPamInCell(m_nCurrentCol, true);
3317 // finish Annotated Level Numbering ?
3318 if (m_pIo->m_bAnl && !m_pIo->m_bCurrentAND_fNumberAcross && m_pActBand)
3319 m_pIo->StopAllAnl(IsValidCell(m_nCurrentCol));
3322 // if necessary register the box for the merge group for this column
3323 void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell const & rCell,
3324 WW8SelBoxInfo* pActGroup,
3325 SwTableBox* pActBox,
3326 sal_uInt16 nCol )
3328 // check if the box has to be merged
3329 // If cell is the first one to be merged, a new merge group has to be provided.
3330 // E.g., it could be that a cell is the first one to be merged, but no
3331 // new merge group is provided, because the potential other cell to be merged
3332 // doesn't exist - see method <WW8TabDesc::MergeCells>.
3333 if ( !(m_pActBand->bExist[ nCol ] &&
3334 ( ( rCell.bFirstMerged && pActGroup ) ||
3335 rCell.bMerged ||
3336 rCell.bVertMerge ||
3337 rCell.bVertRestart )) )
3338 return;
3340 // detect appropriate merge group
3341 WW8SelBoxInfo* pTheMergeGroup = nullptr;
3342 if( pActGroup )
3343 // assign group
3344 pTheMergeGroup = pActGroup;
3345 else
3347 // find group
3348 pTheMergeGroup = FindMergeGroup(
3349 m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
3351 if( pTheMergeGroup )
3353 // add current box to merge group
3354 pTheMergeGroup->push_back(pActBox);
3358 sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
3360 sal_uInt16 nCol = 0;
3361 if( m_pActBand && m_pActBand->pTCs)
3363 for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol )
3365 if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
3366 ++nCol;
3369 return nCol;
3372 // find name of numrule valid for current WW-COL
3373 OUString WW8TabDesc::GetNumRuleName() const
3375 sal_uInt16 nCol = GetLogicalWWCol();
3376 if (nCol < m_aNumRuleNames.size())
3377 return m_aNumRuleNames[nCol];
3378 return OUString();
3381 void WW8TabDesc::SetNumRuleName( const OUString& rName )
3383 sal_uInt16 nCol = GetLogicalWWCol();
3384 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
3385 m_aNumRuleNames.emplace_back();
3386 m_aNumRuleNames[nCol] = rName;
3389 bool SwWW8ImplReader::StartTable(WW8_CP nStartCp)
3391 // Entering a table so make sure the FirstPara flag gets set
3392 m_bFirstPara = true;
3393 // no recursive table, not with InsertFile in table or foot note
3394 if (m_bReadNoTable)
3395 return false;
3397 if (m_xTableDesc)
3398 m_aTableStack.push(std::move(m_xTableDesc));
3400 // #i33818# - determine absolute position object attributes,
3401 // if possible. It's needed for nested tables.
3402 std::unique_ptr<WW8FlyPara> pTableWFlyPara;
3403 WW8SwFlyPara* pTableSFlyPara( nullptr );
3404 // #i45301# - anchor nested table inside Writer fly frame
3405 // only at-character, if absolute position object attributes are available.
3406 // Thus, default anchor type is as-character anchored.
3407 RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR );
3408 if ( m_nInTable )
3410 WW8_TablePos* pNestedTabPos( nullptr );
3411 WW8_TablePos aNestedTabPos;
3412 WW8PLCFxSave1 aSave;
3413 m_xPlcxMan->GetPap()->Save( aSave );
3414 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
3415 WW8_CP nMyStartCp = nStartCp;
3416 if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
3417 ParseTabPos( &aNestedTabPos, pPap ) )
3419 pNestedTabPos = &aNestedTabPos;
3421 m_xPlcxMan->GetPap()->Restore( aSave );
3422 if ( pNestedTabPos )
3424 ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
3425 pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
3426 if ( pTableWFlyPara )
3428 // <WW8SwFlyPara> constructor has changed - new 4th parameter
3429 // containing WW8 page top margin.
3430 pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
3431 m_aSectionManager.GetWWPageTopMargin(),
3432 m_aSectionManager.GetTextAreaWidth(),
3433 m_nIniFlyDx, m_nIniFlyDy);
3435 // #i45301# - anchor nested table Writer fly frame at-character
3436 eAnchor = RndStdIds::FLY_AT_CHAR;
3440 // if first paragraph in table has break-before-page, transfer that setting to the table itself.
3441 else if( StyleExists(m_nCurrentColl) )
3443 const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat;
3444 if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
3445 NewAttr( pStyleFormat->GetBreak() );
3448 m_xTableDesc.reset(new WW8TabDesc(this, nStartCp));
3450 if( m_xTableDesc->Ok() )
3452 int nNewInTable = m_nInTable + 1;
3454 if ((eAnchor == RndStdIds::FLY_AT_CHAR)
3455 && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
3457 m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
3458 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aItemSet(m_rDoc.GetAttrPool());
3459 // #i33818# - anchor the Writer fly frame for the nested table at-character.
3460 // #i45301#
3461 SwFormatAnchor aAnchor( eAnchor );
3462 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3463 aItemSet.Put( aAnchor );
3464 m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
3465 m_xTableDesc->m_pParentPos, &aItemSet);
3466 OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
3467 "Not the anchor type requested!" );
3468 MoveInsideFly(m_xTableDesc->m_pFlyFormat);
3470 m_xTableDesc->CreateSwTable();
3471 if (m_xTableDesc->m_pFlyFormat)
3473 m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat);
3474 // #i33818# - Use absolute position object attributes,
3475 // if existing, and apply them to the created Writer fly frame.
3476 if ( pTableWFlyPara && pTableSFlyPara )
3478 WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false );
3479 SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
3480 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3481 aFlySet.Put( aAnchor );
3482 m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
3484 else
3486 SwFormatHoriOrient aHori =
3487 m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
3488 m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
3489 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) );
3491 // #i33818# - The nested table doesn't have to leave
3492 // the table cell. Thus, the Writer fly frame has to follow the text flow.
3493 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
3495 else
3496 m_xTableDesc->SetSizePosition(nullptr);
3497 m_xTableDesc->UseSwTable();
3499 else
3500 PopTableDesc();
3502 // #i33818#
3503 delete pTableSFlyPara;
3505 return m_xTableDesc != nullptr;
3508 void SwWW8ImplReader::TabCellEnd()
3510 if (m_nInTable && m_xTableDesc)
3511 m_xTableDesc->TableCellEnd();
3513 m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
3514 m_bReadTable = false;
3517 void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
3519 if( ( nLen > 0 ) && ( *pData == 1 ) )
3520 m_bWasTabCellEnd = true;
3523 void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
3525 if( ( nLen > 0 ) && ( *pData == 1 ) )
3526 m_bWasTabRowEnd = true;
3529 void SwWW8ImplReader::PopTableDesc()
3531 if (m_xTableDesc && m_xTableDesc->m_pFlyFormat)
3533 MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos);
3536 m_xTableDesc.reset();
3537 if (!m_aTableStack.empty())
3539 m_xTableDesc = std::move(m_aTableStack.top());
3540 m_aTableStack.pop();
3544 void SwWW8ImplReader::StopTable()
3546 OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!");
3547 if (!m_xTableDesc)
3548 return;
3550 // We are leaving a table so make sure the next paragraph doesn't think
3551 // it's the first paragraph
3552 m_bFirstPara = false;
3554 m_xTableDesc->FinishSwTable();
3555 PopTableDesc();
3557 m_bReadTable = true;
3560 bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const
3562 if( !m_xTableDesc )
3563 return false;
3565 const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell();
3567 return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() )
3568 || ( pCell
3569 && ( !pCell->bFirstMerged
3570 && ( pCell->bMerged
3571 || ( pCell->bVertMerge
3572 && !pCell->bVertRestart
3579 sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
3581 sal_uInt16 nRes = USHRT_MAX;
3582 if( !m_vColl.empty() )
3584 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3585 if( m_vColl[ nI ].m_bValid
3586 && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
3587 nRes = nI;
3589 return nRes;
3592 const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( std::u16string_view rName ) const
3594 SwFormat* pRet = nullptr;
3595 if( !m_vColl.empty() )
3597 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3598 if( m_vColl[ nI ].m_bValid
3599 && (rName == m_vColl[ nI ].GetOrgWWName()) )
3601 pRet = m_vColl[ nI ].m_pFormat;
3602 break;
3605 return pRet;
3609 SprmResult WW8RStyle::HasParaSprm(sal_uInt16 nId) const
3611 if( !mpParaSprms || !mnSprmsLen )
3612 return SprmResult();
3614 return maSprmParser.findSprmData(nId, mpParaSprms, mnSprmsLen);
3617 void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
3619 if (!nLen)
3620 return;
3622 if( bPap )
3624 mpParaSprms = pSprms; // for HasParaSprms()
3625 mnSprmsLen = nLen;
3628 WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
3629 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
3631 #ifdef DEBUGSPRMREADER
3632 fprintf(stderr, "id is %x\n", aIter.GetCurrentId());
3633 #endif
3634 mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
3635 aSprmIter.advance();
3638 mpParaSprms = nullptr;
3639 mnSprmsLen = 0;
3642 void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
3644 if (!nLen)
3645 return;
3647 if (checkSeek(*mpStStrm, nPosFc))
3649 std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
3650 nLen = mpStStrm->ReadBytes(pSprms.get(), nLen);
3651 ImportSprms(pSprms.get(), nLen, bPap);
3655 static short WW8SkipOdd(SvStream* pSt )
3657 if ( pSt->Tell() & 0x1 )
3659 sal_uInt8 c;
3660 return pSt->ReadBytes( &c, 1 );
3662 return 0;
3665 static short WW8SkipEven(SvStream* pSt )
3667 if (!(pSt->Tell() & 0x1))
3669 sal_uInt8 c;
3670 return pSt->ReadBytes( &c, 1 );
3672 return 0;
3675 short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
3677 if( 0 < nLen ) // Empty ?
3679 if (bOdd)
3680 nLen = nLen - WW8SkipEven( mpStStrm );
3681 else
3682 nLen = nLen - WW8SkipOdd( mpStStrm );
3684 sal_Int16 cbUPX(0);
3685 mpStStrm->ReadInt16( cbUPX );
3687 nLen-=2;
3689 if ( cbUPX > nLen )
3690 cbUPX = nLen; // shrink cbUPX to nLen
3692 if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
3694 if( bPAP )
3696 sal_uInt16 id;
3697 mpStStrm->ReadUInt16( id );
3699 cbUPX-= 2;
3700 nLen-= 2;
3703 if( 0 < cbUPX )
3705 sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong,
3706 // this should make it work again
3707 ImportSprms( nPos, cbUPX, bPAP );
3709 if ( mpStStrm->Tell() != nPos + cbUPX )
3710 mpStStrm->Seek( nPos+cbUPX );
3712 nLen = nLen - cbUPX;
3716 return nLen;
3719 void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
3721 if( nLen <= 0 )
3722 return;
3723 if (bOdd)
3724 nLen = nLen - WW8SkipEven( mpStStrm );
3725 else
3726 nLen = nLen - WW8SkipOdd( mpStStrm );
3728 if( bPara ) // Grupx.Papx
3729 nLen = ImportUPX(nLen, true, bOdd);
3730 ImportUPX(nLen, false, bOdd); // Grupx.Chpx
3733 WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI)
3734 : WW8Style(*pI->m_pTableStream, _rFib)
3735 , maSprmParser(_rFib)
3736 , mpIo(pI)
3737 , mpStStrm(pI->m_pTableStream)
3738 , mpStyRule(nullptr)
3739 , mpParaSprms(nullptr)
3740 , mnSprmsLen(0)
3741 , mnWwNumLevel(0)
3742 , mbTextColChanged(false)
3743 , mbFontChanged(false)
3744 , mbCJKFontChanged(false)
3745 , mbCTLFontChanged(false)
3746 , mbFSizeChanged(false)
3747 , mbFCTLSizeChanged(false)
3748 , mbWidowsChanged(false)
3749 , mbBidiChanged(false)
3751 mpIo->m_vColl.resize(m_cstd);
3754 void WW8RStyle::Set1StyleDefaults()
3756 // see #i25247#, #i25561#, #i48064#, #i92341# for default font
3757 if (!mbCJKFontChanged) // Style no CJK Font? set the default
3758 mpIo->SetNewFontAttr(m_ftcFE, true, RES_CHRATR_CJK_FONT);
3760 if (!mbCTLFontChanged) // Style no CTL Font? set the default
3761 mpIo->SetNewFontAttr(m_ftcBi, true, RES_CHRATR_CTL_FONT);
3763 // western 2nd to make western charset conversion the default
3764 if (!mbFontChanged) // Style has no Font? set the default,
3765 mpIo->SetNewFontAttr(m_ftcAsci, true, RES_CHRATR_FONT);
3767 if( mpIo->m_bNoAttrImport )
3768 return;
3770 // Style has no text color set, winword default is auto
3771 if ( !mbTextColChanged )
3772 mpIo->m_pCurrentColl->SetFormatAttr(SvxColorItem(COL_AUTO, RES_CHRATR_COLOR));
3774 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3775 if( !mbFSizeChanged )
3777 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3778 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3779 aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
3780 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3783 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3784 if( !mbFCTLSizeChanged )
3786 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3787 aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
3788 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3791 if( !mbWidowsChanged ) // Widows ?
3793 mpIo->m_pCurrentColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) );
3794 mpIo->m_pCurrentColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) );
3797 // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of
3798 // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr
3799 if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style
3801 mpIo->m_pCurrentColl->SetFormatAttr(
3802 SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
3806 bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle,
3807 sal_uInt16 nNextStyle,
3808 std::map<OUString, sal_Int32>& rParaCollisions,
3809 std::map<OUString, sal_Int32>& rCharCollisions)
3811 SwFormat* pColl;
3812 bool bStyExist;
3814 if (rSI.m_bColl)
3816 // Para-Style
3817 sw::util::ParaStyleMapper::StyleResult aResult =
3818 mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rParaCollisions);
3819 pColl = aResult.first;
3820 bStyExist = aResult.second;
3822 else
3824 // Char-Style
3825 sw::util::CharStyleMapper::StyleResult aResult =
3826 mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rCharCollisions);
3827 pColl = aResult.first;
3828 bStyExist = aResult.second;
3831 bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ?
3833 // Do not override character styles the list import code created earlier.
3834 if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
3835 bImport = false;
3837 bool bOldNoImp = mpIo->m_bNoAttrImport;
3838 rSI.m_bImportSkipped = !bImport;
3840 if( !bImport )
3841 mpIo->m_bNoAttrImport = true;
3842 else
3844 if (bStyExist)
3846 pColl->ResetAllFormatAttr(); // #i73790# - method renamed
3848 pColl->SetAuto(false); // suggested by JP
3849 } // but changes the UI
3850 mpIo->m_pCurrentColl = pColl;
3851 rSI.m_pFormat = pColl; // remember translation WW->SW
3852 rSI.m_bImportSkipped = !bImport;
3854 // Set Based on style
3855 sal_uInt16 j = rSI.m_nBase;
3856 if (j != nThisStyle && j < m_cstd )
3858 SwWW8StyInf* pj = &mpIo->m_vColl[j];
3859 if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
3861 rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
3862 rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet;
3863 rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet;
3864 rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet;
3865 rSI.m_n81Flags = pj->m_n81Flags;
3866 rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags;
3867 if (!rSI.IsWW8BuiltInHeadingStyle())
3869 rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel;
3871 rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore;
3872 rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter;
3874 if (pj->m_xWWFly)
3875 rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get());
3878 else if( mpIo->m_bNewDoc && bStyExist )
3879 rSI.m_pFormat->SetDerivedFrom();
3881 rSI.m_nFollow = nNextStyle; // remember Follow
3883 mpStyRule = nullptr; // recreate if necessary
3884 mbTextColChanged = mbFontChanged = mbCJKFontChanged = mbCTLFontChanged =
3885 mbFSizeChanged = mbFCTLSizeChanged = mbWidowsChanged = false;
3886 mpIo->SetNCurrentColl( nThisStyle );
3887 mpIo->m_bStyNormal = nThisStyle == 0;
3888 return bOldNoImp;
3891 void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp)
3893 // Reset attribute flags, because there are no style-ends.
3895 mpIo->m_bHasBorder = mpIo->m_bSpec = mpIo->m_bObj = mpIo->m_bSymbol = false;
3896 mpIo->m_nCharFormat = -1;
3898 // if style is based on nothing or base ignored
3899 if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
3901 // If Char-Styles does not work
3902 // -> set hard WW-Defaults
3903 Set1StyleDefaults();
3906 mpStyRule = nullptr; // to be on the safe side
3907 mpIo->m_bStyNormal = false;
3908 mpIo->SetNCurrentColl( 0 );
3909 mpIo->m_bNoAttrImport = bOldNoImp;
3910 // reset the list-remember-fields, if used when reading styles
3911 mpIo->m_nLFOPosition = USHRT_MAX;
3912 mpIo->m_nListLevel = MAXLEVEL;
3915 void WW8RStyle::Import1Style(sal_uInt16 nNr,
3916 std::map<OUString, sal_Int32>& rParaCollisions,
3917 std::map<OUString, sal_Int32>& rCharCollisions)
3919 if (nNr >= mpIo->m_vColl.size())
3920 return;
3922 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3924 if( rSI.m_bImported || !rSI.m_bValid )
3925 return;
3927 rSI.m_bImported = true; // set flag now to avoid endless loops
3929 // valid and not NUL and not yet imported
3931 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3932 Import1Style(rSI.m_nBase, rParaCollisions, rCharCollisions);
3934 mpStStrm->Seek( rSI.m_nFilePos );
3936 sal_uInt16 nSkip;
3937 OUString sName;
3939 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName)); // read Style
3941 if (xStd)
3942 rSI.SetOrgWWIdent( sName, xStd->sti );
3944 // either no Name or unused Slot or unknown Style
3946 if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
3948 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
3949 mpStStrm->Seek(mpStStrm->Tell() + nSkip);
3950 return;
3953 bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti),
3954 nNr, xStd->istdNext,
3955 rParaCollisions, rCharCollisions);
3957 // if something is interpreted wrong, this should make it work again
3958 sal_uInt64 nPos = mpStStrm->Tell();
3960 //Variable parts of the STD start at even byte offsets, but "inside
3961 //the STD", which I take to meaning even in relation to the starting
3962 //position of the STD, which matches findings in #89439#, generally it
3963 //doesn't matter as the STSHI starts off nearly always on an even
3964 //offset
3966 //Import of the Style Contents
3967 ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
3969 PostStyle(rSI, bOldNoImp);
3971 mpStStrm->Seek( nPos+nSkip );
3974 void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
3976 if (nNr >= mpIo->m_vColl.size())
3977 return;
3979 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3980 if( rSI.m_bImported || !rSI.m_bValid )
3981 return;
3983 rSI.m_bImported = true;
3985 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3986 RecursiveReg(rSI.m_nBase);
3988 mpIo->RegisterNumFormatOnStyle(nNr);
3993 After all styles are imported then we can recursively apply numbering
3994 styles to them, and change their tab stop settings if they turned out
3995 to have special first line indentation.
3997 void WW8RStyle::PostProcessStyles()
3999 sal_uInt16 i;
4001 Clear all imported flags so that we can recursively apply numbering
4002 formats and use it to mark handled ones
4004 for (i=0; i < m_cstd; ++i)
4005 mpIo->m_vColl[i].m_bImported = false;
4008 Register the num formats and tabstop changes on the styles recursively.
4012 In the same loop apply the tabstop changes required because we need to
4013 change their location if there's a special indentation for the first line,
4014 By avoiding making use of each styles margins during reading of their
4015 tabstops we don't get problems with doubly adjusting tabstops that
4016 are inheritied.
4018 for (i=0; i < m_cstd; ++i)
4020 if (mpIo->m_vColl[i].m_bValid)
4022 RecursiveReg(i);
4027 void WW8RStyle::ScanStyles() // investigate style dependencies
4028 { // and detect Filepos for each Style
4029 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4031 SwWW8StyInf &rSI = mpIo->m_vColl[i];
4033 rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos
4034 sal_uInt16 nSkip;
4035 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD
4036 rSI.m_bValid = xStd != nullptr;
4037 if (rSI.m_bValid)
4039 rSI.m_nBase = xStd->istdBase; // remember Basis
4040 rSI.m_bColl = xStd->sgc == 1; // Para-Style
4042 else
4043 rSI = SwWW8StyInf();
4045 xStd.reset();
4046 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
4047 mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms
4051 std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
4053 std::vector<sal_uInt8> aRet
4056 static_cast< sal_uInt8 >(128 + rChpx.fBold),
4059 static_cast< sal_uInt8 >(128 + rChpx.fItalic),
4062 static_cast< sal_uInt8 >(128 + rChpx.fStrike),
4065 static_cast< sal_uInt8 >(128 + rChpx.fOutline),
4068 static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps),
4071 static_cast< sal_uInt8 >(128 + rChpx.fCaps),
4074 static_cast< sal_uInt8 >(128 + rChpx.fVanish)
4076 if (rChpx.fsFtc)
4078 aRet.push_back(68);
4079 SVBT16 a;
4080 ShortToSVBT16(rChpx.ftc, a);
4081 aRet.push_back(a[1]);
4082 aRet.push_back(a[0]);
4085 if (rChpx.fsKul)
4087 aRet.push_back(69);
4088 aRet.push_back(rChpx.kul);
4091 if (rChpx.fsLid)
4093 aRet.push_back(72);
4094 SVBT16 a;
4095 ShortToSVBT16(rChpx.lid, a);
4096 aRet.push_back(a[1]);
4097 aRet.push_back(a[0]);
4100 if (rChpx.fsIco)
4102 aRet.push_back(73);
4103 aRet.push_back(rChpx.ico);
4106 if (rChpx.fsHps)
4108 aRet.push_back(74);
4110 SVBT16 a;
4111 ShortToSVBT16(rChpx.hps, a);
4112 aRet.push_back(a[0]);
4115 if (rChpx.fsPos)
4117 aRet.push_back(76);
4118 aRet.push_back(rChpx.hpsPos);
4121 aRet.push_back(80);
4122 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
4124 aRet.push_back(81);
4125 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
4127 if (rChpx.fsFtcBi)
4129 aRet.push_back(82);
4130 SVBT16 a;
4131 ShortToSVBT16(rChpx.fsFtcBi, a);
4132 aRet.push_back(a[1]);
4133 aRet.push_back(a[0]);
4136 if (rChpx.fsLidBi)
4138 aRet.push_back(83);
4139 SVBT16 a;
4140 ShortToSVBT16(rChpx.lidBi, a);
4141 aRet.push_back(a[1]);
4142 aRet.push_back(a[0]);
4145 if (rChpx.fsIcoBi)
4147 aRet.push_back(84);
4148 aRet.push_back(rChpx.icoBi);
4151 if (rChpx.fsHpsBi)
4153 aRet.push_back(85);
4154 SVBT16 a;
4155 ShortToSVBT16(rChpx.hpsBi, a);
4156 aRet.push_back(a[1]);
4157 aRet.push_back(a[0]);
4160 return aRet;
4163 Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
4165 Word2CHPX aChpx;
4167 if (!nSize || !checkSeek(rSt, nOffset))
4168 return aChpx;
4170 const size_t nMaxByteCount = rSt.remainingSize();
4171 if (!nMaxByteCount)
4172 return aChpx;
4174 if (nSize > nMaxByteCount)
4176 SAL_WARN("sw.ww8", "ReadWord2Chpx: truncating out of range "
4177 << nSize << " to " << nMaxByteCount);
4178 nSize = nMaxByteCount;
4181 sal_uInt8 nCount=0;
4183 while (true)
4185 sal_uInt8 nFlags8;
4186 rSt.ReadUChar( nFlags8 );
4187 nCount++;
4189 if (!rSt.good())
4190 break;
4192 aChpx.fBold = nFlags8 & 0x01;
4193 aChpx.fItalic = (nFlags8 & 0x02) >> 1;
4194 aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
4195 aChpx.fOutline = (nFlags8 & 0x08) >> 3;
4196 aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
4197 aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
4198 aChpx.fCaps = (nFlags8 & 0x40) >> 6;
4199 aChpx.fVanish = (nFlags8 & 0x80) >> 7;
4201 if (nCount >= nSize) break;
4202 rSt.ReadUChar( nFlags8 );
4203 nCount++;
4205 if (!rSt.good())
4206 break;
4208 aChpx.fRMark = nFlags8 & 0x01;
4209 aChpx.fSpec = (nFlags8 & 0x02) >> 1;
4210 aChpx.fStrike = (nFlags8 & 0x04) >> 2;
4211 aChpx.fObj = (nFlags8 & 0x08) >> 3;
4212 aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
4213 aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
4214 aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
4215 aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
4217 if (nCount >= nSize) break;
4218 rSt.ReadUChar( nFlags8 );
4219 nCount++;
4221 if (!rSt.good())
4222 break;
4224 aChpx.fsIco = nFlags8 & 0x01;
4225 aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
4226 aChpx.fsHps = (nFlags8 & 0x04) >> 2;
4227 aChpx.fsKul = (nFlags8 & 0x08) >> 3;
4228 aChpx.fsPos = (nFlags8 & 0x10) >> 4;
4229 aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
4230 aChpx.fsLid = (nFlags8 & 0x40) >> 6;
4231 aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
4233 if (nCount >= nSize) break;
4234 rSt.ReadUChar( nFlags8 );
4235 nCount++;
4237 if (!rSt.good())
4238 break;
4240 aChpx.fsFtcBi = nFlags8 & 0x01;
4241 aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
4242 aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
4244 if (nCount >= nSize) break;
4245 rSt.ReadUInt16( aChpx.ftc );
4246 nCount+=2;
4248 if (nCount >= nSize) break;
4249 rSt.ReadUInt16( aChpx.hps );
4250 nCount+=2;
4252 if (nCount >= nSize) break;
4253 rSt.ReadUChar( nFlags8 );
4254 nCount++;
4256 if (!rSt.good())
4257 break;
4259 aChpx.qpsSpace = nFlags8 & 0x3F;
4260 aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
4261 aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
4263 if (nCount >= nSize) break;
4264 rSt.ReadUChar( nFlags8 );
4265 nCount++;
4267 if (!rSt.good())
4268 break;
4270 aChpx.ico = nFlags8 & 0x1F;
4271 aChpx.kul = (nFlags8 & 0xE0) >> 5;
4273 if (nCount >= nSize) break;
4274 rSt.ReadUChar( aChpx.hpsPos );
4275 nCount++;
4277 if (nCount >= nSize) break;
4278 rSt.ReadUChar( aChpx.icoBi );
4279 nCount++;
4281 if (nCount >= nSize) break;
4282 rSt.ReadUInt16( aChpx.lid );
4283 nCount+=2;
4285 if (nCount >= nSize) break;
4286 rSt.ReadUInt16( aChpx.ftcBi );
4287 nCount+=2;
4289 if (nCount >= nSize) break;
4290 rSt.ReadUInt16( aChpx.hpsBi );
4291 nCount+=2;
4293 if (nCount >= nSize) break;
4294 rSt.ReadUInt16( aChpx.lidBi );
4295 nCount+=2;
4297 if (nCount >= nSize) break;
4298 rSt.ReadUInt32( aChpx.fcPic );
4299 nCount+=4;
4301 break;
4304 rSt.SeekRel(nSize-nCount);
4305 return aChpx;
4308 namespace
4310 struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
4313 void WW8RStyle::ImportOldFormatStyles()
4315 for (sal_uInt16 i=0; i < m_cstd; ++i)
4317 mpIo->m_vColl[i].m_bColl = true;
4318 //every chain must end eventually at the null style (style code 222)
4319 mpIo->m_vColl[i].m_nBase = 222;
4322 rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
4323 mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid);
4325 sal_uInt16 cstcStd(0);
4326 m_rStream.ReadUInt16( cstcStd );
4328 size_t nMaxByteCount = m_rStream.remainingSize();
4329 sal_uInt16 cbName(0);
4330 m_rStream.ReadUInt16(cbName);
4331 if (cbName > nMaxByteCount)
4333 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4334 << cbName << " to " << nMaxByteCount);
4335 cbName = nMaxByteCount;
4337 sal_uInt16 nByteCount = 2;
4338 sal_uInt16 stcp=0;
4339 while (nByteCount < cbName)
4341 sal_uInt8 nCount(0);
4342 m_rStream.ReadUChar( nCount );
4343 nByteCount++;
4345 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4346 if (stc >=mpIo->m_vColl.size())
4347 continue;
4349 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4350 OUString sName;
4352 if (nCount != 0xFF) // undefined style
4354 if (nCount != 0) // user style
4356 OString aTmp = read_uInt8s_ToOString(m_rStream, nCount);
4357 nByteCount += aTmp.getLength();
4358 sName = OStringToOUString(aTmp, eStructChrSet);
4360 rSI.m_bImported = true;
4363 if (sName.isEmpty())
4365 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4366 if (const char *pStr = GetEnglishNameFromSti(eSti))
4367 sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
4370 if (sName.isEmpty())
4371 sName = "Unknown Style: " + OUString::number(stc);
4373 rSI.SetOrgWWIdent(sName, stc);
4374 stcp++;
4377 sal_uInt16 nStyles=stcp;
4379 std::vector<pxoffset> aCHPXOffsets(stcp);
4380 nMaxByteCount = m_rStream.remainingSize();
4381 sal_uInt16 cbChpx(0);
4382 m_rStream.ReadUInt16(cbChpx);
4383 if (cbChpx > nMaxByteCount)
4385 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4386 << cbChpx << " to " << nMaxByteCount);
4387 cbChpx = nMaxByteCount;
4389 nByteCount = 2;
4390 stcp=0;
4391 std::vector< std::vector<sal_uInt8> > aConvertedChpx;
4392 while (nByteCount < cbChpx)
4394 if (stcp == aCHPXOffsets.size())
4396 //more data than style slots, skip remainder
4397 m_rStream.SeekRel(cbChpx-nByteCount);
4398 break;
4401 sal_uInt8 cb(0);
4402 m_rStream.ReadUChar( cb );
4403 nByteCount++;
4405 aCHPXOffsets[stcp].mnSize = 0;
4407 if (cb != 0xFF)
4409 sal_uInt8 nRemainder = cb;
4411 aCHPXOffsets[stcp].mnOffset = m_rStream.Tell();
4412 aCHPXOffsets[stcp].mnSize = nRemainder;
4414 Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset,
4415 aCHPXOffsets[stcp].mnSize);
4416 aConvertedChpx.push_back( ChpxToSprms(aChpx) );
4418 nByteCount += nRemainder;
4420 else
4421 aConvertedChpx.emplace_back( );
4423 ++stcp;
4426 std::vector<pxoffset> aPAPXOffsets(stcp);
4427 nMaxByteCount = m_rStream.remainingSize();
4428 sal_uInt16 cbPapx(0);
4429 m_rStream.ReadUInt16(cbPapx);
4430 if (cbPapx > nMaxByteCount)
4432 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4433 << cbPapx << " to " << nMaxByteCount);
4434 cbPapx = nMaxByteCount;
4436 nByteCount = 2;
4437 stcp=0;
4438 while (nByteCount < cbPapx)
4440 if (stcp == aPAPXOffsets.size())
4442 m_rStream.SeekRel(cbPapx-nByteCount);
4443 break;
4446 sal_uInt8 cb(0);
4447 m_rStream.ReadUChar( cb );
4448 nByteCount++;
4450 aPAPXOffsets[stcp].mnSize = 0;
4452 if (cb != 0xFF)
4454 sal_uInt8 stc2(0);
4455 m_rStream.ReadUChar( stc2 );
4456 m_rStream.SeekRel(6);
4457 nByteCount+=7;
4458 sal_uInt8 nRemainder = cb-7;
4460 aPAPXOffsets[stcp].mnOffset = m_rStream.Tell();
4461 aPAPXOffsets[stcp].mnSize = nRemainder;
4463 m_rStream.SeekRel(nRemainder);
4464 nByteCount += nRemainder;
4467 ++stcp;
4470 sal_uInt16 iMac(0);
4471 m_rStream.ReadUInt16( iMac );
4473 if (iMac > nStyles) iMac = nStyles;
4475 std::map<OUString, sal_Int32> aParaCollisions;
4476 std::map<OUString, sal_Int32> aCharCollisions;
4478 for (stcp = 0; stcp < iMac; ++stcp)
4480 sal_uInt8 stcNext(0), stcBase(0);
4481 m_rStream.ReadUChar( stcNext );
4482 m_rStream.ReadUChar( stcBase );
4484 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4487 #i64557# style based on itself
4488 every chain must end eventually at the null style (style code 222)
4490 if (stc == stcBase)
4491 stcBase = 222;
4493 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4494 rSI.m_nBase = stcBase;
4496 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4498 if (eSti == ww::stiNil)
4499 continue;
4501 if (stcp >= aPAPXOffsets.size())
4502 continue;
4504 rSI.m_bValid = true;
4506 if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
4507 mpIo->m_vColl[stc].m_bColl = false;
4509 bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext,
4510 aParaCollisions,
4511 aCharCollisions);
4513 ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
4514 true);
4516 if (!aConvertedChpx[stcp].empty())
4517 ImportSprms(aConvertedChpx[stcp].data(),
4518 static_cast< short >(aConvertedChpx[stcp].size()),
4519 false);
4521 PostStyle(rSI, bOldNoImp);
4525 void WW8RStyle::ImportNewFormatStyles()
4527 ScanStyles(); // Scan Based On
4529 std::map<OUString, sal_Int32> aParaCollisions;
4530 std::map<OUString, sal_Int32> aCharCollisions;
4532 for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles
4533 if (mpIo->m_vColl[i].m_bValid)
4534 Import1Style(i, aParaCollisions, aCharCollisions);
4537 void WW8RStyle::Import()
4539 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4540 mpIo->m_pStandardFormatColl =
4541 mpIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
4543 if( mpIo->m_nIniFlags & WW8FL_NO_STYLES )
4544 return;
4546 if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2)
4547 ImportOldFormatStyles();
4548 else
4549 ImportNewFormatStyles();
4551 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4553 // Follow chain
4554 SwWW8StyInf* pi = &mpIo->m_vColl[i];
4555 sal_uInt16 j = pi->m_nFollow;
4556 if( j < m_cstd )
4558 SwWW8StyInf* pj = &mpIo->m_vColl[j];
4559 if ( j != i // rational Index ?
4560 && pi->m_pFormat // Format ok ?
4561 && pj->m_pFormat // Derived-Format ok ?
4562 && pi->m_bColl // only possible for paragraph templates (WW)
4563 && pj->m_bColl ){ // identical Type ?
4564 static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
4565 *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
4570 // Missing special handling for default character template
4571 // "Absatz-Standardschriftart" ( Style-ID 65 ).
4572 // That is empty by default ( WW6 dt and US ) and not changeable
4573 // via WW-UI so this does not matter.
4574 // This could be done by:
4575 // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
4577 // for e.g. tables an always valid Std-Style is necessary
4579 if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() &&
4580 mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid )
4581 mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat);
4582 else
4583 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4585 // set Hyphenation flag on BASIC para-style
4586 if (mpIo->m_bNewDoc && mpIo->m_pStandardFormatColl)
4588 if (mpIo->m_xWDop->fAutoHyphen
4589 && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState(
4590 RES_PARATR_HYPHENZONE, false) )
4592 SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE);
4593 aAttr.GetMinLead() = 2;
4594 aAttr.GetMinTrail() = 2;
4595 aAttr.GetMaxHyphens() = 0;
4597 mpIo->m_pStandardFormatColl->SetFormatAttr( aAttr );
4601 // we do not read styles anymore:
4602 mpIo->m_pCurrentColl = nullptr;
4605 rtl_TextEncoding SwWW8StyInf::GetCharSet() const
4607 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4608 return m_eRTLFontSrcCharSet;
4609 return m_eLTRFontSrcCharSet;
4612 rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
4614 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4615 return m_eRTLFontSrcCharSet;
4616 return m_eCJKFontSrcCharSet;
4619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */