tdf#144668: docx export: simpler way to create lvlText in numberings
[LibreOffice.git] / sw / source / filter / ww8 / ww8par2.cxx
blob7cf96ade0bc2257714453ecb7fc39d553a274099
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 <doc.hxx>
43 #include <IDocumentStylePoolAccess.hxx>
44 #include <docary.hxx>
45 #include <ndtxt.hxx>
46 #include <paratr.hxx>
47 #include <poolfmt.hxx>
48 #include <swtable.hxx>
49 #include <tblsel.hxx>
50 #include <mdiexp.hxx>
51 #include <txtftn.hxx>
52 #include <frmfmt.hxx>
53 #include <ftnidx.hxx>
54 #include <fmtftn.hxx>
55 #include <fmtlsplt.hxx>
56 #include <charfmt.hxx>
57 #include <fmtanchr.hxx>
58 #include <fmtrowsplt.hxx>
59 #include <fmtfollowtextflow.hxx>
60 #include <numrule.hxx>
61 #include "sprmids.hxx"
62 #include <wwstyles.hxx>
63 #include "writerhelper.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().nPgLeft : 0;
150 sal_uInt32 wwSectionManager::GetPageRight() const
152 return !maSegments.empty() ? maSegments.back().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 namespace
172 class DeleteListener final : public SvtListener
174 private:
175 bool bObjectDeleted;
176 public:
177 explicit DeleteListener(SvtBroadcaster& rNotifier)
178 : bObjectDeleted(false)
180 StartListening(rNotifier);
183 virtual void Notify(const SfxHint& rHint) override
185 if (rHint.GetId() == SfxHintId::Dying)
186 bObjectDeleted = true;
189 bool WasDeleted() const
191 return bObjectDeleted;
196 sal_uInt16 SwWW8ImplReader::End_Footnote()
199 Ignoring Footnote outside of the normal Text. People will put footnotes
200 into field results and field commands.
202 if (m_bIgnoreText ||
203 m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex())
205 return 0;
208 OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
209 if (m_aFootnoteStack.empty())
210 return 0;
212 bool bFtEdOk = false;
213 const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
215 //Get the footnote character and remove it from the txtnode. We'll
216 //replace it with the footnote
217 SwTextNode* pText = m_pPaM->GetNode().GetTextNode();
218 sal_Int32 nPos = m_pPaM->GetPoint()->nContent.GetIndex();
220 OUString sChar;
221 SwTextFootnote* pFN = nullptr;
222 //There should have been a footnote char, we will replace this.
223 if (pText && nPos)
225 sChar += OUStringChar(pText->GetText()[--nPos]);
226 m_pPaM->SetMark();
227 --m_pPaM->GetMark()->nContent;
228 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_pLastAnchorPos) : nullptr);
229 m_pLastAnchorPos.reset();
230 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
231 m_pPaM->DeleteMark();
232 if (xLastAnchorCursor)
233 m_pLastAnchorPos.reset(new SwPosition(*xLastAnchorCursor->GetPoint()));
234 SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
235 pFN = static_cast<SwTextFootnote*>(pText->InsertItem(aFootnote, nPos, nPos));
237 OSL_ENSURE(pFN, "Problems creating the footnote text");
238 if (pFN)
240 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
241 WW8PLCFxSaveAll aSave;
242 m_xPlcxMan->SaveAllPLCFx( aSave );
243 std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan;
245 const SwNodeIndex* pSttIdx = pFN->GetStartNode();
246 assert(pSttIdx && "Problems creating footnote text");
248 pFN->SetSeqNo(m_rDoc.GetFootnoteIdxs().size());
250 bool bOld = m_bFootnoteEdn;
251 m_bFootnoteEdn = true;
253 SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr());
255 DeleteListener aDeleteListener(rFormatFootnote.GetNotifier());
257 // read content of Ft-/End-Note
258 Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
260 m_bFootnoteEdn = bOld;
262 SAL_WARN_IF(aDeleteListener.WasDeleted(), "sw.ww8", "Footnode deleted during its import");
263 if (!aDeleteListener.WasDeleted())
265 bFtEdOk = true;
267 OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
268 "footnote autonumbering must be 0x02, and everything else must not be");
270 // If no automatic numbering use the following char from the main text
271 // as the footnote number
272 if (!rDesc.mbAutoNum)
273 pFN->SetNumber(0, 0, sChar);
276 Delete the footnote char from the footnote if its at the beginning
277 as usual. Might not be if the user has already deleted it, e.g.
278 #i14737#
280 SwNodeIndex& rNIdx = m_pPaM->GetPoint()->nNode;
281 rNIdx = pSttIdx->GetIndex() + 1;
282 SwTextNode* pTNd = rNIdx.GetNode().GetTextNode();
283 if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
285 const OUString &rText = pTNd->GetText();
286 if (rText[0] == sChar[0])
288 // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
289 sal_Int32 nFirstLineIndent=0;
290 SfxItemSetFixed<RES_LR_SPACE, RES_LR_SPACE> aSet( m_rDoc.GetAttrPool() );
291 if ( pTNd->GetAttr(aSet) )
293 const SvxLRSpaceItem* pLRSpace = aSet.GetItem<SvxLRSpaceItem>(RES_LR_SPACE);
294 if ( pLRSpace )
295 nFirstLineIndent = pLRSpace->GetTextFirstLineOffset();
298 m_pPaM->GetPoint()->nContent.Assign( pTNd, 0 );
299 m_pPaM->SetMark();
300 // Strip out aesthetic tabs we may have inserted on export #i24762#
301 if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09)
302 ++m_pPaM->GetMark()->nContent;
303 ++m_pPaM->GetMark()->nContent;
304 m_xReffingStck->Delete(*m_pPaM);
305 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
306 m_pPaM->DeleteMark();
311 *m_pPaM->GetPoint() = aTmpPos; // restore Cursor
313 m_xPlcxMan = xOldPlcxMan; // Restore attributes
314 m_xPlcxMan->RestoreAllPLCFx( aSave );
317 if (bFtEdOk)
318 m_aSectionManager.SetCurrentSectionHasFootnote();
320 m_aFootnoteStack.pop_back();
321 return 0;
324 tools::Long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes)
327 Ignoring Footnote outside of the normal Text. People will put footnotes
328 into field results and field commands.
330 if (m_bIgnoreText ||
331 m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex())
333 return 0;
336 FootnoteDescriptor aDesc;
337 aDesc.mbAutoNum = true;
338 if (eEDN == pRes->nSprmId)
340 aDesc.meType = MAN_EDN;
341 WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn();
342 if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
343 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
345 else
347 aDesc.meType = MAN_FTN;
348 WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote();
349 if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
350 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
353 aDesc.mnStartCp = pRes->nCp2OrIdx;
354 aDesc.mnLen = pRes->nMemLen;
356 m_aFootnoteStack.push_back(aDesc);
358 return 0;
361 bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp,
362 int nLevel) const
364 WW8PLCFxDesc aRes;
365 aRes.pMemPos = nullptr;
366 aRes.nEndPos = rStartCp;
367 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
369 while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
371 if (pPap->Where() != WW8_CP_MAX)
373 SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel));
374 const sal_uInt8* pB = aSprmRes.pSprm;
375 if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1)
377 aSprmRes = pPap->HasSprm(0x6649);
378 const sal_uInt8 *pLevel = aSprmRes.pSprm;
379 if (pLevel && aSprmRes.nRemainingData >= 1)
381 if (nLevel + 1 == *pLevel)
382 return true;
384 else
386 OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
387 return true; // RowEnd found
392 aRes.nStartPos = aRes.nEndPos;
393 aRes.pMemPos = nullptr;
394 //Seek to our next block of properties
395 if (!(pPap->SeekPos(aRes.nStartPos)))
397 aRes.nEndPos = WW8_CP_MAX;
398 pPap->SetDirty(true);
400 pPap->GetSprms(&aRes);
401 pPap->SetDirty(false);
402 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
403 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
405 SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
406 break;
408 //Update our aRes to get the new starting point of the next properties
409 rStartCp = aRes.nEndPos;
412 return false;
415 bool SwWW8ImplReader::SearchTableEnd(WW8PLCFx_Cp_FKP* pPap) const
417 if (m_bVer67)
418 // The below SPRM is for WW8 only.
419 return false;
421 WW8PLCFxDesc aRes;
422 aRes.pMemPos = nullptr;
423 aRes.nEndPos = pPap->Where();
424 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
426 while (pPap->HasFkp() && pPap->Where() != WW8_CP_MAX)
428 // See if the current pap is outside the table.
429 SprmResult aSprmRes = pPap->HasSprm(NS_sprm::PFInTable::val);
430 const sal_uInt8* pB = aSprmRes.pSprm;
431 if (!pB || aSprmRes.nRemainingData < 1 || *pB != 1)
432 // Yes, this is the position after the end of the table.
433 return true;
435 // It is, so seek to the next pap.
436 aRes.nStartPos = aRes.nEndPos;
437 aRes.pMemPos = nullptr;
438 if (!pPap->SeekPos(aRes.nStartPos))
439 return false;
441 // Read the sprms and make sure we moved forward to avoid infinite loops.
442 pPap->GetSprms(&aRes);
443 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
444 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
446 SAL_WARN("sw.ww8", "SearchTableEnd, loop in paragraph property chain");
447 break;
451 return false;
454 ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
455 const WW8_TablePos *pTabPos)
457 const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
458 ApoTestResults aRet;
459 // Frame in Style Definition (word appears to ignore them if inside an
460 // text autoshape)
461 sal_uInt16 const nStyle(m_xPlcxMan->GetColl());
462 if (!m_bTxbxFlySection && nStyle < m_vColl.size())
463 aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
466 #i1140#
467 If I have a table and apply a style to one of its frames that should cause
468 a paragraph that it is applied to it to only exist as a separate floating
469 frame, then the behaviour depends on which cell that it has been applied
470 to. If it is the first cell of a row then the whole table row jumps into the
471 new frame, if it isn't then the paragraph attributes are applied
472 "except" for the floating frame stuff. i.e. it's ignored. So if there's a
473 table, and we're not in the first cell then we ignore the fact that the
474 paragraph style wants to be in a different frame.
476 This sort of mindbending inconsistency is surely why frames are deprecated
477 in word 97 onwards and hidden away from the user
479 #i1532# & #i5379#
480 If we are already a table in a frame then we must grab the para properties
481 to see if we are still in that frame.
484 aRet.m_bHasSprm37 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr;
485 SprmResult aSrpm29 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B);
486 const sal_uInt8 *pSrpm29 = aSrpm29.pSprm;
487 aRet.m_bHasSprm29 = pSrpm29 != nullptr;
488 aRet.m_nSprm29 = (pSrpm29 && aSrpm29.nRemainingData >= 1) ? *pSrpm29 : 0;
490 // Is there some frame data here
491 bool bNowApo = aRet.HasFrame() || pTopLevelTable;
492 if (bNowApo)
494 if (!ConstructApo(aRet, pTabPos))
495 bNowApo = false;
498 bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
499 if (bTestAllowed)
501 //Test is allowed if there is no table.
502 //Otherwise only allowed if we are in the
503 //first paragraph of the first cell of a row.
504 //(And only if the row we are inside is at the
505 //same level as the previous row, think tables
506 //in tables)
507 if (nCellLevel == m_nInTable)
510 if (!m_nInTable)
511 bTestAllowed = true;
512 else
514 if (!m_xTableDesc)
516 OSL_ENSURE(m_xTableDesc, "What!");
517 bTestAllowed = false;
519 else
521 // #i39468#
522 // If current cell isn't valid, the test is allowed.
523 // The cell isn't valid, if e.g. there is a new row
524 // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()>
525 bTestAllowed =
526 m_xTableDesc->GetCurrentCol() == 0 &&
527 ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) ||
528 m_xTableDesc->InFirstParaInCell() );
534 if (!bTestAllowed)
535 return aRet;
537 aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
538 aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
540 //If it happens that we are in a table, then if it's not the first cell
541 //then any attributes that might otherwise cause the contents to jump
542 //into another frame don't matter, a table row sticks together as one
543 //unit no matter what else happens. So if we are not in a table at
544 //all, or if we are in the first cell then test that the last frame
545 //data is the same as the current one
546 if (bNowApo && InEqualApo(nCellLevel))
548 // two bordering each other
549 if (!TestSameApo(aRet, pTabPos))
550 aRet.mbStopApo = aRet.mbStartApo = true;
553 return aRet;
556 // helper methods for outline, numbering and bullets
558 static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
560 static const SvxNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
561 SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC,
562 SVX_NUM_ARABIC, SVX_NUM_ARABIC };
564 static const SvxAdjust eAdjA[4] = { SvxAdjust::Left,
565 SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left };
566 if (rAV.nfc < 8) {
567 rNum.SetNumberingType( eNumA[ rAV.nfc ] );
568 } else {
569 SvxNumType nType = SVX_NUM_ARABIC;
570 switch( rAV.nfc ) {
571 case 14:
572 case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
573 case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
574 case 31:nType = SVX_NUM_DI_ZI_ZH; break;
575 case 35:
576 case 36:
577 case 37:
578 case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
579 case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
580 case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
581 case 10:
582 case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
583 case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
584 case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
585 case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
586 case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
587 case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
588 case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
589 case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
590 //case 42:
591 //case 43:
592 case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
593 default:
594 nType= SVX_NUM_ARABIC;break;
596 rNum.SetNumberingType( nType );
599 if ((rAV.aBits1 & 0x4) >> 2)
601 rNum.SetIncludeUpperLevels(nSwLevel + 1);
603 rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) );
604 rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
606 rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) );
607 sal_Int16 nIndent = std::abs(static_cast<sal_Int16>(SVBT16ToUInt16( rAV.dxaIndent )));
608 if( rAV.aBits1 & 0x08 ) //fHang
610 rNum.SetFirstLineOffset( -nIndent );
611 rNum.SetAbsLSpace( nIndent );
613 else
614 rNum.SetCharTextDistance( nIndent ); // width of number is missing
616 if( rAV.nfc == 5 || rAV.nfc == 7 )
618 OUString sP = "." + rNum.GetSuffix();
619 rNum.SetSuffix( sP ); // ordinal number
623 void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV,
624 const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
626 if (nStart > nElements)
627 return;
629 pText += nStart;
630 nElements -= nStart;
632 bool bInsert = false; // Default
633 rtl_TextEncoding eCharSet = m_eStructCharSet;
635 const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo
636 bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/...
638 OUStringBuffer sText;
639 sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
640 if (m_bVer67)
642 if (nLen > nElements)
644 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
645 << nLen << " vs " << nElements << " max");
646 return;
648 sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
649 // ofz#23961 in case of multi-byte input encoding resulting in shorter
650 // output pad to full length with something semi-arbitrary
651 comphelper::string::padToLength(sText, nLen, cBulletChar);
653 else
655 if (nLen > nElements / 2)
657 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
658 << nLen << " vs " << nElements / 2 << " max");
659 return;
661 for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
663 sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText))));
667 if( bOutline )
668 { // outline
669 if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
670 || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
672 // if self defined digits
673 bInsert = true; // then apply character
675 // replace by simple Bullet ?
676 if( bListSymbol )
678 // use cBulletChar for correct mapping on MAC
679 OUStringBuffer aBuf;
680 comphelper::string::padToLength(aBuf, rAV.cbTextBefore
681 + rAV.cbTextAfter, cBulletChar);
682 sText = aBuf;
686 else
687 { // numbering / bullets
688 bInsert = true;
689 if( bListSymbol )
691 FontFamily eFamily;
692 OUString aName;
693 FontPitch ePitch;
695 if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName,
696 ePitch, eCharSet ) ){
698 vcl::Font aFont;
699 aFont.SetFamilyName( aName );
700 aFont.SetFamily( eFamily );
702 aFont.SetCharSet( eCharSet );
703 rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
705 rNum.SetBulletFont( &aFont );
707 // take only the very first character
708 if (rAV.cbTextBefore || rAV.cbTextAfter)
710 rNum.SetBulletChar(
711 sText.toString().iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
713 else
714 rNum.SetBulletChar( 0x2190 );
718 if( !bInsert )
719 return;
721 OUString sPrefix;
722 OUString sSuffix;
723 if (rAV.cbTextBefore)
725 sPrefix = sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear();
727 if( rAV.cbTextAfter )
729 sSuffix = rNum.GetSuffix();
730 sSuffix += sText.copy( rAV.cbTextBefore, rAV.cbTextAfter).makeStringAndClear();
732 if (rAV.cbTextBefore || rAV.cbTextAfter)
734 rNum.SetListFormat(sPrefix, sSuffix, nLevel);
736 // The characters before and after multiple digits do not apply because
737 // those are handled differently by the writer and the result is in most
738 // cases worse than without.
741 // SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
742 // which are provided by pNumR. This is used for everything beside
743 // outline inside the text.
744 void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
745 bool bOutLine)
747 SwNumFormat aNF;
748 if (pAD)
749 { // there is an Anld-Sprm
750 m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross;
751 WW8_ANLV const &rAV = pAD->eAnlv;
752 SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
753 SetAnlvStrings(aNF, nSwLevel, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
755 pNumR->Set(nSwLevel, aNF);
758 // chapter numbering and bullets
760 // Chapter numbering happens in the style definition.
761 // Sprm 13 provides the level, Sprm 12 the content.
763 SwNumRule* SwWW8ImplReader::GetStyRule()
765 if( m_xStyles->mpStyRule ) // Bullet-Style already present
766 return m_xStyles->mpStyRule;
768 const OUString aBaseName("WW8StyleNum");
769 const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
771 // #i86652#
772 sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false,
773 SvxNumberFormat::LABEL_ALIGNMENT );
774 m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul];
775 // Auto == false-> numbering style
776 m_xStyles->mpStyRule->SetAutoRule(false);
778 return m_xStyles->mpStyRule;
781 // Sprm 13
782 void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
784 m_nSwNumLevel = 0xff; // Default: invalid
786 if( nLen <= 0 )
787 return;
789 // StyleDef ?
790 if( m_pCurrentColl )
792 // only for SwTextFormatColl, not CharFormat
793 // WW: 0 = no Numbering
794 SwWW8StyInf * pColl = GetStyle(m_nCurrentColl);
795 if (pColl != nullptr && pColl->m_bColl && *pData)
797 // Range WW:1..9 -> SW:0..8 no bullets / numbering
799 if (*pData <= 9)
801 m_nSwNumLevel = *pData - 1;
802 if (!m_bNoAttrImport)
803 static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
804 // For WW-NoNumbering also NO_NUMBERING could be used.
805 // ( For normal numberierung NO_NUM has to be used:
806 // NO_NUM : pauses numbering,
807 // NO_NUMBERING : no numbering at all )
810 else if( *pData == 10 || *pData == 11 )
812 // remember type, the rest happens at Sprm 12
813 m_xStyles->mnWwNumLevel = *pData;
817 else
819 //Not StyleDef
820 if (!m_bAnl)
821 StartAnl(pData); // begin of outline / bullets
822 NextAnlLine(pData);
826 void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
828 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
829 if( !m_pCurrentColl || nLen <= 0 // only for Styledef
830 || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
831 || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
833 m_nSwNumLevel = 0xff;
834 return;
837 if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD))
839 SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
840 m_nSwNumLevel = 0xff;
841 return;
844 if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8
847 // If NumRuleItems were set, either directly or through inheritance, disable them now
848 m_pCurrentColl->SetFormatAttr( SwNumRuleItem() );
850 const OUString aName("Outline");
851 SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ),
852 SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
853 OUTLINE_RULE );
854 aNR = *m_rDoc.GetOutlineNumRule();
856 SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
858 // Missing Levels need not be replenished
859 m_rDoc.SetOutlineNumRule( aNR );
861 else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){
862 SwNumRule* pNR = GetStyRule();
863 SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
864 m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
866 pStyInf = GetStyle(m_nCurrentColl);
867 if (pStyInf != nullptr)
868 pStyInf->m_bHasStyNumRule = true;
872 // Numbering / Bullets
874 // SetNumOlst() carries the Numrules for this cell to SwNumFormat.
875 // For this the info is fetched from OLST and not from ANLD ( see later )
876 // ( only for outline inside text; Bullets / numbering use ANLDs )
877 void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel)
879 SwNumFormat aNF;
880 WW8_ANLV &rAV = pO->rganlv[nSwLevel];
881 SetBaseAnlv(aNF, rAV, nSwLevel);
882 // ... and then the Strings
883 int nTextOfs = 0;
884 sal_uInt8 i;
885 WW8_ANLV* pAV1; // search String-Positions
886 for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
887 nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
889 if (!m_bVer67)
890 nTextOfs *= 2;
891 SetAnlvStrings(aNF, nSwLevel, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
892 pNumR->Set(nSwLevel, aNF);
895 // The OLST is at the beginning of each section that contains outlines.
896 // The ANLDs that are connected to each outline-line contain only nonsense,
897 // so the OLSTs are remembered for the section to have usable information
898 // when outline-paragraphs occur.
899 void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
901 m_xNumOlst.reset();
902 if (nLen <= 0)
903 return;
905 if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST))
907 SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
908 return;
911 m_xNumOlst.reset(new WW8_OLST);
912 *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
915 WW8LvlType GetNumType(sal_uInt8 nWwLevelNo)
917 WW8LvlType nRet = WW8_None;
918 if( nWwLevelNo == 12 )
919 nRet = WW8_Pause;
920 else if( nWwLevelNo == 10 )
921 nRet = WW8_Numbering;
922 else if( nWwLevelNo == 11 )
923 nRet = WW8_Sequence;
924 else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
925 nRet = WW8_Outline;
926 return nRet;
929 SwNumRule *ANLDRuleMap::GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType)
931 const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
932 if (rNumRule.isEmpty())
933 return nullptr;
934 return rDoc.FindNumRulePtr(rNumRule);
937 void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
939 if (WW8_Numbering == nNumType)
940 msNumberingNumRule = rNumRule;
941 else
942 msOutlineNumRule = rNumRule;
945 // StartAnl is called at the beginning of a row area that contains
946 // outline / numbering / bullets
947 void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13)
949 m_bCurrentAND_fNumberAcross = false;
951 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
952 if (nT == WW8_Pause || nT == WW8_None)
953 return;
955 m_nWwNumType = nT;
956 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
958 // check for COL numbering:
959 SprmResult aS12; // sprmAnld
960 OUString sNumRule;
962 if (m_xTableDesc)
964 sNumRule = m_xTableDesc->GetNumRuleName();
965 if (!sNumRule.isEmpty())
967 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
968 if (!pNumRule)
969 sNumRule.clear();
970 else
972 // this is ROW numbering ?
973 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
974 if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
975 sNumRule.clear();
980 SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
981 if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
983 sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
984 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
985 if (!pNumRule)
986 sNumRule.clear();
989 if (sNumRule.isEmpty())
991 if (!pNumRule)
993 // #i86652#
994 pNumRule = m_rDoc.GetNumRuleTable()[
995 m_rDoc.MakeNumRule( sNumRule, nullptr, false,
996 SvxNumberFormat::LABEL_ALIGNMENT ) ];
998 if (m_xTableDesc)
1000 if (!aS12.pSprm)
1001 aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
1002 if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
1003 m_xTableDesc->SetNumRuleName(pNumRule->GetName());
1007 m_bAnl = true;
1009 sNumRule = pNumRule ? pNumRule->GetName() : OUString();
1010 // set NumRules via stack
1011 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(),
1012 SfxStringItem(RES_FLTR_NUMRULE, sNumRule));
1014 m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
1017 // NextAnlLine() is called once for every row of a
1018 // outline / numbering / bullet
1019 void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13)
1021 if (!m_bAnl)
1022 return;
1024 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
1026 // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3
1028 // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0
1029 if (*pSprm13 == 10 || *pSprm13 == 11)
1031 m_nSwNumLevel = 0;
1032 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1034 // not defined yet
1035 // sprmAnld o. 0
1036 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1037 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1038 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1041 else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
1043 m_nSwNumLevel = *pSprm13 - 1; // outline
1044 // undefined
1045 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1047 if (m_xNumOlst) // there was a OLST
1049 //Assure upper levels are set, #i9556#
1050 for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
1052 if (!pNumRule->GetNumFormat(nI))
1053 SetNumOlst(pNumRule, m_xNumOlst.get(), nI);
1056 SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel);
1058 else // no Olst -> use Anld
1060 // sprmAnld
1061 SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
1062 if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
1063 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
1067 else
1068 m_nSwNumLevel = 0xff; // no number
1070 SwTextNode* pNd = m_pPaM->GetNode().GetTextNode();
1071 if (!pNd)
1072 return;
1074 if (m_nSwNumLevel < MAXLEVEL)
1075 pNd->SetAttrListLevel( m_nSwNumLevel );
1076 else
1078 pNd->SetAttrListLevel(0);
1079 pNd->SetCountedInList( false );
1083 void SwWW8ImplReader::StopAllAnl(bool bGoBack)
1085 //Of course we're not restarting, but we'll make use of our knowledge
1086 //of the implementation to do it.
1087 StopAnlToRestart(WW8_None, bGoBack);
1090 void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack)
1092 if (bGoBack)
1094 SwPosition aTmpPos(*m_pPaM->GetPoint());
1095 m_pPaM->Move(fnMoveBackward, GoInContent);
1096 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1097 *m_pPaM->GetPoint() = aTmpPos;
1099 else
1100 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1102 m_aANLDRules.msNumberingNumRule.clear();
1104 #i18816#
1105 my take on this problem is that moving either way from an outline to a
1106 numbering doesn't halt the outline, while the numbering is always halted
1108 bool bNumberingNotStopOutline =
1109 (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
1110 ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
1111 if (!bNumberingNotStopOutline)
1112 m_aANLDRules.msOutlineNumRule.clear();
1114 m_nSwNumLevel = 0xff;
1115 m_nWwNumType = WW8_None;
1116 m_bAnl = false;
1119 WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc const & rBand )
1121 *this = rBand;
1122 if( rBand.pTCs )
1124 pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]);
1125 // create uninitialized
1126 memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
1128 if( rBand.pSHDs )
1130 pSHDs = new WW8_SHD[nWwCols];
1131 memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
1133 if( rBand.pNewSHDs )
1135 pNewSHDs = new Color[nWwCols];
1136 memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color));
1138 memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
1141 // ReadDef reads the cell position and the borders of a band
1142 void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen)
1144 if (!bVer67)
1146 //the ww8 version of this is unusual in masquerading as a srpm with a
1147 //single byte len arg while it really has a word len arg, after this
1148 //increment nLen is correct to describe the remaining amount of data
1149 pS++;
1152 --nLen; //reduce len by expected nCols arg
1153 if (nLen < 0)
1154 return;
1155 sal_uInt8 nCols = *pS; // number of cells
1157 if (nCols > MAX_COL)
1158 return;
1160 nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments
1161 if (nLen < 0)
1162 return;
1164 short nOldCols = nWwCols;
1165 nWwCols = nCols;
1167 const sal_uInt8* pT = &pS[1];
1168 for (int i = 0; i <= nCols; i++, pT+=2)
1169 nCenter[i] = static_cast<sal_Int16>(SVBT16ToUInt16( pT )); // X-borders
1171 if( nCols != nOldCols ) // different column count
1173 delete[] pTCs;
1174 pTCs = nullptr;
1175 delete[] pSHDs;
1176 pSHDs = nullptr;
1177 delete[] pNewSHDs;
1178 pNewSHDs = nullptr;
1181 short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
1183 if (!pTCs && nCols)
1185 // create empty TCs
1186 pTCs = new WW8_TCell[nCols];
1189 short nColsToRead = std::min<short>(nFileCols, nCols);
1191 if (nColsToRead <= 0)
1192 return;
1194 // read TCs
1197 Attention: Beginning with Ver8 there is an extra ushort per TC
1198 added and the size of the border code is doubled.
1199 Because of this a simple copy (pTCs[i] = *pTc;)
1200 is not possible.
1202 Advantage: The work structure suits better.
1204 WW8_TCell* pCurrentTC = pTCs;
1205 if( bVer67 )
1207 WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
1208 for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc)
1210 // TC from file ?
1211 sal_uInt8 aBits1 = pTc->aBits1Ver6;
1212 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
1213 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
1214 pCurrentTC->rgbrc[ WW8_TOP ]
1215 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
1216 pCurrentTC->rgbrc[ WW8_LEFT ]
1217 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
1218 pCurrentTC->rgbrc[ WW8_BOT ]
1219 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
1220 pCurrentTC->rgbrc[ WW8_RIGHT ]
1221 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1222 if( ( pCurrentTC->bMerged )
1223 && ( i > 0 ) )
1225 // Cell merged -> remember
1226 //bWWMergedVer6[i] = true;
1227 pTCs[i-1].rgbrc[ WW8_RIGHT ]
1228 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1229 // apply right border to previous cell
1230 // bExist must not be set to false, because WW
1231 // does not count this cells in text boxes...
1235 else
1237 WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
1238 for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc )
1240 sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 );
1241 pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
1242 pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
1243 pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
1244 pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
1245 pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
1246 pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
1247 pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
1248 pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
1249 // note: in aBits1 there are 7 bits unused,
1250 // followed by another 16 unused bits
1252 pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
1253 pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
1254 pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
1255 pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
1259 // #i25071 In '97 text direction appears to be only set using TC properties
1260 // not with sprmTTextFlow so we need to cycle through the maDirections and
1261 // double check any non-default directions
1262 for (int k = 0; k < nCols; ++k)
1264 if(maDirections[k] == 4)
1266 if(pTCs[k].bVertical)
1268 if(pTCs[k].bBackward)
1269 maDirections[k] = 3;
1270 else
1271 maDirections[k] = 1;
1277 void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen)
1279 if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s)
1280 return;
1282 if (nParamsLen < 3)
1284 SAL_WARN("sw.ww8", "table border property is too short");
1285 return;
1288 sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
1289 sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
1290 sal_uInt8 nFlag = *(pParamsTSetBRC+2);
1292 if (nitcFirst >= nWwCols)
1293 return;
1295 if (nitcLim > nWwCols)
1296 nitcLim = nWwCols;
1298 bool bChangeRight = (nFlag & 0x08) != 0;
1299 bool bChangeBottom = (nFlag & 0x04) != 0;
1300 bool bChangeLeft = (nFlag & 0x02) != 0;
1301 bool bChangeTop = (nFlag & 0x01) != 0;
1303 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1304 WW8_BRCVer9 brcVer9;
1305 if( nBrcVer == 6 )
1307 if (nParamsLen < sizeof(WW8_BRCVer6) + 3)
1309 SAL_WARN("sw.ww8", "table border property is too short");
1310 return;
1312 brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
1314 else if( nBrcVer == 8 )
1316 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1317 if (nParamsLen < sizeof(WW8_BRC) + 3)
1319 SAL_WARN("sw.ww8", "table border property is too short");
1320 return;
1322 brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
1324 else
1326 if (nParamsLen < sizeof(WW8_BRCVer9) + 3)
1328 SAL_WARN("sw.ww8", "table border property is too short");
1329 return;
1331 brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
1334 for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC )
1336 if( bChangeTop )
1337 pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9;
1338 if( bChangeLeft )
1339 pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9;
1340 if( bChangeBottom )
1341 pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9;
1342 if( bChangeRight )
1343 pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9;
1347 void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen)
1349 // sprmTTableBorders
1350 if( nBrcVer == 6 )
1352 if (nParamsLen < sizeof(WW8_BRCVer6) * 6)
1354 SAL_WARN("sw.ww8", "table border property is too short");
1355 return;
1357 WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
1358 for (int i = 0; i < 6; ++i)
1359 aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
1361 else if ( nBrcVer == 8 )
1363 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1364 if (nParamsLen < sizeof(WW8_BRC) * 6)
1366 SAL_WARN("sw.ww8", "table border property is too short");
1367 return;
1369 for( int i = 0; i < 6; ++i )
1370 aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
1372 else
1374 if (nParamsLen < sizeof( aDefBrcs ))
1376 SAL_WARN("sw.ww8", "table border property is too short");
1377 return;
1379 memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
1383 void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol)
1385 // sprmTDxaCol (opcode 0x7623) changes the width of cells
1386 // whose index is within a certain range to be a certain value.
1388 if( !(nWwCols && pParamsTDxaCol) ) // set one or more cell length(s)
1389 return;
1391 sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
1392 sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
1393 short nDxaCol = static_cast<sal_Int16>(SVBT16ToUInt16( pParamsTDxaCol + 2 ));
1395 for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
1397 const short nOrgWidth = nCenter[i+1] - nCenter[i];
1398 const short nDelta = nDxaCol - nOrgWidth;
1399 for( int j = i+1; j <= nWwCols; j++ )
1401 nCenter[j] = nCenter[j] + nDelta;
1406 void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert)
1408 if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s)
1409 return;
1411 sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
1412 if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
1413 return;
1414 sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
1415 sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 );
1417 short nNewWwCols;
1418 if (nitcInsert > nWwCols)
1420 nNewWwCols = nitcInsert+nctc;
1421 //if new count would be outside max possible count, clip it, and calc a new replacement
1422 //legal nctc
1423 if (nNewWwCols > MAX_COL)
1425 nNewWwCols = MAX_COL;
1426 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
1429 else
1431 nNewWwCols = nWwCols+nctc;
1432 //if new count would be outside max possible count, clip it, and calc a new replacement
1433 //legal nctc
1434 if (nNewWwCols > MAX_COL)
1436 nNewWwCols = MAX_COL;
1437 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
1441 WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
1443 if (pTCs)
1445 memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
1446 delete[] pTCs;
1448 pTCs = pTC2s;
1450 //If we have to move some cells
1451 if (nitcInsert <= nWwCols)
1453 // adjust the left x-position of the dummy at the very end
1454 nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
1455 for( int i = nWwCols-1; i >= nitcInsert; i--)
1457 // adjust the left x-position
1458 nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
1460 // adjust the cell's borders
1461 pTCs[i + nctc] = pTCs[i];
1465 //if itcMac is larger than full size, fill in missing ones first
1466 for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
1467 nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
1469 //now add in our new cells
1470 for( int j = 0;j < nctc; j++)
1471 nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
1473 nWwCols = nNewWwCols;
1477 void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams)
1479 sal_uInt8 nStartCell = *pParams++;
1480 sal_uInt8 nEndCell = *pParams++;
1481 sal_uInt16 nCode = SVBT16ToUInt16(pParams);
1483 OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
1484 OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
1485 if (nStartCell > MAX_COL)
1486 return;
1487 if (nEndCell > MAX_COL + 1)
1488 nEndCell = MAX_COL + 1;
1490 for (;nStartCell < nEndCell; ++nStartCell)
1491 maDirections[nStartCell] = nCode;
1494 void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams)
1496 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1497 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1498 if (nLen != 6)
1499 return;
1500 mbHasSpacing=true;
1501 #if OSL_DEBUG_LEVEL > 0
1502 sal_uInt8 nWhichCell = *pParams;
1503 OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
1504 #endif
1505 ++pParams; //Skip which cell
1506 ++pParams; //unknown byte
1508 sal_uInt8 nSideBits = *pParams++;
1509 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1510 ++pParams; //unknown byte
1511 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1512 for (int i = wwTOP; i <= wwRIGHT; i++)
1514 switch (nSideBits & (1 << i))
1516 case 1 << wwTOP:
1517 mnDefaultTop = nValue;
1518 break;
1519 case 1 << wwLEFT:
1520 mnDefaultLeft = nValue;
1521 break;
1522 case 1 << wwBOTTOM:
1523 mnDefaultBottom = nValue;
1524 break;
1525 case 1 << wwRIGHT:
1526 mnDefaultRight = nValue;
1527 break;
1528 case 0:
1529 break;
1530 default:
1531 OSL_ENSURE(false, "Impossible");
1532 break;
1537 void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams)
1539 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1540 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1541 if (nLen != 6)
1542 return;
1544 const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to.
1545 const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins.
1546 OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings");
1547 if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 )
1548 return;
1550 sal_uInt8 nSideBits = *pParams++;
1551 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1553 const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin
1554 OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width");
1555 if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored
1556 return;
1558 sal_uInt16 nValue = SVBT16ToUInt16( pParams );
1560 for (int nCell = nStartCell; nCell < nEndCell; ++nCell)
1562 nOverrideSpacing[ nCell ] |= nSideBits;
1563 OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits");
1565 for (int i=0; i < 4; i++)
1567 if (nSideBits & (1 << i))
1568 nOverrideValues[ nCell ][ i ] = nValue;
1573 void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete)
1575 if( !(nWwCols && pParamsTDelete) ) // set one or more cell length(s)
1576 return;
1578 sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
1579 if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
1580 return;
1581 sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
1582 if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
1583 return;
1586 * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
1587 * greater than or equal to itcLim to be moved
1589 int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
1591 if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
1593 WW8_TCell* pCurrentTC = pTCs + nitcFirst;
1594 int i = 0;
1595 while( i < nShlCnt )
1597 // adjust the left x-position
1598 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1600 // adjust the cell's borders
1601 *pCurrentTC = pTCs[ nitcLim + i];
1603 ++i;
1604 ++pCurrentTC;
1606 // adjust the left x-position of the dummy at the very end
1607 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1610 short nCellsDeleted = nitcLim - nitcFirst;
1611 //clip delete request to available number of cells
1612 if (nCellsDeleted > nWwCols)
1613 nCellsDeleted = nWwCols;
1614 nWwCols -= nCellsDeleted;
1617 // ReadShd reads the background color of a cell
1618 // ReadDef must be called before
1619 void WW8TabBandDesc::ReadShd(const sal_uInt8* pS )
1621 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1622 if( !nLen )
1623 return;
1625 if( !pSHDs )
1627 pSHDs = new WW8_SHD[nWwCols];
1630 short nCount = nLen >> 1;
1631 if (nCount > nWwCols)
1632 nCount = nWwCols;
1634 SVBT16 const * pShd;
1635 int i;
1636 for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
1637 pSHDs[i].SetWWValue( *pShd );
1640 void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart)
1642 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1643 if (!nLen || nStart >= nWwCols)
1644 return;
1646 if (!pNewSHDs)
1647 pNewSHDs = new Color[nWwCols];
1649 short nCount = nLen / 10 + nStart; //10 bytes each
1650 if (nCount > nWwCols)
1651 nCount = nWwCols;
1653 int i=nStart;
1654 while (i < nCount)
1655 pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
1657 while (i < nWwCols)
1658 pNewSHDs[i++] = COL_AUTO;
1661 namespace
1663 SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
1665 if (bVer67)
1666 return pPap->HasSprm(24);
1667 SprmResult aRes = pPap->HasSprm(0x244B);
1668 if (aRes.pSprm == nullptr)
1669 aRes = pPap->HasSprm(0x2416);
1670 return aRes;
1674 namespace {
1676 enum wwTableSprm
1678 sprmNil,
1680 sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
1681 sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft,
1682 sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete,
1683 sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
1684 sprmTDefTableNewShd, sprmTDefTableNewShd2nd, sprmTDefTableNewShd3rd,
1685 sprmTCellPadding, sprmTCellPaddingDefault
1690 static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
1692 switch (eVer)
1694 case ww::eWW8:
1695 switch (nId)
1697 case NS_sprm::TTableWidth::val:
1698 return sprmTTableWidth;
1699 case NS_sprm::TTextFlow::val:
1700 return sprmTTextFlow;
1701 case NS_sprm::TTableHeader::val:
1702 return sprmTTableHeader;
1703 case NS_sprm::TFCantSplit::val:
1704 return sprmTFCantSplit;
1705 case NS_sprm::TJc90::val:
1706 return sprmTJc;
1707 case NS_sprm::TFBiDi::val:
1708 return sprmTFBiDi;
1709 case NS_sprm::TDelete::val:
1710 return sprmTDelete;
1711 case NS_sprm::TInsert::val:
1712 return sprmTInsert;
1713 case NS_sprm::TDxaCol::val:
1714 return sprmTDxaCol;
1715 case NS_sprm::TDyaRowHeight::val:
1716 return sprmTDyaRowHeight;
1717 case NS_sprm::TDxaLeft::val:
1718 return sprmTDxaLeft;
1719 case NS_sprm::TDxaGapHalf::val:
1720 return sprmTDxaGapHalf;
1721 case NS_sprm::TTableBorders80::val:
1722 return sprmTTableBorders;
1723 case NS_sprm::TDefTable::val:
1724 return sprmTDefTable;
1725 case NS_sprm::TDefTableShd80::val:
1726 return sprmTDefTableShd;
1727 case NS_sprm::TDefTableShd::val:
1728 return sprmTDefTableNewShd;
1729 case NS_sprm::TDefTableShd2nd::val:
1730 return sprmTDefTableNewShd2nd;
1731 case NS_sprm::TDefTableShd3rd::val:
1732 return sprmTDefTableNewShd3rd;
1733 case NS_sprm::TTableBorders::val:
1734 return sprmTTableBorders90;
1735 case NS_sprm::TSetBrc80::val:
1736 return sprmTSetBrc;
1737 case NS_sprm::TSetBrc::val:
1738 return sprmTSetBrc90;
1739 case NS_sprm::TCellPadding::val:
1740 return sprmTCellPadding;
1741 case NS_sprm::TCellPaddingDefault::val:
1742 return sprmTCellPaddingDefault;
1744 break;
1745 case ww::eWW7:
1746 case ww::eWW6:
1747 switch (nId)
1749 case 182:
1750 return sprmTJc;
1751 case 183:
1752 return sprmTDxaLeft;
1753 case 184:
1754 return sprmTDxaGapHalf;
1755 case 186:
1756 return sprmTTableHeader;
1757 case 187:
1758 return sprmTTableBorders;
1759 case 189:
1760 return sprmTDyaRowHeight;
1761 case 190:
1762 return sprmTDefTable;
1763 case 191:
1764 return sprmTDefTableShd;
1765 case 193:
1766 return sprmTSetBrc;
1767 case 194:
1768 return sprmTInsert;
1769 case 195:
1770 return sprmTDelete;
1771 case 196:
1772 return sprmTDxaCol;
1774 break;
1775 case ww::eWW1:
1776 case ww::eWW2:
1777 switch (nId)
1779 case 146:
1780 return sprmTJc;
1781 case 147:
1782 return sprmTDxaLeft;
1783 case 148:
1784 return sprmTDxaGapHalf;
1785 case 153:
1786 return sprmTDyaRowHeight;
1787 case 154:
1788 return sprmTDefTable;
1789 case 155:
1790 return sprmTDefTableShd;
1791 case 157:
1792 return sprmTSetBrc;
1793 case 158:
1794 return sprmTInsert;
1795 case 159:
1796 return sprmTDelete;
1797 case 160:
1798 return sprmTDxaCol;
1800 break;
1802 return sprmNil;
1805 WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) :
1806 m_pIo(pIoClass),
1807 m_pFirstBand(nullptr),
1808 m_pActBand(nullptr),
1809 m_pTableNd(nullptr),
1810 m_pTabLines(nullptr),
1811 m_pTabLine(nullptr),
1812 m_pTabBoxes(nullptr),
1813 m_pTabBox(nullptr),
1814 m_pCurrentWWCell(nullptr),
1815 m_nRows(0),
1816 m_nDefaultSwCols(0),
1817 m_nBands(0),
1818 m_nMinLeft(0),
1819 m_nMaxRight(0),
1820 m_nSwWidth(0),
1821 m_nPreferredWidth(0),
1822 m_nPercentWidth(0),
1823 m_bOk(true),
1824 m_bClaimLineFormat(false),
1825 m_eOri(text::HoriOrientation::LEFT),
1826 m_bIsBiDi(false),
1827 m_nCurrentRow(0),
1828 m_nCurrentBandRow(0),
1829 m_nCurrentCol(0),
1830 m_nRowsToRepeat(0),
1831 m_pTable(nullptr),
1832 m_pParentPos(nullptr),
1833 m_pFlyFormat(nullptr),
1834 m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>)
1836 m_pIo->m_bCurrentAND_fNumberAcross = false;
1838 static const sal_Int16 aOriArr[] =
1840 text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
1843 bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
1844 WW8_TablePos aTabPos;
1846 WW8PLCFxSave1 aSave;
1847 m_pIo->m_xPlcxMan->GetPap()->Save( aSave );
1849 WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF();
1851 WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
1853 wwSprmParser aSprmParser(m_pIo->GetFib());
1855 std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
1857 // process pPap until end of table found
1860 short nTabeDxaNew = SHRT_MAX;
1861 bool bTabRowJustRead = false;
1862 const sal_uInt8* pShadeSprm = nullptr;
1863 const sal_uInt8* pNewShadeSprm[3] = {nullptr, nullptr, nullptr};
1864 const sal_uInt8* pTableBorders = nullptr;
1865 sal_uInt16 nTableBordersLen = 0;
1866 const sal_uInt8* pTableBorders90 = nullptr;
1867 sal_uInt16 nTableBorders90Len = 0;
1868 // params, len
1869 std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s;
1870 WW8_TablePos *pTabPos = nullptr;
1872 // search end of a tab row
1873 if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
1875 m_bOk = false;
1876 break;
1879 // Get the SPRM chains:
1880 // first from PAP and then from PCD (of the Piece Table)
1881 WW8PLCFxDesc aDesc;
1882 pPap->GetSprms( &aDesc );
1883 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
1885 for (int nLoop = 0; nLoop < 2; ++nLoop)
1887 const sal_uInt8* pParams;
1888 while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams()))
1890 sal_uInt16 nId = aSprmIter.GetCurrentId();
1891 sal_Int32 nFixedLen = aSprmParser.DistanceToData(nId);
1892 sal_Int32 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen());
1893 sal_Int32 nLen = nL - nFixedLen;
1894 wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
1895 switch (eSprm)
1897 case sprmTTableWidth:
1899 const sal_uInt8 b0 = pParams[0];
1900 const sal_uInt8 b1 = pParams[1];
1901 const sal_uInt8 b2 = pParams[2];
1902 if (b0 == 3) // Twips
1903 m_nPreferredWidth = b2 * 0x100 + b1;
1904 else if (b0 == 2) // percent in fiftieths of a percent
1906 m_nPercentWidth = (b2 * 0x100 + b1);
1907 // MS documentation: non-negative, and 600% max
1908 if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 )
1909 m_nPercentWidth *= .02;
1910 else
1911 m_nPercentWidth = 100;
1914 break;
1915 case sprmTTextFlow:
1916 pNewBand->ProcessDirection(pParams);
1917 break;
1918 case sprmTFCantSplit:
1919 pNewBand->bCantSplit = *pParams;
1920 m_bClaimLineFormat = true;
1921 break;
1922 case sprmTTableBorders:
1923 pTableBorders = pParams; // process at end
1924 nTableBordersLen = nLen;
1925 break;
1926 case sprmTTableBorders90:
1927 pTableBorders90 = pParams; // process at end
1928 nTableBorders90Len = nLen;
1929 break;
1930 case sprmTTableHeader:
1931 // tdf#105570
1932 if ( m_nRowsToRepeat == m_nRows )
1933 m_nRowsToRepeat = (m_nRows + 1);
1934 break;
1935 case sprmTJc:
1936 // sprmTJc - Justification Code
1937 if (m_nRows == 0)
1938 m_eOri = aOriArr[*pParams & 0x3];
1939 break;
1940 case sprmTFBiDi:
1941 m_bIsBiDi = SVBT16ToUInt16(pParams) != 0;
1942 break;
1943 case sprmTDxaGapHalf:
1944 pNewBand->nGapHalf = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1945 break;
1946 case sprmTDyaRowHeight:
1947 pNewBand->nLineHeight = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1948 m_bClaimLineFormat = true;
1949 break;
1950 case sprmTDefTable:
1951 pNewBand->ReadDef(bOldVer, pParams, nLen);
1952 bTabRowJustRead = true;
1953 break;
1954 case sprmTDefTableShd:
1955 pShadeSprm = pParams;
1956 break;
1957 case sprmTDefTableNewShd:
1958 pNewShadeSprm[0] = pParams;
1959 break;
1960 case sprmTDefTableNewShd2nd:
1961 pNewShadeSprm[1] = pParams;
1962 break;
1963 case sprmTDefTableNewShd3rd:
1964 pNewShadeSprm[2] = pParams;
1965 break;
1966 case sprmTDxaLeft:
1967 // our Writer cannot shift single table lines
1968 // horizontally so we have to find the smallest
1969 // parameter (meaning the left-most position) and then
1970 // shift the whole table to that margin (see below)
1972 short nDxaNew = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
1973 if( nDxaNew < nTabeDxaNew )
1974 nTabeDxaNew = nDxaNew;
1976 break;
1977 case sprmTSetBrc:
1978 aTSetBrcs.emplace_back(pParams, nLen); // process at end
1979 break;
1980 case sprmTSetBrc90:
1981 aTSetBrc90s.emplace_back(pParams, nLen); // process at end
1982 break;
1983 case sprmTDxaCol:
1984 pNewBand->ProcessSprmTDxaCol(pParams);
1985 break;
1986 case sprmTInsert:
1987 pNewBand->ProcessSprmTInsert(pParams);
1988 break;
1989 case sprmTDelete:
1990 pNewBand->ProcessSprmTDelete(pParams);
1991 break;
1992 case sprmTCellPaddingDefault:
1993 pNewBand->ProcessSpacing(pParams);
1994 break;
1995 case sprmTCellPadding:
1996 pNewBand->ProcessSpecificSpacing(pParams);
1997 break;
1998 default:
2001 aSprmIter.advance();
2004 if( !nLoop )
2006 pPap->GetPCDSprms( aDesc );
2007 aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
2011 // WW-Tables can contain Fly-changes. For this abort tables here
2012 // and start again. *pPap is still before TabRowEnd, so TestApo()
2013 // can be called with the last parameter set to false and therefore
2014 // take effect.
2016 if (bTabRowJustRead)
2018 // Some SPRMs need to be processed *after* ReadDef is called
2019 // so they were saved up until here
2020 if (pShadeSprm)
2021 pNewBand->ReadShd(pShadeSprm);
2022 if (pNewShadeSprm[0])
2023 pNewBand->ReadNewShd(pNewShadeSprm[0], bOldVer, /*nStart=*/0);
2024 if (pNewShadeSprm[1])
2025 pNewBand->ReadNewShd(pNewShadeSprm[1], bOldVer, /*nStart=*/22);
2026 if (pNewShadeSprm[2])
2027 pNewBand->ReadNewShd(pNewShadeSprm[2], bOldVer, /*nStart=*/44);
2028 if (pTableBorders90)
2029 pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len);
2030 else if (pTableBorders)
2031 pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
2032 pTableBorders, nTableBordersLen);
2033 for (const auto& a : aTSetBrcs)
2034 pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second);
2035 for (const auto& a : aTSetBrc90s)
2036 pNewBand->ProcessSprmTSetBRC(9, a.first, a.second);
2039 if( nTabeDxaNew < SHRT_MAX )
2041 short* pCenter = pNewBand->nCenter;
2042 short firstDxaCenter = *pCenter;
2043 for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
2045 // #i30298# Use sprmTDxaLeft to adjust the left indent
2046 // #i40461# Use dxaGapHalf during calculation
2047 *pCenter +=
2048 (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
2052 if (!m_pActBand)
2053 m_pActBand = m_pFirstBand = pNewBand;
2054 else
2056 m_pActBand->pNextBand = pNewBand;
2057 m_pActBand = pNewBand;
2059 m_nBands++;
2061 pNewBand = new WW8TabBandDesc;
2063 m_nRows++;
2064 m_pActBand->nRows++;
2066 //Seek our pap to its next block of properties
2067 WW8PLCFxDesc aRes;
2068 aRes.pMemPos = nullptr;
2069 aRes.nStartPos = nStartCp;
2071 if (!(pPap->SeekPos(aRes.nStartPos)))
2073 aRes.nEndPos = WW8_CP_MAX;
2074 pPap->SetDirty(true);
2076 pPap->GetSprms(&aRes);
2077 pPap->SetDirty(false);
2079 //Are we at the end of available properties
2080 if (
2081 !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
2082 aRes.nStartPos == WW8_CP_MAX
2085 m_bOk = false;
2086 break;
2089 //Are we still in a table cell
2090 SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer);
2091 const sal_uInt8* pParams = aParamsRes.pSprm;
2092 SprmResult aLevelRes = pPap->HasSprm(0x6649);
2093 const sal_uInt8 *pLevel = aLevelRes.pSprm;
2094 // InTable
2095 if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) ||
2096 (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable)))
2098 break;
2101 //Get the end of row new table positioning data
2102 WW8_CP nMyStartCp=nStartCp;
2103 if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
2104 if (m_pIo->ParseTabPos(&aTabPos, pPap))
2105 pTabPos = &aTabPos;
2107 //Move back to this cell
2108 aRes.pMemPos = nullptr;
2109 aRes.nStartPos = nStartCp;
2111 // PlcxMan currently points too far ahead so we need to bring
2112 // it back to where we are trying to make a table
2113 m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
2114 m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
2115 if (!(pPap->SeekPos(aRes.nStartPos)))
2117 aRes.nEndPos = WW8_CP_MAX;
2118 pPap->SetDirty(true);
2120 pPap->GetSprms(&aRes);
2121 pPap->SetDirty(false);
2123 //Does this row match up with the last row closely enough to be
2124 //considered part of the same table
2125 ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
2128 ##513##, #79474# If this is not sufficient, then we should look at
2129 sprmPD{y|x}aAbs as our indicator that the following set of rows is not
2130 part of this table, but instead is an absolutely positioned table
2131 outside of this one
2133 if (aApo.mbStopApo)
2134 break;
2135 if (aApo.mbStartApo)
2137 //if there really is a fly here, and not a "null" fly then break.
2138 if (m_pIo->ConstructApo(aApo, pTabPos))
2139 break;
2142 auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
2143 if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
2145 SAL_WARN("sw.ww8", "WW8TabDesc, loop in paragraph property chain");
2146 break;
2148 nStartCp = aRes.nEndPos;
2150 while(true);
2152 if( m_bOk )
2154 if( m_pActBand->nRows > 1 )
2156 // last band has more than 1 cell
2157 delete pNewBand;
2158 pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
2159 m_pActBand->nRows--; // because of special treatment of border defaults
2160 pNewBand->nRows = 1;
2161 m_pActBand->pNextBand = pNewBand; // append at the end
2162 m_nBands++;
2163 pNewBand = nullptr; // do not delete
2165 CalcDefaults();
2167 delete pNewBand;
2169 m_pIo->m_xPlcxMan->GetPap()->Restore( aSave );
2172 WW8TabDesc::~WW8TabDesc()
2174 WW8TabBandDesc* pR = m_pFirstBand;
2175 while(pR)
2177 WW8TabBandDesc* pR2 = pR->pNextBand;
2178 delete pR;
2179 pR = pR2;
2182 delete m_pParentPos;
2185 void WW8TabDesc::CalcDefaults()
2187 short nMinCols = SHRT_MAX;
2188 WW8TabBandDesc* pR;
2190 m_nMinLeft = SHRT_MAX;
2191 m_nMaxRight = SHRT_MIN;
2194 If we are an honestly inline centered table, then the normal rules of
2195 engagement for left and right margins do not apply. The multiple rows are
2196 centered regardless of the actual placement of rows, so we cannot have
2197 mismatched rows as is possible in other configurations.
2199 e.g. change the example bugdoc in word from text wrapping of none (inline)
2200 to around (in frame (bApo)) and the table splits into two very disjoint
2201 rows as the beginning point of each row are very different
2203 if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
2205 for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
2206 for( short i = pR->nWwCols; i >= 0; --i)
2207 pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
2210 // First loop: find outermost L and R borders
2211 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2213 if( pR->nCenter[0] < m_nMinLeft )
2214 m_nMinLeft = pR->nCenter[0];
2216 // Following adjustment moves a border and then uses it to find width
2217 // of next cell, so collect current widths, to avoid situation when width
2218 // adjustment to too narrow cell makes next cell have negative width
2219 short nOrigWidth[MAX_COL + 1];
2220 for( short i = 0; i < pR->nWwCols; i++ )
2222 nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
2225 for( short i = 0; i < pR->nWwCols; i++ )
2228 If the margins are so large as to make the displayable
2229 area inside them smaller than the minimum allowed then adjust the
2230 width to fit. But only do it if the two cells are not the exact
2231 same value, if they are then the cell does not really exist and will
2232 be blended together into the same cell through the use of the
2233 nTrans(late) array.
2234 #i28333# If the nGapHalf is greater than the cell width best to ignore it
2236 int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
2237 if (nCellWidth != nOrigWidth[i])
2239 if (nOrigWidth[i] == 0)
2240 nCellWidth = 0; // restore zero-width "cell"
2241 else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
2242 nCellWidth = pR->nGapHalf + 1; // avoid false ignore
2243 else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
2244 nCellWidth = 1; // minimal non-zero width to minimize distortion
2246 if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
2248 nCellWidth = MINLAY + pR->nGapHalf * 2;
2250 pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
2253 if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
2254 m_nMaxRight = pR->nCenter[pR->nWwCols];
2256 m_nSwWidth = m_nMaxRight - m_nMinLeft;
2258 // If the table is right aligned we need to align all rows to the
2259 // row that has the furthest right point
2261 if(m_eOri == text::HoriOrientation::RIGHT)
2263 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2265 int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
2266 for( short i = 0; i < pR->nWwCols + 1; i++ )
2268 pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
2274 // 2. pass: Detect number of writer columns. This can exceed the count
2275 // of columns in WW by 2, because SW in contrast to WW does not provide
2276 // fringed left and right borders and has to fill with empty boxes.
2277 // Non existent cells can reduce the number of columns.
2279 // 3. pass: Replace border with defaults if needed
2280 for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
2282 if( !pR->pTCs )
2284 pR->pTCs = new WW8_TCell[ pR->nWwCols ];
2286 for (int k = 0; k < pR->nWwCols; ++k)
2288 WW8_TCell& rT = pR->pTCs[k];
2289 for (int i = 0; i < 4; ++i)
2291 if (rT.rgbrc[i].brcType()==0)
2293 // if shadow is set, its invalid
2294 int j = i;
2295 switch( i )
2297 case 0:
2298 // outer top / horizontally inside
2299 j = (pR == m_pFirstBand) ? 0 : 4;
2300 break;
2301 case 1:
2302 // outer left / vertically inside
2303 j = k ? 5 : 1;
2304 break;
2305 case 2:
2306 // outer bottom / horizontally inside
2307 j = pR->pNextBand ? 4 : 2;
2308 break;
2309 case 3:
2310 // outer right / vertically inside
2311 j = (k == pR->nWwCols - 1) ? 3 : 5;
2312 break;
2314 // merge with above defaults
2315 rT.rgbrc[i] = pR->aDefBrcs[j];
2321 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2323 pR->nSwCols = pR->nWwCols;
2324 pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
2325 pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols]) >= MINLAY;
2327 short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
2328 sal_uInt16 i;
2329 sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
2330 for (i = 0; i < pR->nWwCols; ++i)
2332 pR->nTransCell[i] = static_cast<sal_Int8>(j);
2333 if ( pR->nCenter[i] < pR->nCenter[i+1] )
2335 pR->bExist[i] = true;
2336 j++;
2338 else
2340 pR->bExist[i] = false;
2341 nAddCols--;
2345 OSL_ENSURE(i,"no columns in row ?");
2348 If the last cell was "false" then there is no valid cell following it,
2349 so the default mapping forward won't work. So map it (and
2350 contiguous invalid cells backwards to the last valid cell instead.)
2352 if (i && !pR->bExist[i-1])
2354 sal_uInt16 k=i-1;
2355 while (k && !pR->bExist[k])
2356 k--;
2357 for (sal_uInt16 n=k+1;n<i;n++)
2358 pR->nTransCell[n] = pR->nTransCell[k];
2361 pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other
2362 pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol
2364 pR->nSwCols = pR->nSwCols + nAddCols;
2365 if( pR->nSwCols < nMinCols )
2366 nMinCols = pR->nSwCols;
2369 if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
2370 (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
2371 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
2373 m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
2374 if( m_nDefaultSwCols == 0 )
2375 m_bOk = false;
2376 m_pActBand = m_pFirstBand;
2377 m_nCurrentBandRow = 0;
2378 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2381 void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat)
2383 SwFrameFormat* pApply = pFrameFormat;
2384 if (!pApply )
2385 pApply = m_pTable->GetFrameFormat();
2386 OSL_ENSURE(pApply,"No frame");
2387 pApply->SetFormatAttr(m_aItemSet);
2388 if (pFrameFormat)
2390 SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
2391 aSize.SetHeightSizeType(SwFrameSize::Minimum);
2392 aSize.SetHeight(MINLAY);
2393 pFrameFormat->SetFormatAttr(aSize);
2394 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
2398 void wwSectionManager::PrependedInlineNode(const SwPosition &rPos,
2399 const SwNode &rNode)
2401 OSL_ENSURE(!maSegments.empty(),
2402 "should not be possible, must be at least one segment");
2403 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.nNode))
2404 maSegments.back().maStart.Assign(rNode);
2407 void WW8TabDesc::CreateSwTable()
2409 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
2411 // if there is already some content on the Node append new node to ensure
2412 // that this content remains ABOVE the table
2413 SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
2414 bool bInsNode = pPoint->nContent.GetIndex() != 0;
2415 bool bSetMinHeight = false;
2418 #i8062#
2419 Set fly anchor to its anchor pos, so that if a table starts immediately
2420 at this position a new node will be inserted before inserting the table.
2422 SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo)
2423 ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr;
2424 if (pFormat)
2426 const SwPosition* pAPos =
2427 pFormat->GetAnchor().GetContentAnchor();
2428 if (pAPos && &pAPos->nNode.GetNode() == &pPoint->nNode.GetNode())
2430 bInsNode = true;
2431 bSetMinHeight = true;
2433 SwFormatSurround aSur(pFormat->GetSurround());
2434 aSur.SetAnchorOnly(true);
2435 pFormat->SetFormatAttr(aSur);
2439 if (bSetMinHeight)
2441 // minimize Fontsize to minimize height growth of the header/footer
2442 // set font size to 1 point to minimize y-growth of Hd/Ft
2443 SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE);
2444 m_pIo->NewAttr( aSz );
2445 m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
2448 if (bInsNode)
2449 m_pIo->AppendTextNode(*pPoint);
2451 m_xTmpPos.reset(new SwPosition(*m_pIo->m_pPaM->GetPoint()));
2453 // Because SW cannot handle multi-page floating frames,
2454 // _any unnecessary_ floating tables have been converted to inline.
2455 tools::Long nLeft = 0;
2456 if ( m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->pFlyFormat )
2458 // Get the table orientation from the fly
2459 // Do we also need to check m_pIo->m_xSFlyPara->bTogglePos/IsPosToggle()? [Probably not - layout-only concern]
2460 const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos;
2461 const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT
2462 : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT;
2463 if ( bIsInsideMargin && bAdjustMargin )
2464 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH;
2465 else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE )
2466 m_eOri = m_pIo->m_xSFlyPara->eHAlign;
2467 if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH )
2469 nLeft = m_pIo->m_xSFlyPara->nXPos;
2470 if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME )
2472 if ( !m_bIsBiDi )
2473 nLeft -= m_pIo->m_aSectionManager.GetPageLeft();
2474 else
2475 nLeft += m_pIo->m_aSectionManager.GetPageRight();
2480 // The table is small: The number of columns is the lowest count of
2481 // columns of the origin, because inserting is faster than deleting.
2482 // The number of rows is the count of bands because (identically)
2483 // rows of a band can be duplicated easy.
2484 m_pTable = m_pIo->m_rDoc.InsertTable(
2485 SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 0 ),
2486 *m_xTmpPos, m_nBands, m_nDefaultSwCols, m_eOri );
2488 OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
2489 if (!m_pTable || !m_pTable->GetFrameFormat())
2490 return;
2492 SwTableNode* pTableNode = m_pTable->GetTableNode();
2493 OSL_ENSURE(pTableNode, "no table node!");
2494 if (pTableNode)
2496 m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(),
2497 *pTableNode);
2500 // Check if the node into which the table should be inserted already
2501 // contains a Pagedesc. If so that Pagedesc would be moved to the
2502 // row after the table, that would be wrong. So delete and
2503 // set later to the table format.
2504 if (SwTextNode *const pNd = m_xTmpPos->nNode.GetNode().GetTextNode())
2506 if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
2508 SfxPoolItem *pSetAttr = nullptr;
2509 const SfxPoolItem* pItem;
2510 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false, &pItem))
2512 pSetAttr = new SvxFormatBreakItem( *static_cast<const SvxFormatBreakItem*>(pItem) );
2513 pNd->ResetAttr( RES_BREAK );
2516 // eventually set the PageDesc/Break now to the table
2517 if (pSetAttr)
2519 m_aItemSet.Put(*pSetAttr);
2520 delete pSetAttr;
2525 // total width of table
2526 if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols )
2528 SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, m_nSwWidth);
2529 // Don't set relative width if the table is in a floating frame
2530 if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->pFlyFormat) )
2531 aFrameSize.SetWidthPercent(m_nPercentWidth);
2532 m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize);
2533 m_aItemSet.Put(aFrameSize);
2536 SvxFrameDirectionItem aDirection(
2537 m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
2538 m_pTable->GetFrameFormat()->SetFormatAttr(aDirection);
2540 if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
2542 if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_xSFlyPara &&
2543 m_pIo->m_xSFlyPara->pFlyFormat && GetMinLeft())
2545 //If we are inside a frame and we have a border, the frames
2546 //placement does not consider the tables border, which word
2547 //displays outside the frame, so adjust here.
2548 SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->pFlyFormat->GetHoriOrient());
2549 sal_Int16 eHori = aHori.GetHoriOrient();
2550 if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
2551 (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
2553 //With multiple table, use last table settings. Perhaps
2554 //the maximum is what word does ?
2555 aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft());
2556 aHori.SetHoriOrient(text::HoriOrientation::NONE);
2557 m_pIo->m_xSFlyPara->pFlyFormat->SetFormatAttr(aHori);
2560 else // Not directly in a floating frame.
2562 //Historical note: If InLocalApo(), then this table is being placed in a floating
2563 //frame, and the frame matches the left and right *lines* of the
2564 //table, so the space to the left of the table isn't to be used
2565 //inside the frame, in word the dialog involved greys out the
2566 //ability to set the margin.
2567 SvxLRSpaceItem aL( RES_LR_SPACE );
2569 if (!m_bIsBiDi)
2570 nLeft += GetMinLeft();
2571 else
2573 const short nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth;
2574 nLeft += m_pIo->m_aSectionManager.GetTextAreaWidth();
2575 nLeft = nLeft - nTableWidth - GetMinLeft();
2577 aL.SetLeft(nLeft);
2579 m_aItemSet.Put(aL);
2583 mxOldRedlineStack = std::move(m_pIo->m_xRedlineStack);
2584 m_pIo->m_xRedlineStack.reset(new sw::util::RedlineStack(m_pIo->m_rDoc));
2587 void WW8TabDesc::UseSwTable()
2589 // init global Vars
2590 m_pTabLines = &m_pTable->GetTabLines();
2591 m_nCurrentRow = m_nCurrentCol = m_nCurrentBandRow = 0;
2593 m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
2594 GetSttNd()->FindTableNode());
2595 OSL_ENSURE( m_pTableNd, "Where is my table node" );
2597 // #i69519# - Restrict rows to repeat to a decent value
2598 if ( m_nRowsToRepeat == o3tl::narrowing<sal_uInt16>(m_nRows) )
2599 m_nRowsToRepeat = 1;
2601 m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat );
2602 // insert extra cells if needed and something like this
2603 AdjustNewBand();
2605 WW8DupProperties aDup(m_pIo->m_rDoc, m_pIo->m_xCtrlStck.get());
2606 m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
2608 // now set the correct PaM and prepare first merger group if any
2609 SetPamInCell(m_nCurrentCol, true);
2610 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2612 m_pIo->m_bWasTabRowEnd = false;
2613 m_pIo->m_bWasTabCellEnd = false;
2616 void WW8TabDesc::MergeCells()
2618 short nRow;
2620 for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand)
2622 // insert current box into merge group if appropriate.
2623 // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
2624 if( m_pActBand->pTCs )
2626 for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
2627 for( short i = 0; i < m_pActBand->nWwCols; i++ )
2629 WW8SelBoxInfo* pActMGroup = nullptr;
2631 // start a new merge group if appropriate
2633 OSL_ENSURE(nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()),
2634 "Too few lines, table ended early");
2635 if (nRow >= o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2636 return;
2637 m_pTabLine = (*m_pTabLines)[ nRow ];
2638 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2640 sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
2641 if (!m_pActBand->bExist[i])
2642 continue;
2643 OSL_ENSURE(nCol < m_pTabBoxes->size(),
2644 "Too few columns, table ended early");
2645 if (nCol >= m_pTabBoxes->size())
2646 return;
2647 m_pTabBox = (*m_pTabBoxes)[nCol];
2648 WW8_TCell& rCell = m_pActBand->pTCs[ i ];
2649 // is this the left upper cell of a merge group ?
2651 bool bMerge = false;
2652 if ( rCell.bVertRestart && !rCell.bMerged )
2653 bMerge = true;
2654 else if (rCell.bFirstMerged && m_pActBand->bExist[i])
2656 // Some tests to avoid merging cells which previously were
2657 // declared invalid because of sharing the exact same dimensions
2658 // as their previous cell
2660 //If there's anything underneath/above we're ok.
2661 if (rCell.bVertMerge || rCell.bVertRestart)
2662 bMerge = true;
2663 else
2665 //If it's a hori merge only, and the only things in
2666 //it are invalid cells then it's already taken care
2667 //of, so don't merge.
2668 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2669 if (m_pActBand->pTCs[ i2 ].bMerged &&
2670 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2672 if (m_pActBand->bExist[i2])
2674 bMerge = true;
2675 break;
2678 else
2679 break;
2683 // remove numbering from cells that will be disabled in the merge
2684 if( rCell.bVertMerge && !rCell.bVertRestart )
2686 SwPaM aPam( *m_pTabBox->GetSttNd(), 0 );
2687 aPam.GetPoint()->nNode++;
2688 SwTextNode* pNd = aPam.GetNode().GetTextNode();
2689 while( pNd )
2691 pNd->SetCountedInList( false );
2693 aPam.GetPoint()->nNode++;
2694 pNd = aPam.GetNode().GetTextNode();
2698 if (bMerge)
2700 short nX1 = m_pActBand->nCenter[ i ];
2701 short nWidth = m_pActBand->nWidth[ i ];
2703 // 2. create current merge group
2704 pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
2706 // determine size of new merge group
2707 // before inserted the new merge group.
2708 // Needed to correctly locked previously created merge groups.
2709 // Calculate total width and set
2710 short nSizCell = m_pActBand->nWidth[ i ];
2711 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2712 if (m_pActBand->pTCs[ i2 ].bMerged &&
2713 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2715 nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
2717 else
2718 break;
2719 pActMGroup->nGroupWidth = nSizCell;
2721 // locked previously created merge groups,
2722 // after determining the size for the new merge group.
2723 // 1. If necessary close old merge group(s) that overlap
2724 // the X-area of the new group
2725 for (;;)
2727 WW8SelBoxInfo* p = FindMergeGroup(
2728 nX1, pActMGroup->nGroupWidth, false );
2729 if (p == nullptr)
2731 break;
2733 p->bGroupLocked = true;
2736 // 3. push to group array
2737 m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
2740 // if necessary add the current box to a merge group
2741 // (that can be a newly created or another group)
2742 UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
2748 //There is a limbo area in word at the end of the row marker
2749 //where properties can live in word, there is no location in
2750 //writer equivalent, so try and park the cursor in the best
2751 //match, see #i23022#/#i18644#
2752 void WW8TabDesc::ParkPaM()
2754 SwTableBox *pTabBox2 = nullptr;
2755 short nRow = m_nCurrentRow + 1;
2756 if (nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
2758 if (SwTableLine *pLine = (*m_pTabLines)[nRow])
2760 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
2761 pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
2765 if (!pTabBox2 || !pTabBox2->GetSttNd())
2767 MoveOutsideTable();
2768 return;
2771 SwNodeOffset nSttNd = pTabBox2->GetSttIdx() + 1,
2772 nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
2774 if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd)
2778 m_pIo->m_pPaM->GetPoint()->nNode = nSttNd;
2780 while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2782 m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
2783 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2787 void WW8TabDesc::MoveOutsideTable()
2789 OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored");
2790 if (m_xTmpPos && m_pIo)
2791 *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos;
2794 void WW8TabDesc::FinishSwTable()
2796 m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
2798 // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
2799 // place, or somewhere close if that place got destroyed
2800 std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_pLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pLastAnchorPos) : nullptr);
2801 m_pIo->m_pLastAnchorPos.reset();
2803 m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack);
2805 if (xLastAnchorCursor)
2806 m_pIo->m_pLastAnchorPos.reset(new SwPosition(*xLastAnchorCursor->GetPoint()));
2808 WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get());
2809 m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
2811 MoveOutsideTable();
2812 m_xTmpPos.reset();
2814 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2816 m_pIo->m_bWasTabRowEnd = false;
2817 m_pIo->m_bWasTabCellEnd = false;
2819 m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM);
2821 MergeCells();
2823 // if needed group cells together that should be merged
2824 if (m_MergeGroups.empty())
2825 return;
2827 // process all merge groups one by one
2828 for (auto const& groupIt : m_MergeGroups)
2830 if((1 < groupIt->size()) && groupIt->row(0)[0])
2832 SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
2833 pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->nGroupWidth, 0));
2834 const sal_uInt16 nRowSpan = groupIt->rowsCount();
2835 for (sal_uInt16 n = 0; n < nRowSpan; ++n)
2837 auto& rRow = groupIt->row(n);
2838 for (size_t i = 0; i<rRow.size(); ++i)
2840 const sal_Int32 nRowSpanSet = (n == 0) && (i == 0) ?
2841 nRowSpan :
2842 (-1 * (nRowSpan - n));
2843 SwTableBox* pCurrentBox = rRow[i];
2844 pCurrentBox->setRowSpan(nRowSpanSet);
2846 if (i == 0)
2847 pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
2848 else
2850 SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
2851 pFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 0, 0));
2857 m_pIo->m_xFormatOfJustInsertedApo.reset();
2858 m_MergeGroups.clear();
2861 // browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
2863 // Parameter: nXcenter = center position of asking box
2864 // nWidth = width of asking box
2865 // bExact = flag, if box has to fit into group
2866 // or only has to touch
2868 WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
2870 if (!m_MergeGroups.empty())
2872 // still valid area near the boundary
2873 const short nTolerance = 4;
2874 // box boundary
2875 short nX2 = nX1 + nWidth;
2876 // approximate group boundary
2877 short nGrX1;
2878 short nGrX2;
2880 // improvement: search backwards
2881 for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
2883 // the currently inspected group
2884 WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
2885 if (!rActGroup.bGroupLocked)
2887 // approximate group boundary with room (tolerance) to the *outside*
2888 nGrX1 = rActGroup.nGroupXStart - nTolerance;
2889 nGrX2 = rActGroup.nGroupXStart
2890 + rActGroup.nGroupWidth + nTolerance;
2892 // If box fits report success
2894 if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
2896 return &rActGroup;
2899 // does the box share areas with the group?
2901 if( !bExact )
2903 // successful if nX1 *or* nX2 are inside the group
2904 if( ( ( nX1 > nGrX1 )
2905 && ( nX1 < nGrX2 - 2*nTolerance ) )
2906 || ( ( nX2 > nGrX1 + 2*nTolerance )
2907 && ( nX2 < nGrX2 ) )
2908 // or nX1 and nX2 surround the group
2909 || ( ( nX1 <=nGrX1 )
2910 && ( nX2 >=nGrX2 ) ) )
2912 return &rActGroup;
2918 return nullptr;
2921 bool WW8TabDesc::IsValidCell(short nCol) const
2923 return (o3tl::make_unsigned(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) &&
2924 m_pActBand->bExist[nCol] &&
2925 o3tl::make_unsigned(m_nCurrentRow) < m_pTabLines->size();
2928 bool WW8TabDesc::InFirstParaInCell() const
2930 //e.g. #i19718#
2931 if (!m_pTabBox || !m_pTabBox->GetSttNd())
2933 OSL_FAIL("Problem with table");
2934 return false;
2937 if (!IsValidCell(GetCurrentCol()))
2938 return false;
2940 return m_pIo->m_pPaM->GetPoint()->nNode == m_pTabBox->GetSttIdx() + 1;
2943 void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
2945 OSL_ENSURE( m_pActBand, "pActBand is 0" );
2946 if (!m_pActBand)
2947 return;
2949 sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
2951 if (o3tl::make_unsigned(m_nCurrentRow) >= m_pTabLines->size())
2953 OSL_ENSURE(false, "Actual row bigger than expected." );
2954 if (bPam)
2955 MoveOutsideTable();
2956 return;
2959 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
2960 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2962 if (nCol >= m_pTabBoxes->size())
2964 if (bPam)
2966 // The first paragraph in a cell with upper autospacing has upper
2967 // spacing set to 0
2968 if (
2969 m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara &&
2970 !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing
2973 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
2976 // The last paragraph in a cell with lower autospacing has lower
2977 // spacing set to 0
2978 if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
2979 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
2981 ParkPaM();
2983 return;
2985 m_pTabBox = (*m_pTabBoxes)[nCol];
2986 if( !m_pTabBox->GetSttNd() )
2988 OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table");
2989 if (bPam)
2990 MoveOutsideTable();
2991 return;
2993 if (!bPam)
2994 return;
2996 m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ];
2998 // The first paragraph in a cell with upper autospacing has upper spacing set to 0
2999 if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
3000 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
3002 // The last paragraph in a cell with lower autospacing has lower spacing set to 0
3003 if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
3004 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
3006 //We need to set the pPaM on the first cell, invalid
3007 //or not so that we can collect paragraph properties over
3008 //all the cells, but in that case on the valid cell we do not
3009 //want to reset the fmt properties
3010 SwNodeOffset nSttNd = m_pTabBox->GetSttIdx() + 1,
3011 nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
3012 if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd)
3016 m_pIo->m_pPaM->GetPoint()->nNode = nSttNd;
3018 while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
3019 m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
3020 // Precautionally set now, otherwise the style is not set for cells
3021 // that are inserted for margin balancing.
3022 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
3023 // because this cells are invisible helper constructions only to simulate
3024 // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
3027 // Better to turn Snap to Grid off for all paragraphs in tables
3028 SwTextNode *pNd = m_pIo->m_pPaM->GetNode().GetTextNode();
3029 if(!pNd)
3030 return;
3032 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
3033 const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
3035 if(!rSnapToGrid.GetValue())
3036 return;
3038 SvxParaGridItem aGridItem( rSnapToGrid );
3039 aGridItem.SetValue(false);
3041 SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
3043 const sal_Int32 nEnd = pGridPos->nContent.GetIndex();
3044 pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
3045 m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem);
3046 pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), nEnd);
3047 m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
3050 void WW8TabDesc::InsertCells( short nIns )
3052 m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
3053 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
3054 m_pTabBox = (*m_pTabBoxes)[0];
3056 m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, static_cast<SwTableBoxFormat*>(m_pTabBox->GetFrameFormat()),
3057 const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
3058 // The third parameter contains the FrameFormat of the boxes.
3059 // Here it is possible to optimize to save (reduce) FrameFormats.
3062 void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
3064 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3065 return; // faked cells -> no border
3067 SvxBoxItem aFormatBox( RES_BOX );
3068 if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
3070 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3071 if (SwWW8ImplReader::IsBorder(pT->rgbrc))
3072 SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
3075 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP))
3077 aFormatBox.SetDistance(
3078 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP],
3079 SvxBoxItemLine::TOP);
3081 else
3082 aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
3083 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM))
3085 aFormatBox.SetDistance(
3086 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM],
3087 SvxBoxItemLine::BOTTOM);
3089 else
3090 aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
3092 // nGapHalf for WW is a *horizontal* gap between table cell and content.
3093 short nLeftDist =
3094 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf;
3095 short nRightDist =
3096 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf;
3097 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT))
3099 aFormatBox.SetDistance(
3100 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT],
3101 SvxBoxItemLine::LEFT);
3103 else
3104 aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
3105 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT))
3107 aFormatBox.SetDistance(
3108 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT],
3109 SvxBoxItemLine::RIGHT);
3111 else
3112 aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
3114 pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
3117 void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
3119 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3120 return; // faked cells -> no color
3122 bool bFound=false;
3123 if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
3125 Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
3126 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND));
3127 bFound = true;
3130 //If there was no new shades, or no new shade setting
3131 if (m_pActBand->pSHDs && !bFound)
3133 WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
3134 if (!rSHD.GetValue()) // auto
3135 return;
3137 SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
3138 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.aColor, RES_BACKGROUND));
3142 static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
3144 SvxFrameDirection eDir = SvxFrameDirection::Environment;
3145 // 1: Asian layout with rotated CJK characters
3146 // 5: Asian layout
3147 // 3: Western layout rotated by 90 degrees
3148 // 4: Western layout
3149 switch (nCode)
3151 default:
3152 OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield");
3153 [[fallthrough]];
3154 case 3:
3155 eDir = SvxFrameDirection::Vertical_LR_BT;
3156 break;
3157 case 5:
3158 eDir = SvxFrameDirection::Vertical_RL_TB;
3159 break;
3160 case 1:
3161 eDir = SvxFrameDirection::Vertical_RL_TB;
3162 break;
3163 case 4:
3164 eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables
3165 break;
3167 return eDir;
3170 void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx)
3172 if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
3173 return;
3174 SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR);
3175 pBox->GetFrameFormat()->SetFormatAttr(aItem);
3178 void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
3180 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3181 return;
3183 sal_Int16 eVertOri=text::VertOrientation::TOP;
3185 if( m_pActBand->pTCs )
3187 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3188 switch (pT->nVertAlign)
3190 case 0:
3191 default:
3192 eVertOri = text::VertOrientation::TOP;
3193 break;
3194 case 1:
3195 eVertOri = text::VertOrientation::CENTER;
3196 break;
3197 case 2:
3198 eVertOri = text::VertOrientation::BOTTOM;
3199 break;
3203 pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
3206 void WW8TabDesc::AdjustNewBand()
3208 if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
3209 InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols );
3211 SetPamInCell( 0, false);
3212 OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == o3tl::narrowing<sal_uInt16>(m_pActBand->nSwCols),
3213 "Wrong column count in table" );
3215 if( m_bClaimLineFormat )
3217 m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
3218 SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default
3220 if (m_pActBand->nLineHeight == 0) // 0 = Auto
3221 aF.SetHeightSizeType( SwFrameSize::Variable );
3222 else
3224 if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
3226 aF.SetHeightSizeType(SwFrameSize::Fixed);
3227 m_pActBand->nLineHeight = -m_pActBand->nLineHeight;
3229 if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
3230 m_pActBand->nLineHeight = MINLAY;
3232 aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
3234 m_pTabLine->GetFrameFormat()->SetFormatAttr(aF);
3237 //Word stores 1 for bCantSplit if the row cannot be split, we set true if
3238 //we can split the row
3239 bool bSetCantSplit = m_pActBand->bCantSplit;
3240 m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit));
3242 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
3243 if (bSetCantSplit && m_pTabLines->size() == 1)
3244 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit(false));
3246 short i; // SW-Index
3247 short j; // WW-Index
3248 short nW; // Width
3249 SwFormatFrameSize aFS( SwFrameSize::Fixed );
3250 j = m_pActBand->bLEmptyCol ? -1 : 0;
3252 for( i = 0; i < m_pActBand->nSwCols; i++ )
3254 // set cell width
3255 if( j < 0 )
3256 nW = m_pActBand->nCenter[0] - m_nMinLeft;
3257 else
3259 //Set j to first non invalid cell
3260 while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
3261 j++;
3263 if( j < m_pActBand->nWwCols )
3264 nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3265 else
3266 nW = m_nMaxRight - m_pActBand->nCenter[j];
3267 m_pActBand->nWidth[ j ] = nW;
3270 SwTableBox* pBox = (*m_pTabBoxes)[i];
3271 // could be reduced further by intelligent moving of FrameFormats
3272 pBox->ClaimFrameFormat();
3274 SetTabBorders(pBox, j);
3276 SvxBoxItem aCurrentBox(sw::util::ItemGet<SvxBoxItem>(*(pBox->GetFrameFormat()), RES_BOX));
3277 pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
3279 SetTabVertAlign(pBox, j);
3280 SetTabDirection(pBox, j);
3281 if( m_pActBand->pSHDs || m_pActBand->pNewSHDs)
3282 SetTabShades(pBox, j);
3283 j++;
3285 aFS.SetWidth( nW );
3286 pBox->GetFrameFormat()->SetFormatAttr( aFS );
3288 // skip non existing cells
3289 while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
3291 m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3292 j++;
3297 void WW8TabDesc::TableCellEnd()
3299 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
3301 // new line/row
3302 if( m_pIo->m_bWasTabRowEnd )
3304 // bWasTabRowEnd will be deactivated in
3305 // SwWW8ImplReader::ProcessSpecial()
3307 sal_uInt16 iCol = GetLogicalWWCol();
3308 if (iCol < m_aNumRuleNames.size())
3310 m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
3311 m_aNumRuleNames.end());
3314 m_nCurrentCol = 0;
3315 m_nCurrentRow++;
3316 m_nCurrentBandRow++;
3317 OSL_ENSURE( m_pActBand , "pActBand is 0" );
3318 if( m_pActBand )
3320 if( m_nCurrentRow >= m_nRows ) // nothing to at end of table
3321 return;
3323 bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows;
3324 if( bNewBand )
3325 { // new band needed ?
3326 m_pActBand = m_pActBand->pNextBand;
3327 m_nCurrentBandRow = 0;
3328 OSL_ENSURE( m_pActBand, "pActBand is 0" );
3329 AdjustNewBand();
3331 else
3333 SwTableBox* pBox = (*m_pTabBoxes)[0];
3334 SwSelBoxes aBoxes;
3335 m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
3339 else
3340 { // new column ( cell )
3341 m_nCurrentCol++;
3343 SetPamInCell(m_nCurrentCol, true);
3345 // finish Annotated Level Numbering ?
3346 if (m_pIo->m_bAnl && !m_pIo->m_bCurrentAND_fNumberAcross && m_pActBand)
3347 m_pIo->StopAllAnl(IsValidCell(m_nCurrentCol));
3350 // if necessary register the box for the merge group for this column
3351 void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell const & rCell,
3352 WW8SelBoxInfo* pActGroup,
3353 SwTableBox* pActBox,
3354 sal_uInt16 nCol )
3356 // check if the box has to be merged
3357 // If cell is the first one to be merged, a new merge group has to be provided.
3358 // E.g., it could be that a cell is the first one to be merged, but no
3359 // new merge group is provided, because the potential other cell to be merged
3360 // doesn't exist - see method <WW8TabDesc::MergeCells>.
3361 if ( !(m_pActBand->bExist[ nCol ] &&
3362 ( ( rCell.bFirstMerged && pActGroup ) ||
3363 rCell.bMerged ||
3364 rCell.bVertMerge ||
3365 rCell.bVertRestart )) )
3366 return;
3368 // detect appropriate merge group
3369 WW8SelBoxInfo* pTheMergeGroup = nullptr;
3370 if( pActGroup )
3371 // assign group
3372 pTheMergeGroup = pActGroup;
3373 else
3375 // find group
3376 pTheMergeGroup = FindMergeGroup(
3377 m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
3379 if( pTheMergeGroup )
3381 // add current box to merge group
3382 pTheMergeGroup->push_back(pActBox);
3386 sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
3388 sal_uInt16 nCol = 0;
3389 if( m_pActBand && m_pActBand->pTCs)
3391 for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol )
3393 if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
3394 ++nCol;
3397 return nCol;
3400 // find name of numrule valid for current WW-COL
3401 OUString WW8TabDesc::GetNumRuleName() const
3403 sal_uInt16 nCol = GetLogicalWWCol();
3404 if (nCol < m_aNumRuleNames.size())
3405 return m_aNumRuleNames[nCol];
3406 return OUString();
3409 void WW8TabDesc::SetNumRuleName( const OUString& rName )
3411 sal_uInt16 nCol = GetLogicalWWCol();
3412 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
3413 m_aNumRuleNames.emplace_back();
3414 m_aNumRuleNames[nCol] = rName;
3417 bool SwWW8ImplReader::StartTable(WW8_CP nStartCp)
3419 // Entering a table so make sure the FirstPara flag gets set
3420 m_bFirstPara = true;
3421 // no recursive table, not with InsertFile in table or foot note
3422 if (m_bReadNoTable)
3423 return false;
3425 if (m_xTableDesc)
3426 m_aTableStack.push(std::move(m_xTableDesc));
3428 // #i33818# - determine absolute position object attributes,
3429 // if possible. It's needed for nested tables.
3430 std::unique_ptr<WW8FlyPara> pTableWFlyPara;
3431 WW8SwFlyPara* pTableSFlyPara( nullptr );
3432 // #i45301# - anchor nested table inside Writer fly frame
3433 // only at-character, if absolute position object attributes are available.
3434 // Thus, default anchor type is as-character anchored.
3435 RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR );
3436 if ( m_nInTable )
3438 WW8_TablePos* pNestedTabPos( nullptr );
3439 WW8_TablePos aNestedTabPos;
3440 WW8PLCFxSave1 aSave;
3441 m_xPlcxMan->GetPap()->Save( aSave );
3442 WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
3443 WW8_CP nMyStartCp = nStartCp;
3444 if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
3445 ParseTabPos( &aNestedTabPos, pPap ) )
3447 pNestedTabPos = &aNestedTabPos;
3449 m_xPlcxMan->GetPap()->Restore( aSave );
3450 if ( pNestedTabPos )
3452 ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
3453 pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
3454 if ( pTableWFlyPara )
3456 // <WW8SwFlyPara> constructor has changed - new 4th parameter
3457 // containing WW8 page top margin.
3458 pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
3459 m_aSectionManager.GetWWPageTopMargin(),
3460 m_aSectionManager.GetTextAreaWidth(),
3461 m_nIniFlyDx, m_nIniFlyDy);
3463 // #i45301# - anchor nested table Writer fly frame at-character
3464 eAnchor = RndStdIds::FLY_AT_CHAR;
3468 // if first paragraph in table has break-before-page, transfer that setting to the table itself.
3469 else if( StyleExists(m_nCurrentColl) )
3471 const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat;
3472 if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
3473 NewAttr( pStyleFormat->GetBreak() );
3476 m_xTableDesc.reset(new WW8TabDesc(this, nStartCp));
3478 if( m_xTableDesc->Ok() )
3480 int nNewInTable = m_nInTable + 1;
3482 if ((eAnchor == RndStdIds::FLY_AT_CHAR)
3483 && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
3485 m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
3486 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aItemSet(m_rDoc.GetAttrPool());
3487 // #i33818# - anchor the Writer fly frame for the nested table at-character.
3488 // #i45301#
3489 SwFormatAnchor aAnchor( eAnchor );
3490 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3491 aItemSet.Put( aAnchor );
3492 m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
3493 m_xTableDesc->m_pParentPos, &aItemSet);
3494 OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
3495 "Not the anchor type requested!" );
3496 MoveInsideFly(m_xTableDesc->m_pFlyFormat);
3498 m_xTableDesc->CreateSwTable();
3499 if (m_xTableDesc->m_pFlyFormat)
3501 m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat);
3502 // #i33818# - Use absolute position object attributes,
3503 // if existing, and apply them to the created Writer fly frame.
3504 if ( pTableWFlyPara && pTableSFlyPara )
3506 WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false );
3507 SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
3508 aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
3509 aFlySet.Put( aAnchor );
3510 m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
3512 else
3514 SwFormatHoriOrient aHori =
3515 m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
3516 m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
3517 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) );
3519 // #i33818# - The nested table doesn't have to leave
3520 // the table cell. Thus, the Writer fly frame has to follow the text flow.
3521 m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
3523 else
3524 m_xTableDesc->SetSizePosition(nullptr);
3525 m_xTableDesc->UseSwTable();
3527 else
3528 PopTableDesc();
3530 // #i33818#
3531 delete pTableSFlyPara;
3533 return m_xTableDesc != nullptr;
3536 void SwWW8ImplReader::TabCellEnd()
3538 if (m_nInTable && m_xTableDesc)
3539 m_xTableDesc->TableCellEnd();
3541 m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
3542 m_bReadTable = false;
3545 void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
3547 if( ( nLen > 0 ) && ( *pData == 1 ) )
3548 m_bWasTabCellEnd = true;
3551 void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
3553 if( ( nLen > 0 ) && ( *pData == 1 ) )
3554 m_bWasTabRowEnd = true;
3557 void SwWW8ImplReader::PopTableDesc()
3559 if (m_xTableDesc && m_xTableDesc->m_pFlyFormat)
3561 MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos);
3564 m_xTableDesc.reset();
3565 if (!m_aTableStack.empty())
3567 m_xTableDesc = std::move(m_aTableStack.top());
3568 m_aTableStack.pop();
3572 void SwWW8ImplReader::StopTable()
3574 OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!");
3575 if (!m_xTableDesc)
3576 return;
3578 // We are leaving a table so make sure the next paragraph doesn't think
3579 // it's the first paragraph
3580 m_bFirstPara = false;
3582 m_xTableDesc->FinishSwTable();
3583 PopTableDesc();
3585 m_bReadTable = true;
3588 bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const
3590 if( !m_xTableDesc )
3591 return false;
3593 const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell();
3595 return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() )
3596 || ( pCell
3597 && ( !pCell->bFirstMerged
3598 && ( pCell->bMerged
3599 || ( pCell->bVertMerge
3600 && !pCell->bVertRestart
3607 sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
3609 sal_uInt16 nRes = USHRT_MAX;
3610 if( !m_vColl.empty() )
3612 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3613 if( m_vColl[ nI ].m_bValid
3614 && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
3615 nRes = nI;
3617 return nRes;
3620 const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( std::u16string_view rName ) const
3622 SwFormat* pRet = nullptr;
3623 if( !m_vColl.empty() )
3625 for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
3626 if( m_vColl[ nI ].m_bValid
3627 && (rName == m_vColl[ nI ].GetOrgWWName()) )
3629 pRet = m_vColl[ nI ].m_pFormat;
3630 break;
3633 return pRet;
3637 SprmResult WW8RStyle::HasParaSprm(sal_uInt16 nId) const
3639 if( !mpParaSprms || !mnSprmsLen )
3640 return SprmResult();
3642 return maSprmParser.findSprmData(nId, mpParaSprms, mnSprmsLen);
3645 void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
3647 if (!nLen)
3648 return;
3650 if( bPap )
3652 mpParaSprms = pSprms; // for HasParaSprms()
3653 mnSprmsLen = nLen;
3656 WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
3657 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
3659 #ifdef DEBUGSPRMREADER
3660 fprintf(stderr, "id is %x\n", aIter.GetCurrentId());
3661 #endif
3662 mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
3663 aSprmIter.advance();
3666 mpParaSprms = nullptr;
3667 mnSprmsLen = 0;
3670 void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
3672 if (!nLen)
3673 return;
3675 if (checkSeek(*mpStStrm, nPosFc))
3677 std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
3678 nLen = mpStStrm->ReadBytes(pSprms.get(), nLen);
3679 ImportSprms(pSprms.get(), nLen, bPap);
3683 static short WW8SkipOdd(SvStream* pSt )
3685 if ( pSt->Tell() & 0x1 )
3687 sal_uInt8 c;
3688 return pSt->ReadBytes( &c, 1 );
3690 return 0;
3693 static short WW8SkipEven(SvStream* pSt )
3695 if (!(pSt->Tell() & 0x1))
3697 sal_uInt8 c;
3698 return pSt->ReadBytes( &c, 1 );
3700 return 0;
3703 short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
3705 if( 0 < nLen ) // Empty ?
3707 if (bOdd)
3708 nLen = nLen - WW8SkipEven( mpStStrm );
3709 else
3710 nLen = nLen - WW8SkipOdd( mpStStrm );
3712 sal_Int16 cbUPX(0);
3713 mpStStrm->ReadInt16( cbUPX );
3715 nLen-=2;
3717 if ( cbUPX > nLen )
3718 cbUPX = nLen; // shrink cbUPX to nLen
3720 if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
3722 if( bPAP )
3724 sal_uInt16 id;
3725 mpStStrm->ReadUInt16( id );
3727 cbUPX-= 2;
3728 nLen-= 2;
3731 if( 0 < cbUPX )
3733 sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong,
3734 // this should make it work again
3735 ImportSprms( nPos, cbUPX, bPAP );
3737 if ( mpStStrm->Tell() != nPos + cbUPX )
3738 mpStStrm->Seek( nPos+cbUPX );
3740 nLen = nLen - cbUPX;
3744 return nLen;
3747 void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
3749 if( nLen <= 0 )
3750 return;
3751 if (bOdd)
3752 nLen = nLen - WW8SkipEven( mpStStrm );
3753 else
3754 nLen = nLen - WW8SkipOdd( mpStStrm );
3756 if( bPara ) // Grupx.Papx
3757 nLen = ImportUPX(nLen, true, bOdd);
3758 ImportUPX(nLen, false, bOdd); // Grupx.Chpx
3761 WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI)
3762 : WW8Style(*pI->m_pTableStream, _rFib)
3763 , maSprmParser(_rFib)
3764 , mpIo(pI)
3765 , mpStStrm(pI->m_pTableStream)
3766 , mpStyRule(nullptr)
3767 , mpParaSprms(nullptr)
3768 , mnSprmsLen(0)
3769 , mnWwNumLevel(0)
3770 , mbTextColChanged(false)
3771 , mbFontChanged(false)
3772 , mbCJKFontChanged(false)
3773 , mbCTLFontChanged(false)
3774 , mbFSizeChanged(false)
3775 , mbFCTLSizeChanged(false)
3776 , mbWidowsChanged(false)
3777 , mbBidiChanged(false)
3779 mpIo->m_vColl.resize(m_cstd);
3782 void WW8RStyle::Set1StyleDefaults()
3784 // see #i25247#, #i25561#, #i48064#, #i92341# for default font
3785 if (!mbCJKFontChanged) // Style no CJK Font? set the default
3786 mpIo->SetNewFontAttr(m_ftcFE, true, RES_CHRATR_CJK_FONT);
3788 if (!mbCTLFontChanged) // Style no CTL Font? set the default
3789 mpIo->SetNewFontAttr(m_ftcBi, true, RES_CHRATR_CTL_FONT);
3791 // western 2nd to make western charset conversion the default
3792 if (!mbFontChanged) // Style has no Font? set the default,
3793 mpIo->SetNewFontAttr(m_ftcAsci, true, RES_CHRATR_FONT);
3795 if( mpIo->m_bNoAttrImport )
3796 return;
3798 // Style has no text color set, winword default is auto
3799 if ( !mbTextColChanged )
3800 mpIo->m_pCurrentColl->SetFormatAttr(SvxColorItem(COL_AUTO, RES_CHRATR_COLOR));
3802 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3803 if( !mbFSizeChanged )
3805 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3806 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3807 aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
3808 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3811 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3812 if( !mbFCTLSizeChanged )
3814 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3815 aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
3816 mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
3819 if( !mbWidowsChanged ) // Widows ?
3821 mpIo->m_pCurrentColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) );
3822 mpIo->m_pCurrentColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) );
3825 // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of
3826 // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr
3827 if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style
3829 mpIo->m_pCurrentColl->SetFormatAttr(
3830 SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
3834 bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle, sal_uInt16 nNextStyle)
3836 SwFormat* pColl;
3837 bool bStyExist;
3839 if (rSI.m_bColl)
3841 // Para-Style
3842 sw::util::ParaStyleMapper::StyleResult aResult =
3843 mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti);
3844 pColl = aResult.first;
3845 bStyExist = aResult.second;
3847 else
3849 // Char-Style
3850 sw::util::CharStyleMapper::StyleResult aResult =
3851 mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti);
3852 pColl = aResult.first;
3853 bStyExist = aResult.second;
3856 bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ?
3858 // Do not override character styles the list import code created earlier.
3859 if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
3860 bImport = false;
3862 bool bOldNoImp = mpIo->m_bNoAttrImport;
3863 rSI.m_bImportSkipped = !bImport;
3865 if( !bImport )
3866 mpIo->m_bNoAttrImport = true;
3867 else
3869 if (bStyExist)
3871 pColl->ResetAllFormatAttr(); // #i73790# - method renamed
3873 pColl->SetAuto(false); // suggested by JP
3874 } // but changes the UI
3875 mpIo->m_pCurrentColl = pColl;
3876 rSI.m_pFormat = pColl; // remember translation WW->SW
3877 rSI.m_bImportSkipped = !bImport;
3879 // Set Based on style
3880 sal_uInt16 j = rSI.m_nBase;
3881 if (j != nThisStyle && j < m_cstd )
3883 SwWW8StyInf* pj = &mpIo->m_vColl[j];
3884 if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
3886 rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
3887 rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet;
3888 rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet;
3889 rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet;
3890 rSI.m_n81Flags = pj->m_n81Flags;
3891 rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags;
3892 if (!rSI.IsWW8BuiltInHeadingStyle())
3894 rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel;
3896 rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore;
3897 rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter;
3899 if (pj->m_xWWFly)
3900 rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get());
3903 else if( mpIo->m_bNewDoc && bStyExist )
3904 rSI.m_pFormat->SetDerivedFrom();
3906 rSI.m_nFollow = nNextStyle; // remember Follow
3908 mpStyRule = nullptr; // recreate if necessary
3909 mbTextColChanged = mbFontChanged = mbCJKFontChanged = mbCTLFontChanged =
3910 mbFSizeChanged = mbFCTLSizeChanged = mbWidowsChanged = false;
3911 mpIo->SetNCurrentColl( nThisStyle );
3912 mpIo->m_bStyNormal = nThisStyle == 0;
3913 return bOldNoImp;
3916 void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp)
3918 // Reset attribute flags, because there are no style-ends.
3920 mpIo->m_bHasBorder = mpIo->m_bSpec = mpIo->m_bObj = mpIo->m_bSymbol = false;
3921 mpIo->m_nCharFormat = -1;
3923 // if style is based on nothing or base ignored
3924 if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
3926 // If Char-Styles does not work
3927 // -> set hard WW-Defaults
3928 Set1StyleDefaults();
3931 mpStyRule = nullptr; // to be on the safe side
3932 mpIo->m_bStyNormal = false;
3933 mpIo->SetNCurrentColl( 0 );
3934 mpIo->m_bNoAttrImport = bOldNoImp;
3935 // reset the list-remember-fields, if used when reading styles
3936 mpIo->m_nLFOPosition = USHRT_MAX;
3937 mpIo->m_nListLevel = MAXLEVEL;
3940 void WW8RStyle::Import1Style( sal_uInt16 nNr )
3942 if (nNr >= mpIo->m_vColl.size())
3943 return;
3945 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
3947 if( rSI.m_bImported || !rSI.m_bValid )
3948 return;
3950 rSI.m_bImported = true; // set flag now to avoid endless loops
3952 // valid and not NUL and not yet imported
3954 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
3955 Import1Style( rSI.m_nBase );
3957 mpStStrm->Seek( rSI.m_nFilePos );
3959 sal_uInt16 nSkip;
3960 OUString sName;
3962 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName));// read Style
3964 if (xStd)
3965 rSI.SetOrgWWIdent( sName, xStd->sti );
3967 // either no Name or unused Slot or unknown Style
3969 if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
3971 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
3972 mpStStrm->Seek(mpStStrm->Tell() + nSkip);
3973 return;
3976 bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti), nNr, xStd->istdNext);
3978 // if something is interpreted wrong, this should make it work again
3979 tools::Long nPos = mpStStrm->Tell();
3981 //Variable parts of the STD start at even byte offsets, but "inside
3982 //the STD", which I take to meaning even in relation to the starting
3983 //position of the STD, which matches findings in #89439#, generally it
3984 //doesn't matter as the STSHI starts off nearly always on an even
3985 //offset
3987 //Import of the Style Contents
3988 ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
3990 PostStyle(rSI, bOldNoImp);
3992 mpStStrm->Seek( nPos+nSkip );
3995 void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
3997 if (nNr >= mpIo->m_vColl.size())
3998 return;
4000 SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
4001 if( rSI.m_bImported || !rSI.m_bValid )
4002 return;
4004 rSI.m_bImported = true;
4006 if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
4007 RecursiveReg(rSI.m_nBase);
4009 mpIo->RegisterNumFormatOnStyle(nNr);
4014 After all styles are imported then we can recursively apply numbering
4015 styles to them, and change their tab stop settings if they turned out
4016 to have special first line indentation.
4018 void WW8RStyle::PostProcessStyles()
4020 sal_uInt16 i;
4022 Clear all imported flags so that we can recursively apply numbering
4023 formats and use it to mark handled ones
4025 for (i=0; i < m_cstd; ++i)
4026 mpIo->m_vColl[i].m_bImported = false;
4029 Register the num formats and tabstop changes on the styles recursively.
4033 In the same loop apply the tabstop changes required because we need to
4034 change their location if there's a special indentation for the first line,
4035 By avoiding making use of each styles margins during reading of their
4036 tabstops we don't get problems with doubly adjusting tabstops that
4037 are inheritied.
4039 for (i=0; i < m_cstd; ++i)
4041 if (mpIo->m_vColl[i].m_bValid)
4043 RecursiveReg(i);
4048 void WW8RStyle::ScanStyles() // investigate style dependencies
4049 { // and detect Filepos for each Style
4050 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4052 SwWW8StyInf &rSI = mpIo->m_vColl[i];
4054 rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos
4055 sal_uInt16 nSkip;
4056 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD
4057 rSI.m_bValid = xStd != nullptr;
4058 if (rSI.m_bValid)
4060 rSI.m_nBase = xStd->istdBase; // remember Basis
4061 rSI.m_bColl = xStd->sgc == 1; // Para-Style
4063 else
4064 rSI = SwWW8StyInf();
4066 xStd.reset();
4067 nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
4068 mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms
4072 std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
4074 std::vector<sal_uInt8> aRet
4077 static_cast< sal_uInt8 >(128 + rChpx.fBold),
4080 static_cast< sal_uInt8 >(128 + rChpx.fItalic),
4083 static_cast< sal_uInt8 >(128 + rChpx.fStrike),
4086 static_cast< sal_uInt8 >(128 + rChpx.fOutline),
4089 static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps),
4092 static_cast< sal_uInt8 >(128 + rChpx.fCaps),
4095 static_cast< sal_uInt8 >(128 + rChpx.fVanish)
4097 if (rChpx.fsFtc)
4099 aRet.push_back(68);
4100 SVBT16 a;
4101 ShortToSVBT16(rChpx.ftc, a);
4102 aRet.push_back(a[1]);
4103 aRet.push_back(a[0]);
4106 if (rChpx.fsKul)
4108 aRet.push_back(69);
4109 aRet.push_back(rChpx.kul);
4112 if (rChpx.fsLid)
4114 aRet.push_back(72);
4115 SVBT16 a;
4116 ShortToSVBT16(rChpx.lid, a);
4117 aRet.push_back(a[1]);
4118 aRet.push_back(a[0]);
4121 if (rChpx.fsIco)
4123 aRet.push_back(73);
4124 aRet.push_back(rChpx.ico);
4127 if (rChpx.fsHps)
4129 aRet.push_back(74);
4131 SVBT16 a;
4132 ShortToSVBT16(rChpx.hps, a);
4133 aRet.push_back(a[0]);
4136 if (rChpx.fsPos)
4138 aRet.push_back(76);
4139 aRet.push_back(rChpx.hpsPos);
4142 aRet.push_back(80);
4143 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
4145 aRet.push_back(81);
4146 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
4148 if (rChpx.fsFtcBi)
4150 aRet.push_back(82);
4151 SVBT16 a;
4152 ShortToSVBT16(rChpx.fsFtcBi, a);
4153 aRet.push_back(a[1]);
4154 aRet.push_back(a[0]);
4157 if (rChpx.fsLidBi)
4159 aRet.push_back(83);
4160 SVBT16 a;
4161 ShortToSVBT16(rChpx.lidBi, a);
4162 aRet.push_back(a[1]);
4163 aRet.push_back(a[0]);
4166 if (rChpx.fsIcoBi)
4168 aRet.push_back(84);
4169 aRet.push_back(rChpx.icoBi);
4172 if (rChpx.fsHpsBi)
4174 aRet.push_back(85);
4175 SVBT16 a;
4176 ShortToSVBT16(rChpx.hpsBi, a);
4177 aRet.push_back(a[1]);
4178 aRet.push_back(a[0]);
4181 return aRet;
4184 Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
4186 Word2CHPX aChpx;
4188 if (!nSize || !checkSeek(rSt, nOffset))
4189 return aChpx;
4191 const size_t nMaxByteCount = rSt.remainingSize();
4192 if (!nMaxByteCount)
4193 return aChpx;
4195 if (nSize > nMaxByteCount)
4197 SAL_WARN("sw.ww8", "ReadWord2Chpx: truncating out of range "
4198 << nSize << " to " << nMaxByteCount);
4199 nSize = nMaxByteCount;
4202 sal_uInt8 nCount=0;
4204 while (true)
4206 sal_uInt8 nFlags8;
4207 rSt.ReadUChar( nFlags8 );
4208 nCount++;
4210 if (!rSt.good())
4211 break;
4213 aChpx.fBold = nFlags8 & 0x01;
4214 aChpx.fItalic = (nFlags8 & 0x02) >> 1;
4215 aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
4216 aChpx.fOutline = (nFlags8 & 0x08) >> 3;
4217 aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
4218 aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
4219 aChpx.fCaps = (nFlags8 & 0x40) >> 6;
4220 aChpx.fVanish = (nFlags8 & 0x80) >> 7;
4222 if (nCount >= nSize) break;
4223 rSt.ReadUChar( nFlags8 );
4224 nCount++;
4226 if (!rSt.good())
4227 break;
4229 aChpx.fRMark = nFlags8 & 0x01;
4230 aChpx.fSpec = (nFlags8 & 0x02) >> 1;
4231 aChpx.fStrike = (nFlags8 & 0x04) >> 2;
4232 aChpx.fObj = (nFlags8 & 0x08) >> 3;
4233 aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
4234 aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
4235 aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
4236 aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
4238 if (nCount >= nSize) break;
4239 rSt.ReadUChar( nFlags8 );
4240 nCount++;
4242 if (!rSt.good())
4243 break;
4245 aChpx.fsIco = nFlags8 & 0x01;
4246 aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
4247 aChpx.fsHps = (nFlags8 & 0x04) >> 2;
4248 aChpx.fsKul = (nFlags8 & 0x08) >> 3;
4249 aChpx.fsPos = (nFlags8 & 0x10) >> 4;
4250 aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
4251 aChpx.fsLid = (nFlags8 & 0x40) >> 6;
4252 aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
4254 if (nCount >= nSize) break;
4255 rSt.ReadUChar( nFlags8 );
4256 nCount++;
4258 if (!rSt.good())
4259 break;
4261 aChpx.fsFtcBi = nFlags8 & 0x01;
4262 aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
4263 aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
4265 if (nCount >= nSize) break;
4266 rSt.ReadUInt16( aChpx.ftc );
4267 nCount+=2;
4269 if (nCount >= nSize) break;
4270 rSt.ReadUInt16( aChpx.hps );
4271 nCount+=2;
4273 if (nCount >= nSize) break;
4274 rSt.ReadUChar( nFlags8 );
4275 nCount++;
4277 if (!rSt.good())
4278 break;
4280 aChpx.qpsSpace = nFlags8 & 0x3F;
4281 aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
4282 aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
4284 if (nCount >= nSize) break;
4285 rSt.ReadUChar( nFlags8 );
4286 nCount++;
4288 if (!rSt.good())
4289 break;
4291 aChpx.ico = nFlags8 & 0x1F;
4292 aChpx.kul = (nFlags8 & 0xE0) >> 5;
4294 if (nCount >= nSize) break;
4295 rSt.ReadUChar( aChpx.hpsPos );
4296 nCount++;
4298 if (nCount >= nSize) break;
4299 rSt.ReadUChar( aChpx.icoBi );
4300 nCount++;
4302 if (nCount >= nSize) break;
4303 rSt.ReadUInt16( aChpx.lid );
4304 nCount+=2;
4306 if (nCount >= nSize) break;
4307 rSt.ReadUInt16( aChpx.ftcBi );
4308 nCount+=2;
4310 if (nCount >= nSize) break;
4311 rSt.ReadUInt16( aChpx.hpsBi );
4312 nCount+=2;
4314 if (nCount >= nSize) break;
4315 rSt.ReadUInt16( aChpx.lidBi );
4316 nCount+=2;
4318 if (nCount >= nSize) break;
4319 rSt.ReadUInt32( aChpx.fcPic );
4320 nCount+=4;
4322 break;
4325 rSt.SeekRel(nSize-nCount);
4326 return aChpx;
4329 namespace
4331 struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
4334 void WW8RStyle::ImportOldFormatStyles()
4336 for (sal_uInt16 i=0; i < m_cstd; ++i)
4338 mpIo->m_vColl[i].m_bColl = true;
4339 //every chain must end eventually at the null style (style code 222)
4340 mpIo->m_vColl[i].m_nBase = 222;
4343 rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
4344 mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid);
4346 sal_uInt16 cstcStd(0);
4347 m_rStream.ReadUInt16( cstcStd );
4349 size_t nMaxByteCount = m_rStream.remainingSize();
4350 sal_uInt16 cbName(0);
4351 m_rStream.ReadUInt16(cbName);
4352 if (cbName > nMaxByteCount)
4354 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4355 << cbName << " to " << nMaxByteCount);
4356 cbName = nMaxByteCount;
4358 sal_uInt16 nByteCount = 2;
4359 sal_uInt16 stcp=0;
4360 while (nByteCount < cbName)
4362 sal_uInt8 nCount(0);
4363 m_rStream.ReadUChar( nCount );
4364 nByteCount++;
4366 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4367 if (stc >=mpIo->m_vColl.size())
4368 continue;
4370 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4371 OUString sName;
4373 if (nCount != 0xFF) // undefined style
4375 if (nCount != 0) // user style
4377 OString aTmp = read_uInt8s_ToOString(m_rStream, nCount);
4378 nByteCount += aTmp.getLength();
4379 sName = OStringToOUString(aTmp, eStructChrSet);
4381 rSI.m_bImported = true;
4384 if (sName.isEmpty())
4386 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4387 if (const char *pStr = GetEnglishNameFromSti(eSti))
4388 sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
4391 if (sName.isEmpty())
4392 sName = "Unknown Style: " + OUString::number(stc);
4394 rSI.SetOrgWWIdent(sName, stc);
4395 stcp++;
4398 sal_uInt16 nStyles=stcp;
4400 std::vector<pxoffset> aCHPXOffsets(stcp);
4401 nMaxByteCount = m_rStream.remainingSize();
4402 sal_uInt16 cbChpx(0);
4403 m_rStream.ReadUInt16(cbChpx);
4404 if (cbChpx > nMaxByteCount)
4406 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4407 << cbChpx << " to " << nMaxByteCount);
4408 cbChpx = nMaxByteCount;
4410 nByteCount = 2;
4411 stcp=0;
4412 std::vector< std::vector<sal_uInt8> > aConvertedChpx;
4413 while (nByteCount < cbChpx)
4415 if (stcp == aCHPXOffsets.size())
4417 //more data than style slots, skip remainder
4418 m_rStream.SeekRel(cbChpx-nByteCount);
4419 break;
4422 sal_uInt8 cb(0);
4423 m_rStream.ReadUChar( cb );
4424 nByteCount++;
4426 aCHPXOffsets[stcp].mnSize = 0;
4428 if (cb != 0xFF)
4430 sal_uInt8 nRemainder = cb;
4432 aCHPXOffsets[stcp].mnOffset = m_rStream.Tell();
4433 aCHPXOffsets[stcp].mnSize = nRemainder;
4435 Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset,
4436 aCHPXOffsets[stcp].mnSize);
4437 aConvertedChpx.push_back( ChpxToSprms(aChpx) );
4439 nByteCount += nRemainder;
4441 else
4442 aConvertedChpx.emplace_back( );
4444 ++stcp;
4447 std::vector<pxoffset> aPAPXOffsets(stcp);
4448 nMaxByteCount = m_rStream.remainingSize();
4449 sal_uInt16 cbPapx(0);
4450 m_rStream.ReadUInt16(cbPapx);
4451 if (cbPapx > nMaxByteCount)
4453 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4454 << cbPapx << " to " << nMaxByteCount);
4455 cbPapx = nMaxByteCount;
4457 nByteCount = 2;
4458 stcp=0;
4459 while (nByteCount < cbPapx)
4461 if (stcp == aPAPXOffsets.size())
4463 m_rStream.SeekRel(cbPapx-nByteCount);
4464 break;
4467 sal_uInt8 cb(0);
4468 m_rStream.ReadUChar( cb );
4469 nByteCount++;
4471 aPAPXOffsets[stcp].mnSize = 0;
4473 if (cb != 0xFF)
4475 sal_uInt8 stc2(0);
4476 m_rStream.ReadUChar( stc2 );
4477 m_rStream.SeekRel(6);
4478 nByteCount+=7;
4479 sal_uInt8 nRemainder = cb-7;
4481 aPAPXOffsets[stcp].mnOffset = m_rStream.Tell();
4482 aPAPXOffsets[stcp].mnSize = nRemainder;
4484 m_rStream.SeekRel(nRemainder);
4485 nByteCount += nRemainder;
4488 ++stcp;
4491 sal_uInt16 iMac(0);
4492 m_rStream.ReadUInt16( iMac );
4494 if (iMac > nStyles) iMac = nStyles;
4496 for (stcp = 0; stcp < iMac; ++stcp)
4498 sal_uInt8 stcNext(0), stcBase(0);
4499 m_rStream.ReadUChar( stcNext );
4500 m_rStream.ReadUChar( stcBase );
4502 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4505 #i64557# style based on itself
4506 every chain must end eventually at the null style (style code 222)
4508 if (stc == stcBase)
4509 stcBase = 222;
4511 SwWW8StyInf &rSI = mpIo->m_vColl[stc];
4512 rSI.m_nBase = stcBase;
4514 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4516 if (eSti == ww::stiNil)
4517 continue;
4519 if (stcp >= aPAPXOffsets.size())
4520 continue;
4522 rSI.m_bValid = true;
4524 if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
4525 mpIo->m_vColl[stc].m_bColl = false;
4527 bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext);
4529 ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
4530 true);
4532 if (!aConvertedChpx[stcp].empty())
4533 ImportSprms(aConvertedChpx[stcp].data(),
4534 static_cast< short >(aConvertedChpx[stcp].size()),
4535 false);
4537 PostStyle(rSI, bOldNoImp);
4541 void WW8RStyle::ImportNewFormatStyles()
4543 ScanStyles(); // Scan Based On
4545 for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles
4546 if (mpIo->m_vColl[i].m_bValid)
4547 Import1Style( i );
4550 void WW8RStyle::Import()
4552 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4553 mpIo->m_pStandardFormatColl =
4554 mpIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
4556 if( mpIo->m_nIniFlags & WW8FL_NO_STYLES )
4557 return;
4559 if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2)
4560 ImportOldFormatStyles();
4561 else
4562 ImportNewFormatStyles();
4564 for (sal_uInt16 i = 0; i < m_cstd; ++i)
4566 // Follow chain
4567 SwWW8StyInf* pi = &mpIo->m_vColl[i];
4568 sal_uInt16 j = pi->m_nFollow;
4569 if( j < m_cstd )
4571 SwWW8StyInf* pj = &mpIo->m_vColl[j];
4572 if ( j != i // rational Index ?
4573 && pi->m_pFormat // Format ok ?
4574 && pj->m_pFormat // Derived-Format ok ?
4575 && pi->m_bColl // only possible for paragraph templates (WW)
4576 && pj->m_bColl ){ // identical Type ?
4577 static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
4578 *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
4583 // Missing special handling for default character template
4584 // "Absatz-Standardschriftart" ( Style-ID 65 ).
4585 // That is empty by default ( WW6 dt and US ) and not changeable
4586 // via WW-UI so this does not matter.
4587 // This could be done by:
4588 // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
4590 // for e.g. tables an always valid Std-Style is necessary
4592 if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() &&
4593 mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid )
4594 mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat);
4595 else
4596 mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
4598 // set Hyphenation flag on BASIC para-style
4599 if (mpIo->m_bNewDoc && mpIo->m_pStandardFormatColl)
4601 if (mpIo->m_xWDop->fAutoHyphen
4602 && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState(
4603 RES_PARATR_HYPHENZONE, false) )
4605 SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE);
4606 aAttr.GetMinLead() = 2;
4607 aAttr.GetMinTrail() = 2;
4608 aAttr.GetMaxHyphens() = 0;
4610 mpIo->m_pStandardFormatColl->SetFormatAttr( aAttr );
4614 // we do not read styles anymore:
4615 mpIo->m_pCurrentColl = nullptr;
4618 rtl_TextEncoding SwWW8StyInf::GetCharSet() const
4620 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4621 return m_eRTLFontSrcCharSet;
4622 return m_eLTRFontSrcCharSet;
4625 rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
4627 if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
4628 return m_eRTLFontSrcCharSet;
4629 return m_eCJKFontSrcCharSet;
4632 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */