build fix
[LibreOffice.git] / sw / source / filter / ww8 / ww8par2.cxx
blobefc967099f9c5aa410972024cf21dcf2ebc0ffc3
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>
22 #include <comphelper/string.hxx>
23 #include <tools/solar.h>
24 #include <vcl/vclenum.hxx>
25 #include <vcl/font.hxx>
26 #include <hintids.hxx>
27 #include <editeng/colritem.hxx>
28 #include <editeng/orphitem.hxx>
29 #include <editeng/widwitem.hxx>
30 #include <editeng/brushitem.hxx>
31 #include <editeng/boxitem.hxx>
32 #include <editeng/lrspitem.hxx>
33 #include <editeng/ulspitem.hxx>
34 #include <editeng/fhgtitem.hxx>
35 #include <editeng/hyphenzoneitem.hxx>
36 #include <editeng/frmdiritem.hxx>
37 #include <editeng/langitem.hxx>
38 #include <editeng/charrotateitem.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 <fmtpdsc.hxx>
52 #include <txtftn.hxx>
53 #include <frmfmt.hxx>
54 #include <ftnidx.hxx>
55 #include <fmtftn.hxx>
56 #include <fmtlsplt.hxx>
57 #include <charfmt.hxx>
58 #include <SwStyleNameMapper.hxx>
59 #include <fltshell.hxx>
60 #include <fmtanchr.hxx>
61 #include <fmtrowsplt.hxx>
62 #include <fmtfollowtextflow.hxx>
63 #include <numrule.hxx>
64 #include <sprmids.hxx>
65 #include <wwstyles.hxx>
66 #include "writerhelper.hxx"
67 #include "ww8struc.hxx"
68 #include "ww8par.hxx"
69 #include "ww8par2.hxx"
71 #include <frmatr.hxx>
73 #include <iostream>
74 #include <memory>
76 using namespace ::com::sun::star;
78 // Gets filled in WW8TabDesc::MergeCells().
79 // Algorithm must ensure proper row and column order in WW8SelBoxInfo!
80 class WW8SelBoxInfo
82 private:
83 std::vector<std::vector<SwTableBox*> > m_vRows;
85 WW8SelBoxInfo(WW8SelBoxInfo const&) = delete;
86 WW8SelBoxInfo& operator=(WW8SelBoxInfo const&) = delete;
88 public:
89 short nGroupXStart;
90 short nGroupWidth;
91 bool bGroupLocked;
93 WW8SelBoxInfo(short nXCenter, short nWidth)
94 : nGroupXStart( nXCenter ), nGroupWidth( nWidth ), bGroupLocked(false)
97 size_t size() const
99 size_t nResult = 0;
100 for (auto& it : m_vRows)
101 nResult += it.size();
102 return nResult;
105 size_t rowsCount() const { return m_vRows.size(); }
107 const std::vector<SwTableBox*>& row( size_t nIndex ) { return m_vRows[nIndex]; }
109 void push_back( SwTableBox* pBox )
111 bool bDone = false;
112 for (auto& iRow : m_vRows)
113 if (iRow[0]->GetUpper() == pBox->GetUpper())
115 iRow.push_back(pBox);
116 bDone = true;
117 break;
119 if (!bDone)
121 const size_t sz = m_vRows.size();
122 m_vRows.resize(sz+1);
123 m_vRows[sz].push_back(pBox);
128 WW8TabBandDesc::WW8TabBandDesc()
130 memset(this, 0, sizeof(*this));
131 for (sal_uInt16 & rn : maDirections)
132 rn = 4;
135 WW8TabBandDesc::~WW8TabBandDesc()
137 delete[] pTCs;
138 delete[] pSHDs;
139 delete[] pNewSHDs;
142 class WW8TabDesc
144 std::vector<OUString> m_aNumRuleNames;
145 sw::util::RedlineStack *mpOldRedlineStack;
147 SwWW8ImplReader* m_pIo;
149 WW8TabBandDesc* m_pFirstBand;
150 WW8TabBandDesc* m_pActBand;
152 SwPosition* m_pTmpPos;
154 SwTableNode* m_pTableNd; // table node
155 const SwTableLines* m_pTabLines; // row array of node
156 SwTableLine* m_pTabLine; // current row
157 SwTableBoxes* m_pTabBoxes; // boxes array in current row
158 SwTableBox* m_pTabBox; // current cell
160 std::vector<std::unique_ptr<WW8SelBoxInfo>> m_MergeGroups; // list of all cells to be merged
162 WW8_TCell* m_pAktWWCell;
164 short m_nRows;
165 short m_nDefaultSwCols;
166 short m_nBands;
167 short m_nMinLeft;
168 short m_nConvertedLeft;
169 short m_nMaxRight;
170 short m_nSwWidth;
171 short m_nPreferredWidth;
172 short m_nOrgDxaLeft;
174 bool m_bOk;
175 bool m_bClaimLineFormat;
176 sal_Int16 m_eOri;
177 bool m_bIsBiDi;
178 // 2. common admin info
179 short m_nAktRow;
180 short m_nAktBandRow; // SW: row of current band
181 // 3. admin info for writer
182 short m_nAktCol;
184 sal_uInt16 m_nRowsToRepeat;
186 // 4. methods
188 sal_uInt16 GetLogicalWWCol() const;
189 void SetTabBorders( SwTableBox* pBox, short nIdx );
190 void SetTabShades( SwTableBox* pBox, short nWwIdx );
191 void SetTabVertAlign( SwTableBox* pBox, short nWwIdx );
192 void SetTabDirection( SwTableBox* pBox, short nWwIdx );
193 void CalcDefaults();
194 void SetPamInCell(short nWwCol, bool bPam);
195 void InsertCells( short nIns );
196 void AdjustNewBand();
198 WW8SelBoxInfo* FindMergeGroup(short nX1, short nWidth, bool bExact);
200 // single box - maybe used in a merge group
201 // (the merge groups are processed later at once)
202 void UpdateTableMergeGroup(WW8_TCell& rCell,
203 WW8SelBoxInfo* pActGroup, SwTableBox* pActBox, sal_uInt16 nCol );
204 void StartMiserableHackForUnsupportedDirection(short nWwCol);
205 void EndMiserableHackForUnsupportedDirection(short nWwCol);
207 WW8TabDesc(WW8TabDesc const&) = delete;
208 WW8TabDesc& operator=(WW8TabDesc const&) = delete;
210 public:
211 const SwTable* m_pTable; // table
212 SwPosition* m_pParentPos;
213 SwFlyFrameFormat* m_pFlyFormat;
214 SfxItemSet m_aItemSet;
215 bool IsValidCell(short nCol) const;
216 bool InFirstParaInCell() const;
218 WW8TabDesc( SwWW8ImplReader* pIoClass, WW8_CP nStartCp );
219 bool Ok() const { return m_bOk; }
220 void CreateSwTable();
221 void UseSwTable();
222 void SetSizePosition(SwFrameFormat* pFrameFormat);
223 void TableCellEnd();
224 void MoveOutsideTable();
225 void ParkPaM();
226 void FinishSwTable();
227 void MergeCells();
228 short GetMinLeft() const { return m_nConvertedLeft; }
229 ~WW8TabDesc();
231 const WW8_TCell* GetAktWWCell() const { return m_pAktWWCell; }
232 short GetAktCol() const { return m_nAktCol; }
233 // find name of numrule valid for current WW-COL
234 OUString GetNumRuleName() const;
235 void SetNumRuleName( const OUString& rName );
237 sw::util::RedlineStack* getOldRedlineStack(){ return mpOldRedlineStack; }
240 void sw::util::RedlineStack::close( const SwPosition& rPos,
241 RedlineType_t eType, WW8TabDesc* pTabDesc )
243 // If the redline type is not found in the redline stack, we have to check if there has been
244 // a tabledesc and to check its saved redline stack, too. (#136939, #i68139)
245 if( !close( rPos, eType ) )
247 if( pTabDesc && pTabDesc->getOldRedlineStack() )
249 bool const bResult =
250 pTabDesc->getOldRedlineStack()->close(rPos, eType);
251 OSL_ENSURE( bResult, "close without open!");
252 (void) bResult; // unused in non-debug
257 void wwSectionManager::SetCurrentSectionHasFootnote()
259 OSL_ENSURE(!maSegments.empty(),
260 "should not be possible, must be at least one segment");
261 if (!maSegments.empty())
262 maSegments.back().mbHasFootnote = true;
265 void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA)
267 OSL_ENSURE(!maSegments.empty(),
268 "should not be possible, must be at least one segment");
269 if ( !maSegments.empty() )
270 maSegments.back().mnVerticalAdjustment = nVA;
273 bool wwSectionManager::CurrentSectionIsVertical() const
275 OSL_ENSURE(!maSegments.empty(),
276 "should not be possible, must be at least one segment");
277 if (!maSegments.empty())
278 return maSegments.back().IsVertical();
279 return false;
282 bool wwSectionManager::CurrentSectionIsProtected() const
284 OSL_ENSURE(!maSegments.empty(),
285 "should not be possible, must be at least one segment");
286 if (!maSegments.empty())
287 return SectionIsProtected(maSegments.back());
288 return false;
291 sal_uInt32 wwSectionManager::GetPageLeft() const
293 return !maSegments.empty() ? maSegments.back().nPgLeft : 0;
296 sal_uInt32 wwSectionManager::GetPageRight() const
298 return !maSegments.empty() ? maSegments.back().nPgRight : 0;
301 sal_uInt32 wwSectionManager::GetPageWidth() const
303 return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0;
306 sal_uInt32 wwSectionManager::GetTextAreaWidth() const
308 return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0;
311 sal_uInt32 wwSectionManager::GetWWPageTopMargin() const
313 return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0;
316 sal_uInt16 SwWW8ImplReader::End_Footnote()
319 Ignoring Footnote outside of the normal Text. People will put footnotes
320 into field results and field commands.
322 if (m_bIgnoreText ||
323 m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex())
325 return 0;
328 OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
329 if (m_aFootnoteStack.empty())
330 return 0;
332 bool bFtEdOk = false;
333 const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
335 //Get the footnote character and remove it from the txtnode. We'll
336 //replace it with the footnote
337 SwTextNode* pText = m_pPaM->GetNode().GetTextNode();
338 sal_Int32 nPos = m_pPaM->GetPoint()->nContent.GetIndex();
340 OUString sChar;
341 SwTextAttr* pFN = nullptr;
342 //There should have been a footnote char, we will replace this.
343 if (pText && nPos)
345 sChar += OUStringLiteral1(pText->GetText()[--nPos]);
346 m_pPaM->SetMark();
347 --m_pPaM->GetMark()->nContent;
348 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
349 m_pPaM->DeleteMark();
350 SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
351 pFN = pText->InsertItem(aFootnote, nPos, nPos);
353 OSL_ENSURE(pFN, "Probleme beim Anlegen des Fussnoten-Textes");
354 if (pFN)
357 SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
358 WW8PLCFxSaveAll aSave;
359 m_pPlcxMan->SaveAllPLCFx( aSave );
360 WW8PLCFMan* pOldPlcxMan = m_pPlcxMan;
362 const SwNodeIndex* pSttIdx = static_cast<SwTextFootnote*>(pFN)->GetStartNode();
363 OSL_ENSURE(pSttIdx, "Probleme beim Anlegen des Fussnoten-Textes");
365 static_cast<SwTextFootnote*>(pFN)->SetSeqNo( m_rDoc.GetFootnoteIdxs().size() );
367 bool bOld = m_bFootnoteEdn;
368 m_bFootnoteEdn = true;
370 // read content of Ft-/End-Note
371 Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
372 bFtEdOk = true;
373 m_bFootnoteEdn = bOld;
375 OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
376 "footnote autonumbering must be 0x02, and everything else must not be");
378 // If no automatic numbering use the following char from the main text
379 // as the footnote number
380 if (!rDesc.mbAutoNum)
381 static_cast<SwTextFootnote*>(pFN)->SetNumber(0, sChar);
384 Delete the footnote char from the footnote if its at the beginning
385 as usual. Might not be if the user has already deleted it, e.g.
386 #i14737#
388 SwNodeIndex& rNIdx = m_pPaM->GetPoint()->nNode;
389 rNIdx = pSttIdx->GetIndex() + 1;
390 SwTextNode* pTNd = rNIdx.GetNode().GetTextNode();
391 if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
393 const OUString &rText = pTNd->GetText();
394 if (rText[0] == sChar[0])
396 m_pPaM->GetPoint()->nContent.Assign( pTNd, 0 );
397 m_pPaM->SetMark();
398 // Strip out tabs we may have inserted on export #i24762#
399 if (rText.getLength() > 1 && rText[1] == 0x09)
400 ++m_pPaM->GetMark()->nContent;
401 ++m_pPaM->GetMark()->nContent;
402 m_pReffingStck->Delete(*m_pPaM);
403 m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
404 m_pPaM->DeleteMark();
408 *m_pPaM->GetPoint() = aTmpPos; // restore Cursor
410 m_pPlcxMan = pOldPlcxMan; // Restore attributes
411 m_pPlcxMan->RestoreAllPLCFx( aSave );
414 if (bFtEdOk)
415 m_aSectionManager.SetCurrentSectionHasFootnote();
417 m_aFootnoteStack.pop_back();
418 return 0;
421 long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes)
424 Ignoring Footnote outside of the normal Text. People will put footnotes
425 into field results and field commands.
427 if (m_bIgnoreText ||
428 m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex())
430 return 0;
433 FootnoteDescriptor aDesc;
434 aDesc.mbAutoNum = true;
435 if (eEDN == pRes->nSprmId)
437 aDesc.meType = MAN_EDN;
438 WW8PLCFx_SubDoc* pEndNote = m_pPlcxMan->GetEdn();
439 if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
440 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
442 else
444 aDesc.meType = MAN_FTN;
445 WW8PLCFx_SubDoc* pFootNote = m_pPlcxMan->GetFootnote();
446 if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
447 aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
450 aDesc.mnStartCp = pRes->nCp2OrIdx;
451 aDesc.mnLen = pRes->nMemLen;
453 m_aFootnoteStack.push_back(aDesc);
455 return 0;
458 bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp,
459 int nLevel) const
461 WW8PLCFxDesc aRes;
462 aRes.pMemPos = nullptr;
463 aRes.nEndPos = rStartCp;
464 bool bReadRes(false);
465 WW8PLCFxDesc aPrevRes;
467 while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
469 if (pPap->Where() != WW8_CP_MAX)
471 const sal_uInt8* pB = pPap->HasSprm(TabRowSprm(nLevel));
472 if (pB && *pB == 1)
474 const sal_uInt8 *pLevel = nullptr;
475 if (nullptr != (pLevel = pPap->HasSprm(0x6649)))
477 if (nLevel + 1 == *pLevel)
478 return true;
480 else
482 OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
483 return true; // RowEnd found
488 aRes.nStartPos = aRes.nEndPos;
489 aRes.pMemPos = nullptr;
490 //Seek to our next block of properties
491 if (!(pPap->SeekPos(aRes.nStartPos)))
493 aRes.nEndPos = WW8_CP_MAX;
494 pPap->SetDirty(true);
496 pPap->GetSprms(&aRes);
497 pPap->SetDirty(false);
498 if (bReadRes && aRes.nEndPos == aPrevRes.nEndPos && aRes.nStartPos == aPrevRes.nStartPos)
500 SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
501 break;
503 bReadRes = true;
504 aPrevRes = aRes;
505 //Update our aRes to get the new starting point of the next properties
506 rStartCp = aRes.nEndPos;
509 return false;
512 bool SwWW8ImplReader::SearchTableEnd(WW8PLCFx_Cp_FKP* pPap) const
514 if (m_bVer67)
515 // The below SPRM is for WW8 only.
516 return false;
518 WW8PLCFxDesc aRes;
519 aRes.pMemPos = nullptr;
520 aRes.nEndPos = pPap->Where();
521 bool bReadRes(false);
522 WW8PLCFxDesc aPrevRes;
524 while (pPap->HasFkp() && pPap->Where() != WW8_CP_MAX)
526 // See if the current pap is outside the table.
527 const sal_uInt8* pB = pPap->HasSprm(NS_sprm::LN_PFInTable);
528 if (!pB || *pB != 1)
529 // Yes, this is the position after the end of the table.
530 return true;
532 // It is, so seek to the next pap.
533 aRes.nStartPos = aRes.nEndPos;
534 aRes.pMemPos = nullptr;
535 if (!pPap->SeekPos(aRes.nStartPos))
536 return false;
538 // Read the sprms and make sure we moved forward to avoid infinite loops.
539 pPap->GetSprms(&aRes);
540 if (bReadRes && aRes.nEndPos == aPrevRes.nEndPos && aRes.nStartPos == aPrevRes.nStartPos)
541 return false;
543 bReadRes = true;
544 aPrevRes = aRes;
547 return false;
550 ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
551 const WW8_TablePos *pTabPos)
553 const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
554 ApoTestResults aRet;
555 // Frame in Style Definition (word appears to ignore them if inside an
556 // text autoshape)
557 sal_uInt16 const nStyle(m_pPlcxMan->GetColl());
558 if (!m_bTxbxFlySection && nStyle < m_vColl.size())
559 aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
562 #i1140#
563 If I have a table and apply a style to one of its frames that should cause
564 a paragraph that it is applied to it to only exist as a separate floating
565 frame, then the behaviour depends on which cell that it has been applied
566 to. If it is the first cell of a row then the whole table row jumps into the
567 new frame, if it isn't then the paragraph attributes are applied
568 "except" for the floating frame stuff. i.e. it's ignored. So if there's a
569 table, and we're not in the first cell then we ignore the fact that the
570 paragraph style wants to be in a different frame.
572 This sort of mindbending inconsistency is surely why frames are deprecated
573 in word 97 onwards and hidden away from the user
575 #i1532# & #i5379#
576 If we are already a table in a frame then we must grab the para properties
577 to see if we are still in that frame.
580 aRet.m_bHasSprm37 = m_pPlcxMan->HasParaSprm( m_bVer67 ? 37 : 0x2423 );
581 const sal_uInt8 *pSrpm29 = m_pPlcxMan->HasParaSprm( m_bVer67 ? 29 : 0x261B );
582 aRet.m_bHasSprm29 = pSrpm29 != nullptr;
583 aRet.m_nSprm29 = pSrpm29 ? *pSrpm29 : 0;
585 // Is there some frame data here
586 bool bNowApo = aRet.HasFrame() || pTopLevelTable;
587 if (bNowApo)
589 if (WW8FlyPara *pTest = ConstructApo(aRet, pTabPos))
590 delete pTest;
591 else
592 bNowApo = false;
595 bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
596 if (bTestAllowed)
598 //Test is allowed if there is no table.
599 //Otherwise only allowed if we are in the
600 //first paragraph of the first cell of a row.
601 //(And only if the row we are inside is at the
602 //same level as the previous row, think tables
603 //in tables)
604 if (nCellLevel == m_nInTable)
607 if (!m_nInTable)
608 bTestAllowed = true;
609 else
611 if (!m_pTableDesc)
613 OSL_ENSURE(m_pTableDesc, "What!");
614 bTestAllowed = false;
616 else
618 // #i39468#
619 // If current cell isn't valid, the test is allowed.
620 // The cell isn't valid, if e.g. there is a new row
621 // <pTableDesc->nAktRow> >= <pTableDesc->pTabLines->Count()>
622 bTestAllowed =
623 m_pTableDesc->GetAktCol() == 0 &&
624 ( !m_pTableDesc->IsValidCell( m_pTableDesc->GetAktCol() ) ||
625 m_pTableDesc->InFirstParaInCell() );
631 if (!bTestAllowed)
632 return aRet;
634 aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
635 aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
637 //If it happens that we are in a table, then if it's not the first cell
638 //then any attributes that might otherwise cause the contents to jump
639 //into another frame don't matter, a table row sticks together as one
640 //unit no matter what else happens. So if we are not in a table at
641 //all, or if we are in the first cell then test that the last frame
642 //data is the same as the current one
643 if (bNowApo && InEqualApo(nCellLevel))
645 // two bordering eachother
646 if (!TestSameApo(aRet, pTabPos))
647 aRet.mbStopApo = aRet.mbStartApo = true;
650 return aRet;
653 // helper methods for outline, numbering and bullets
655 static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
657 static const SvxExtNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
658 SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC,
659 SVX_NUM_ARABIC, SVX_NUM_ARABIC };
661 static const SvxAdjust eAdjA[4] = { SVX_ADJUST_LEFT,
662 SVX_ADJUST_RIGHT, SVX_ADJUST_LEFT, SVX_ADJUST_LEFT };
663 if (rAV.nfc < 8) {
664 rNum.SetNumberingType( static_cast< sal_Int16 >(eNumA[ rAV.nfc ] ));
665 } else {
666 sal_Int16 nType= style::NumberingType::ARABIC;
667 switch( rAV.nfc ) {
668 case 14:
669 case 19:nType = style::NumberingType::FULLWIDTH_ARABIC; break;
670 case 30:nType = style::NumberingType::TIAN_GAN_ZH; break;
671 case 31:nType = style::NumberingType::DI_ZI_ZH; break;
672 case 35:
673 case 36:
674 case 37:
675 case 39:
676 nType = style::NumberingType::NUMBER_LOWER_ZH; break;
677 case 34:nType = style::NumberingType::NUMBER_UPPER_ZH_TW;break;
678 case 38:nType = style::NumberingType::NUMBER_UPPER_ZH; break;
679 case 10:
680 case 11:
681 nType = style::NumberingType::NUMBER_TRADITIONAL_JA;break;
682 case 20:nType = style::NumberingType::AIU_FULLWIDTH_JA;break;
683 case 12:nType = style::NumberingType::AIU_HALFWIDTH_JA;break;
684 case 21:nType = style::NumberingType::IROHA_FULLWIDTH_JA;break;
685 case 13:nType = style::NumberingType::IROHA_HALFWIDTH_JA;break;
686 case 24:nType = style::NumberingType::HANGUL_SYLLABLE_KO;break;
687 case 25:nType = style::NumberingType::HANGUL_JAMO_KO;break;
688 case 41:nType = style::NumberingType::NUMBER_HANGUL_KO;break;
689 //case 42:
690 //case 43:
691 case 44:nType = style::NumberingType::NUMBER_UPPER_KO; break;
692 default:
693 nType= style::NumberingType::ARABIC;break;
695 rNum.SetNumberingType( nType );
698 if ((rAV.aBits1 & 0x4) >> 2)
700 rNum.SetIncludeUpperLevels(nSwLevel + 1);
702 rNum.SetStart( SVBT16ToShort( rAV.iStartAt ) );
703 rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
705 rNum.SetCharTextDistance( SVBT16ToShort( rAV.dxaSpace ) );
706 sal_Int16 nIndent = std::abs((sal_Int16)SVBT16ToShort( rAV.dxaIndent ));
707 if( rAV.aBits1 & 0x08 ) //fHang
709 rNum.SetFirstLineOffset( -nIndent );
710 rNum.SetAbsLSpace( nIndent );
712 else
713 rNum.SetCharTextDistance( nIndent ); // width of number is missing
715 if( rAV.nfc == 5 || rAV.nfc == 7 )
717 OUString sP = "." + rNum.GetSuffix();
718 rNum.SetSuffix( sP ); // ordinal number
722 void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, WW8_ANLV const &rAV,
723 const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
725 if (nStart > nElements)
726 return;
728 pText += nStart;
729 nElements -= nStart;
731 bool bInsert = false; // Default
732 rtl_TextEncoding eCharSet = m_eStructCharSet;
734 const WW8_FFN* pF = m_pFonts->GetFont(SVBT16ToShort(rAV.ftc)); // FontInfo
735 bool bListSymbol = pF && ( pF->chs == 2 ); // Symbol/WingDings/...
737 OUString sText;
738 sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
739 if (m_bVer67)
741 if (nLen > nElements)
743 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
744 << nLen << " vs " << nElements << " max");
745 return;
747 sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
749 else
751 if (nLen > nElements / 2)
753 SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
754 << nLen << " vs " << nElements / 2 << " max");
755 return;
757 for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
759 sText += OUStringLiteral1(SVBT16ToShort(*reinterpret_cast<SVBT16 const *>(pText)));
763 if( bOutline )
764 { // outline
765 if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
766 || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
768 // if self defined digits
769 bInsert = true; // then apply character
771 // replace by simple Bullet ?
772 if( bListSymbol )
774 // use cBulletChar for correct mapping on MAC
775 OUStringBuffer aBuf;
776 comphelper::string::padToLength(aBuf, rAV.cbTextBefore
777 + rAV.cbTextAfter, cBulletChar);
778 sText = aBuf.makeStringAndClear();
782 else
783 { // numbering / bullets
784 bInsert = true;
785 if( bListSymbol )
787 FontFamily eFamily;
788 OUString aName;
789 FontPitch ePitch;
791 if( GetFontParams( SVBT16ToShort( rAV.ftc ), eFamily, aName,
792 ePitch, eCharSet ) ){
794 vcl::Font aFont;
795 aFont.SetFamilyName( aName );
796 aFont.SetFamily( eFamily );
798 aFont.SetCharSet( eCharSet );
799 rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
801 rNum.SetBulletFont( &aFont );
803 // take only the very first character
804 if (rAV.cbTextBefore || rAV.cbTextAfter)
805 rNum.SetBulletChar( sText[ 0 ] );
806 else
807 rNum.SetBulletChar( 0x2190 );
811 if( bInsert )
813 if (rAV.cbTextBefore)
815 OUString sP( sText.copy( 0, rAV.cbTextBefore ) );
816 rNum.SetPrefix( sP );
818 if( rAV.cbTextAfter )
820 OUString sP( rNum.GetSuffix() );
821 sP += sText.copy( rAV.cbTextBefore, rAV.cbTextAfter);
822 rNum.SetSuffix( sP );
824 // The characters before and after multipe digits do not apply because
825 // those are handled different by the writer and the result is in most
826 // cases worse than without.
830 // SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
831 // which are provided by pNumR. This is used for everything beside
832 // outline inside the text.
833 void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
834 bool bOutLine)
836 SwNumFormat aNF;
837 if (pAD)
838 { // there is a Anld-Sprm
839 m_bAktAND_fNumberAcross = 0 != pAD->fNumberAcross;
840 WW8_ANLV const &rAV = pAD->eAnlv;
841 SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
842 SetAnlvStrings(aNF, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
844 pNumR->Set(nSwLevel, aNF);
847 // chapter numbering and bullets
849 // Chapter numbering happens in the style definition.
850 // Sprm 13 provides the level, Sprm 12 the content.
852 SwNumRule* SwWW8ImplReader::GetStyRule()
854 if( m_pStyles->pStyRule ) // Bullet-Style already present
855 return m_pStyles->pStyRule;
857 const OUString aBaseName("WW8StyleNum");
858 const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
860 // #i86652#
861 sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false,
862 SvxNumberFormat::LABEL_ALIGNMENT );
863 m_pStyles->pStyRule = m_rDoc.GetNumRuleTable()[nRul];
864 // Auto == false-> Nummerierungsvorlage
865 m_pStyles->pStyRule->SetAutoRule(false);
867 return m_pStyles->pStyRule;
870 // Sprm 13
871 void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
873 m_nSwNumLevel = 0xff; // Default: invalid
875 if( nLen <= 0 )
876 return;
878 // StyleDef ?
879 if( m_pAktColl )
881 // only for SwTextFormatColl, not CharFormat
882 // WW: 0 = no Numbering
883 SwWW8StyInf * pColl = GetStyle(m_nAktColl);
884 if (pColl != nullptr && pColl->m_bColl && *pData)
886 // Range WW:1..9 -> SW:0..8 no bullets / numbering
888 if (*pData <= MAXLEVEL && *pData <= 9)
890 m_nSwNumLevel = *pData - 1;
891 if (!m_bNoAttrImport)
892 static_cast<SwTextFormatColl*>(m_pAktColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
893 // For WW-NoNumbering also NO_NUMBERING could be used.
894 // ( For normal numberierung NO_NUM has to be used:
895 // NO_NUM : pauses numbering,
896 // NO_NUMBERING : no numbering at all )
899 else if( *pData == 10 || *pData == 11 )
901 // remember type, the rest happens at Sprm 12
902 m_pStyles->nWwNumLevel = *pData;
906 else
908 //Not StyleDef
909 if (!m_bAnl)
910 StartAnl(pData); // begin of outline / bullets
911 NextAnlLine(pData);
915 void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
917 SwWW8StyInf * pStyInf = GetStyle(m_nAktColl);
918 if( !m_pAktColl || nLen <= 0 // only for Styledef
919 || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
920 || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
922 m_nSwNumLevel = 0xff;
923 return;
926 if (static_cast<size_t>(nLen) < sizeof(WW8_ANLD))
928 SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
929 m_nSwNumLevel = 0xff;
930 return;
933 if( m_nSwNumLevel <= MAXLEVEL // Value range mapping WW:1..9 -> SW:0..8
934 && m_nSwNumLevel <= 9 ){ // No Bullets or Numbering
936 // If NumRuleItems were set, either directly or through inheritance, disable them now
937 m_pAktColl->SetFormatAttr( SwNumRuleItem() );
939 const OUString aName("Outline");
940 SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ),
941 SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
942 OUTLINE_RULE );
943 aNR = *m_rDoc.GetOutlineNumRule();
945 SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
947 // Missing Levels need not be replenished
948 m_rDoc.SetOutlineNumRule( aNR );
949 }else if( m_pStyles->nWwNumLevel == 10 || m_pStyles->nWwNumLevel == 11 ){
950 SwNumRule* pNR = GetStyRule();
951 SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
952 m_pAktColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
954 pStyInf = GetStyle(m_nAktColl);
955 if (pStyInf != nullptr)
956 pStyInf->m_bHasStyNumRule = true;
960 // Numbering / Bullets
962 // SetNumOlst() carries the Numrules for this cell to SwNumFormat.
963 // For this the info is fetched from OLST and not from ANLD ( see later )
964 // ( only for outline inside text; Bullets / numbering use ANLDs )
965 void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel)
967 SwNumFormat aNF;
968 WW8_ANLV &rAV = pO->rganlv[nSwLevel];
969 SetBaseAnlv(aNF, rAV, nSwLevel);
970 // ... and then the Strings
971 int nTextOfs = 0;
972 sal_uInt8 i;
973 WW8_ANLV* pAV1; // search String-Positions
974 for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
975 nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
977 if (!m_bVer67)
978 nTextOfs *= 2;
979 SetAnlvStrings(aNF, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
980 pNumR->Set(nSwLevel, aNF);
983 // The OLST is at the beginning of each section that contains outlines.
984 // The ANLDs that are connected to each outline-line contain only nonsense,
985 // so the OLSTs are remembered for the section to have usable information
986 // when outline-paragraphs occur.
987 void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
989 delete m_pNumOlst;
990 if (nLen <= 0)
992 m_pNumOlst = nullptr;
993 return;
996 if (static_cast<size_t>(nLen) < sizeof(WW8_OLST))
998 SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
999 m_pNumOlst = nullptr;
1000 return;
1003 m_pNumOlst = new WW8_OLST;
1004 if( nLen < sal::static_int_cast< sal_Int32 >(sizeof( WW8_OLST )) ) // fill if to short
1005 memset( m_pNumOlst, 0, sizeof( *m_pNumOlst ) );
1006 *m_pNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
1009 WW8LvlType GetNumType(sal_uInt8 nWwLevelNo)
1011 WW8LvlType nRet = WW8_None;
1012 if( nWwLevelNo == 12 )
1013 nRet = WW8_Pause;
1014 else if( nWwLevelNo == 10 )
1015 nRet = WW8_Numbering;
1016 else if( nWwLevelNo == 11 )
1017 nRet = WW8_Sequence;
1018 else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
1019 nRet = WW8_Outline;
1020 return nRet;
1023 SwNumRule *ANLDRuleMap::GetNumRule(SwDoc& rDoc, sal_uInt8 nNumType)
1025 const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
1026 if (rNumRule.isEmpty())
1027 return nullptr;
1028 return rDoc.FindNumRulePtr(rNumRule);
1031 void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
1033 if (WW8_Numbering == nNumType)
1034 msNumberingNumRule = rNumRule;
1035 else
1036 msOutlineNumRule = rNumRule;
1039 // StartAnl is called at the beginning of a row area that contains
1040 // outline / numbering / bullets
1041 void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13)
1043 m_bAktAND_fNumberAcross = false;
1045 sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
1046 if (nT == WW8_Pause || nT == WW8_None)
1047 return;
1049 m_nWwNumType = nT;
1050 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
1052 // check for COL numbering:
1053 const sal_uInt8* pS12 = nullptr;// sprmAnld
1054 OUString sNumRule;
1056 if (m_pTableDesc)
1058 sNumRule = m_pTableDesc->GetNumRuleName();
1059 if (!sNumRule.isEmpty())
1061 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
1062 if (!pNumRule)
1063 sNumRule.clear();
1064 else
1066 // this is ROW numbering ?
1067 pS12 = m_pPlcxMan->HasParaSprm(m_bVer67 ? 12 : 0xC63E); // sprmAnld
1068 if (pS12 && 0 != reinterpret_cast<WW8_ANLD const *>(pS12)->fNumberAcross)
1069 sNumRule.clear();
1074 SwWW8StyInf * pStyInf = GetStyle(m_nAktColl);
1075 if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
1077 sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
1078 pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
1079 if (!pNumRule)
1080 sNumRule.clear();
1083 if (sNumRule.isEmpty())
1085 if (!pNumRule)
1087 // #i86652#
1088 pNumRule = m_rDoc.GetNumRuleTable()[
1089 m_rDoc.MakeNumRule( sNumRule, nullptr, false,
1090 SvxNumberFormat::LABEL_ALIGNMENT ) ];
1092 if (m_pTableDesc)
1094 if (!pS12)
1095 pS12 = m_pPlcxMan->HasParaSprm(m_bVer67 ? 12 : 0xC63E); // sprmAnld
1096 if (!pS12 || !reinterpret_cast<WW8_ANLD const *>(pS12)->fNumberAcross)
1097 m_pTableDesc->SetNumRuleName(pNumRule->GetName());
1101 m_bAnl = true;
1103 sNumRule = pNumRule ? pNumRule->GetName() : OUString();
1104 // set NumRules via stack
1105 m_pCtrlStck->NewAttr(*m_pPaM->GetPoint(),
1106 SfxStringItem(RES_FLTR_NUMRULE, sNumRule));
1108 m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
1111 // NextAnlLine() is called once for every row of a
1112 // outline / numbering / bullet
1113 void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13)
1115 if (!m_bAnl)
1116 return;
1118 SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
1120 // pNd->UpdateNum ohne Regelwerk gibt GPF spaetestens beim Speichern als
1121 // sdw3
1123 // WW:10 = numberierung -> SW:0 & WW:11 = bullets -> SW:0
1124 if (*pSprm13 == 10 || *pSprm13 == 11)
1126 m_nSwNumLevel = 0;
1127 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1129 // not defined yet
1130 // sprmAnld o. 0
1131 const sal_uInt8* pS12 = m_pPlcxMan->HasParaSprm(m_bVer67 ? 12 : 0xC63E);
1132 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(pS12), m_nSwNumLevel, false);
1135 else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
1137 m_nSwNumLevel = *pSprm13 - 1; // outline
1138 // undefined
1139 if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
1141 if (m_pNumOlst) // there was a OLST
1143 //Assure upper levels are set, #i9556#
1144 for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
1146 if (!pNumRule->GetNumFormat(nI))
1147 SetNumOlst(pNumRule, m_pNumOlst, nI);
1150 SetNumOlst(pNumRule, m_pNumOlst , m_nSwNumLevel);
1152 else // no Olst -> use Anld
1154 // sprmAnld
1155 const sal_uInt8* pS12 = m_pPlcxMan->HasParaSprm(m_bVer67 ? 12 : 0xC63E);
1156 SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(pS12), m_nSwNumLevel, false);
1160 else
1161 m_nSwNumLevel = 0xff; // no number
1163 SwTextNode* pNd = m_pPaM->GetNode().GetTextNode();
1164 if (m_nSwNumLevel < MAXLEVEL)
1165 pNd->SetAttrListLevel( m_nSwNumLevel );
1166 else
1168 pNd->SetAttrListLevel(0);
1169 pNd->SetCountedInList( false );
1173 void SwWW8ImplReader::StopAllAnl(bool bGoBack)
1175 //Of course we're not restarting, but we'll make use of our knowledge
1176 //of the implementation to do it.
1177 StopAnlToRestart(WW8_None, bGoBack);
1180 void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack)
1182 if (bGoBack)
1184 SwPosition aTmpPos(*m_pPaM->GetPoint());
1185 m_pPaM->Move(fnMoveBackward, GoInContent);
1186 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1187 *m_pPaM->GetPoint() = aTmpPos;
1189 else
1190 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
1192 m_aANLDRules.msNumberingNumRule.clear();
1194 #i18816#
1195 my take on this problem is that moving either way from an outline to a
1196 numbering doesn't halt the outline, while the numbering is always halted
1198 bool bNumberingNotStopOutline =
1199 (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
1200 ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
1201 if (!bNumberingNotStopOutline)
1202 m_aANLDRules.msOutlineNumRule.clear();
1204 m_nSwNumLevel = 0xff;
1205 m_nWwNumType = WW8_None;
1206 m_bAnl = false;
1209 WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc& rBand )
1211 *this = rBand;
1212 if( rBand.pTCs )
1214 pTCs = new WW8_TCell[nWwCols];
1215 memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
1217 if( rBand.pSHDs )
1219 pSHDs = new WW8_SHD[nWwCols];
1220 memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
1222 if( rBand.pNewSHDs )
1224 pNewSHDs = new sal_uInt32[nWwCols];
1225 memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(sal_uInt32));
1227 memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
1230 // ReadDef reads the cell position and the borders of a band
1231 void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS)
1233 if (!bVer67)
1234 pS++;
1236 short nLen = (sal_Int16)SVBT16ToShort( pS - 2 ); // not beautiful
1238 sal_uInt8 nCols = *pS; // number of cells
1239 short nOldCols = nWwCols;
1241 if( nCols > MAX_COL )
1242 return;
1244 nWwCols = nCols;
1246 const sal_uInt8* pT = &pS[1];
1247 nLen --;
1248 int i;
1249 for(i=0; i<=nCols; i++, pT+=2 )
1250 nCenter[i] = (sal_Int16)SVBT16ToShort( pT ); // X-borders
1251 nLen -= 2 * ( nCols + 1 );
1252 if( nCols != nOldCols ) // different column count
1254 delete[] pTCs;
1255 pTCs = nullptr;
1256 delete[] pSHDs;
1257 pSHDs = nullptr;
1258 delete[] pNewSHDs;
1259 pNewSHDs = nullptr;
1262 short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
1264 if (!pTCs && nCols)
1266 // create empty TCs
1267 pTCs = new WW8_TCell[nCols];
1268 setcelldefaults(pTCs,nCols);
1271 short nColsToRead = nFileCols;
1272 if (nColsToRead > nCols)
1273 nColsToRead = nCols;
1275 if( nColsToRead )
1277 // read TCs
1280 Attention: Beginning with Ver8 there is an extra ushort per TC
1281 added and the size of the border code is doubled.
1282 Because of this a simple copy (pTCs[i] = *pTc;)
1283 is not possible.
1285 Advantage: The work structure suits better.
1287 WW8_TCell* pAktTC = pTCs;
1288 if( bVer67 )
1290 WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
1291 for(i=0; i<nColsToRead; i++, ++pAktTC,++pTc)
1293 if( i < nColsToRead )
1294 { // TC from file ?
1295 sal_uInt8 aBits1 = pTc->aBits1Ver6;
1296 pAktTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
1297 pAktTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
1298 pAktTC->rgbrc[ WW8_TOP ]
1299 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
1300 pAktTC->rgbrc[ WW8_LEFT ]
1301 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
1302 pAktTC->rgbrc[ WW8_BOT ]
1303 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
1304 pAktTC->rgbrc[ WW8_RIGHT ]
1305 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1306 if( ( pAktTC->bMerged )
1307 && ( i > 0 ) )
1309 // Cell merged -> remember
1310 //bWWMergedVer6[i] = true;
1311 pTCs[i-1].rgbrc[ WW8_RIGHT ]
1312 = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
1313 // apply right border to previous cell
1314 // bExist must not be set to false, because WW
1315 // does not count this cells in text boxes....
1320 else
1322 WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
1323 for (int k = 0; k < nColsToRead; ++k, ++pAktTC, ++pTc )
1325 sal_uInt16 aBits1 = SVBT16ToShort( pTc->aBits1Ver8 );
1326 pAktTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
1327 pAktTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
1328 pAktTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
1329 pAktTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
1330 pAktTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
1331 pAktTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
1332 pAktTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
1333 pAktTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
1334 // note: in aBits1 there are 7 bits unused,
1335 // followed by another 16 unused bits
1337 pAktTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
1338 pAktTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
1339 pAktTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
1340 pAktTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
1344 // #i25071 In '97 text direction appears to be only set using TC properties
1345 // not with sprmTTextFlow so we need to cycle through the maDirections and
1346 // double check any non-default directions
1347 for (int k = 0; k < nCols; ++k)
1349 if(maDirections[k] == 4)
1351 if(pTCs[k].bVertical)
1353 if(pTCs[k].bBackward)
1354 maDirections[k] = 3;
1355 else
1356 maDirections[k] = 1;
1363 void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC)
1365 if( pParamsTSetBRC && pTCs ) // set one or more cell border(s)
1367 sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
1368 sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
1369 sal_uInt8 nFlag = *(pParamsTSetBRC+2);
1371 if (nitcFirst >= nWwCols)
1372 return;
1374 if (nitcLim > nWwCols)
1375 nitcLim = nWwCols;
1377 bool bChangeRight = (nFlag & 0x08) != 0;
1378 bool bChangeBottom = (nFlag & 0x04) != 0;
1379 bool bChangeLeft = (nFlag & 0x02) != 0;
1380 bool bChangeTop = (nFlag & 0x01) != 0;
1382 WW8_TCell* pAktTC = pTCs + nitcFirst;
1383 WW8_BRCVer9 brcVer9;
1384 if( nBrcVer == 6 )
1385 brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
1386 else if( nBrcVer == 8 )
1387 brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
1388 else
1389 brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
1391 for( int i = nitcFirst; i < nitcLim; ++i, ++pAktTC )
1393 if( bChangeTop )
1394 pAktTC->rgbrc[ WW8_TOP ] = brcVer9;
1395 if( bChangeLeft )
1396 pAktTC->rgbrc[ WW8_LEFT ] = brcVer9;
1397 if( bChangeBottom )
1398 pAktTC->rgbrc[ WW8_BOT ] = brcVer9;
1399 if( bChangeRight )
1400 pAktTC->rgbrc[ WW8_RIGHT ] = brcVer9;
1405 void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams)
1407 // sprmTTableBorders
1408 if( nBrcVer == 6 )
1410 WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
1411 for (int i = 0; i < 6; ++i)
1412 aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
1414 else if ( nBrcVer == 8 )
1416 static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
1417 for( int i = 0; i < 6; ++i )
1418 aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
1420 else
1421 memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
1424 void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol)
1426 // sprmTDxaCol (opcode 0x7623) changes the width of cells
1427 // whose index is within a certain range to be a certain value.
1429 if( nWwCols && pParamsTDxaCol ) // set one or more cell length(s)
1431 sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
1432 sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
1433 short nDxaCol = (sal_Int16)SVBT16ToShort( pParamsTDxaCol + 2 );
1435 for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
1437 const short nOrgWidth = nCenter[i+1] - nCenter[i];
1438 const short nDelta = nDxaCol - nOrgWidth;
1439 for( int j = i+1; j <= nWwCols; j++ )
1441 nCenter[j] = nCenter[j] + nDelta;
1447 void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert)
1449 if( nWwCols && pParamsTInsert ) // set one or more cell length(s)
1451 sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
1452 if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
1453 return;
1454 sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
1455 sal_uInt16 ndxaCol = SVBT16ToShort( pParamsTInsert+2 );
1457 short nNewWwCols;
1458 if (nitcInsert > nWwCols)
1460 nNewWwCols = nitcInsert+nctc;
1461 //if new count would be outside max possible count, clip it, and calc a new replacement
1462 //legal nctc
1463 if (nNewWwCols > MAX_COL)
1465 nNewWwCols = MAX_COL;
1466 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
1469 else
1471 nNewWwCols = nWwCols+nctc;
1472 //if new count would be outside max possible count, clip it, and calc a new replacement
1473 //legal nctc
1474 if (nNewWwCols > MAX_COL)
1476 nNewWwCols = MAX_COL;
1477 nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
1481 WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
1482 setcelldefaults(pTC2s, nNewWwCols);
1484 if (pTCs)
1486 memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
1487 delete[] pTCs;
1489 pTCs = pTC2s;
1491 //If we have to move some cells
1492 if (nitcInsert <= nWwCols)
1494 // adjust the left x-position of the dummy at the very end
1495 nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
1496 for( int i = nWwCols-1; i >= nitcInsert; i--)
1498 // adjust the left x-position
1499 nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
1501 // adjust the cell's borders
1502 pTCs[i + nctc] = pTCs[i];
1506 //if itcMac is larger than full size, fill in missing ones first
1507 for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
1508 nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
1510 //now add in our new cells
1511 for( int j = 0;j < nctc; j++)
1512 nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
1514 nWwCols = nNewWwCols;
1518 void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams)
1520 sal_uInt8 nStartCell = *pParams++;
1521 sal_uInt8 nEndCell = *pParams++;
1522 sal_uInt16 nCode = SVBT16ToShort(pParams);
1524 OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
1525 OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
1526 if (nStartCell > MAX_COL)
1527 return;
1528 if (nEndCell > MAX_COL + 1)
1529 nEndCell = MAX_COL + 1;
1531 for (;nStartCell < nEndCell; ++nStartCell)
1532 maDirections[nStartCell] = nCode;
1535 void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams)
1537 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1538 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1539 if (nLen != 6)
1540 return;
1541 mbHasSpacing=true;
1542 #if OSL_DEBUG_LEVEL > 0
1543 sal_uInt8 nWhichCell = *pParams;
1544 OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
1545 #endif
1546 ++pParams; //Skip which cell
1547 ++pParams; //unknown byte
1549 sal_uInt8 nSideBits = *pParams++;
1550 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1551 ++pParams; //unknown byte
1552 sal_uInt16 nValue = SVBT16ToShort( pParams );
1553 for (int i = wwTOP; i <= wwRIGHT; i++)
1555 switch (nSideBits & (1 << i))
1557 case 1 << wwTOP:
1558 mnDefaultTop = nValue;
1559 break;
1560 case 1 << wwLEFT:
1561 mnDefaultLeft = nValue;
1562 break;
1563 case 1 << wwBOTTOM:
1564 mnDefaultBottom = nValue;
1565 break;
1566 case 1 << wwRIGHT:
1567 mnDefaultRight = nValue;
1568 break;
1569 case 0:
1570 break;
1571 default:
1572 OSL_ENSURE(false, "Impossible");
1573 break;
1578 void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams)
1580 sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
1581 OSL_ENSURE(nLen == 6, "Unexpected spacing len");
1582 if (nLen != 6)
1583 return;
1584 sal_uInt8 nWhichCell = *pParams++;
1585 OSL_ENSURE(nWhichCell < MAX_COL + 1, "Cell out of range in spacings");
1586 if (nWhichCell >= MAX_COL + 1)
1587 return;
1589 ++pParams; //unknown byte
1590 sal_uInt8 nSideBits = *pParams++;
1591 OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
1592 nOverrideSpacing[nWhichCell] |= nSideBits;
1594 OSL_ENSURE(nOverrideSpacing[nWhichCell] < 0x10,
1595 "Unexpected value for nSideBits");
1596 #if OSL_DEBUG_LEVEL > 0
1597 sal_uInt8 nUnknown2 = *pParams;
1598 OSL_ENSURE(nUnknown2 == 0x3, "Unexpected value for spacing2");
1599 #endif
1600 ++pParams;
1601 sal_uInt16 nValue = SVBT16ToShort( pParams );
1603 for (int i=0; i < 4; i++)
1605 if (nSideBits & (1 << i))
1606 nOverrideValues[nWhichCell][i] = nValue;
1610 void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete)
1612 if( nWwCols && pParamsTDelete ) // set one or more cell length(s)
1614 sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
1615 if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
1616 return;
1617 sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
1618 if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
1619 return;
1622 * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
1623 * greater than or equal to itcLim to be moved
1625 int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
1627 if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
1629 WW8_TCell* pAktTC = pTCs + nitcFirst;
1630 int i = 0;
1631 while( i < nShlCnt )
1633 // adjust the left x-position
1634 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1636 // adjust the cell's borders
1637 *pAktTC = pTCs[ nitcLim + i];
1639 ++i;
1640 ++pAktTC;
1642 // adjust the left x-position of the dummy at the very end
1643 nCenter[nitcFirst + i] = nCenter[nitcLim + i];
1646 short nCellsDeleted = nitcLim - nitcFirst;
1647 //clip delete request to available number of cells
1648 if (nCellsDeleted > nWwCols)
1649 nCellsDeleted = nWwCols;
1650 nWwCols -= nCellsDeleted;
1654 // ReadShd reads the background color of a cell
1655 // ReadDef must be called before
1656 void WW8TabBandDesc::ReadShd(const sal_uInt8* pS )
1658 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1659 if( !nLen )
1660 return;
1662 if( !pSHDs )
1664 pSHDs = new WW8_SHD[nWwCols];
1665 memset( pSHDs, 0, nWwCols * sizeof( WW8_SHD ) );
1668 short nCount = nLen >> 1;
1669 if (nCount > nWwCols)
1670 nCount = nWwCols;
1672 SVBT16 const * pShd;
1673 int i;
1674 for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
1675 pSHDs[i].SetWWValue( *pShd );
1678 void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67)
1680 sal_uInt8 nLen = pS ? *(pS - 1) : 0;
1681 if (!nLen)
1682 return;
1684 if (!pNewSHDs)
1685 pNewSHDs = new sal_uInt32[nWwCols];
1687 short nCount = nLen / 10; //10 bytes each
1688 if (nCount > nWwCols)
1689 nCount = nWwCols;
1691 int i=0;
1692 while (i < nCount)
1693 pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
1695 while (i < nWwCols)
1696 pNewSHDs[i++] = COL_AUTO;
1699 void WW8TabBandDesc::setcelldefaults(WW8_TCell *pCells, short nCols)
1701 memset( pCells, 0, nCols * sizeof( WW8_TCell ) );
1704 const sal_uInt8 *HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
1706 const sal_uInt8 *pParams;
1707 if (bVer67)
1708 pParams = pPap->HasSprm(24);
1709 else
1711 if (nullptr == (pParams = pPap->HasSprm(0x244B)))
1712 pParams = pPap->HasSprm(0x2416);
1714 return pParams;
1717 enum wwTableSprm
1719 sprmNil,
1721 sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
1722 sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft,
1723 sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete,
1724 sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
1725 sprmTDefTableNewShd, sprmTCellPadding, sprmTCellPaddingDefault
1728 wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
1730 switch (eVer)
1732 case ww::eWW8:
1733 switch (nId)
1735 case NS_sprm::LN_TTableWidth:
1736 return sprmTTableWidth;
1737 case NS_sprm::LN_TTextFlow:
1738 return sprmTTextFlow;
1739 case NS_sprm::LN_TTableHeader:
1740 return sprmTTableHeader;
1741 case NS_sprm::LN_TFCantSplit:
1742 return sprmTFCantSplit;
1743 case NS_sprm::LN_TJc90:
1744 return sprmTJc;
1745 case NS_sprm::LN_TFBiDi:
1746 return sprmTFBiDi;
1747 case NS_sprm::LN_TDelete:
1748 return sprmTDelete;
1749 case NS_sprm::LN_TInsert:
1750 return sprmTInsert;
1751 case NS_sprm::LN_TDxaCol:
1752 return sprmTDxaCol;
1753 case NS_sprm::LN_TDyaRowHeight:
1754 return sprmTDyaRowHeight;
1755 case NS_sprm::LN_TDxaLeft:
1756 return sprmTDxaLeft;
1757 case NS_sprm::LN_TDxaGapHalf:
1758 return sprmTDxaGapHalf;
1759 case NS_sprm::LN_TTableBorders80:
1760 return sprmTTableBorders;
1761 case NS_sprm::LN_TDefTable:
1762 return sprmTDefTable;
1763 case NS_sprm::LN_TDefTableShd80:
1764 return sprmTDefTableShd;
1765 case NS_sprm::LN_TDefTableShd:
1766 return sprmTDefTableNewShd;
1767 case NS_sprm::LN_TTableBorders:
1768 return sprmTTableBorders90;
1769 case NS_sprm::LN_TSetBrc80:
1770 return sprmTSetBrc;
1771 case NS_sprm::LN_TSetBrc:
1772 return sprmTSetBrc90;
1773 case NS_sprm::LN_TCellPadding:
1774 return sprmTCellPadding;
1775 case NS_sprm::LN_TCellPaddingDefault:
1776 return sprmTCellPaddingDefault;
1778 break;
1779 case ww::eWW7:
1780 case ww::eWW6:
1781 switch (nId)
1783 case 182:
1784 return sprmTJc;
1785 case 183:
1786 return sprmTDxaLeft;
1787 case 184:
1788 return sprmTDxaGapHalf;
1789 case 186:
1790 return sprmTTableHeader;
1791 case 187:
1792 return sprmTTableBorders;
1793 case 189:
1794 return sprmTDyaRowHeight;
1795 case 190:
1796 return sprmTDefTable;
1797 case 191:
1798 return sprmTDefTableShd;
1799 case 193:
1800 return sprmTSetBrc;
1801 case 194:
1802 return sprmTInsert;
1803 case 195:
1804 return sprmTDelete;
1805 case 196:
1806 return sprmTDxaCol;
1808 break;
1809 case ww::eWW1:
1810 case ww::eWW2:
1811 switch (nId)
1813 case 146:
1814 return sprmTJc;
1815 case 147:
1816 return sprmTDxaLeft;
1817 case 148:
1818 return sprmTDxaGapHalf;
1819 case 153:
1820 return sprmTDyaRowHeight;
1821 case 154:
1822 return sprmTDefTable;
1823 case 155:
1824 return sprmTDefTableShd;
1825 case 157:
1826 return sprmTSetBrc;
1827 case 158:
1828 return sprmTInsert;
1829 case 159:
1830 return sprmTDelete;
1831 case 160:
1832 return sprmTDxaCol;
1834 break;
1836 return sprmNil;
1839 WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) :
1840 mpOldRedlineStack(nullptr),
1841 m_pIo(pIoClass),
1842 m_pFirstBand(nullptr),
1843 m_pActBand(nullptr),
1844 m_pTmpPos(nullptr),
1845 m_pTableNd(nullptr),
1846 m_pTabLines(nullptr),
1847 m_pTabLine(nullptr),
1848 m_pTabBoxes(nullptr),
1849 m_pTabBox(nullptr),
1850 m_pAktWWCell(nullptr),
1851 m_nRows(0),
1852 m_nDefaultSwCols(0),
1853 m_nBands(0),
1854 m_nMinLeft(0),
1855 m_nConvertedLeft(0),
1856 m_nMaxRight(0),
1857 m_nSwWidth(0),
1858 m_nPreferredWidth(0),
1859 m_nOrgDxaLeft(0),
1860 m_bOk(true),
1861 m_bClaimLineFormat(false),
1862 m_eOri(text::HoriOrientation::NONE),
1863 m_bIsBiDi(false),
1864 m_nAktRow(0),
1865 m_nAktBandRow(0),
1866 m_nAktCol(0),
1867 m_nRowsToRepeat(0),
1868 m_pTable(nullptr),
1869 m_pParentPos(nullptr),
1870 m_pFlyFormat(nullptr),
1871 m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),RES_FRMATR_BEGIN,RES_FRMATR_END-1)
1873 m_pIo->m_bAktAND_fNumberAcross = false;
1875 static const sal_Int16 aOriArr[] =
1877 text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
1880 bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
1881 WW8_TablePos aTabPos;
1883 WW8PLCFxSave1 aSave;
1884 m_pIo->m_pPlcxMan->GetPap()->Save( aSave );
1886 WW8PLCFx_Cp_FKP* pPap = m_pIo->m_pPlcxMan->GetPapPLCF();
1888 m_eOri = text::HoriOrientation::LEFT;
1890 WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
1892 wwSprmParser aSprmParser(m_pIo->GetFib());
1894 // process pPap until end of table found
1897 short nTabeDxaNew = SHRT_MAX;
1898 bool bTabRowJustRead = false;
1899 const sal_uInt8* pShadeSprm = nullptr;
1900 const sal_uInt8* pNewShadeSprm = nullptr;
1901 const sal_uInt8* pTableBorders = nullptr;
1902 const sal_uInt8* pTableBorders90 = nullptr;
1903 std::vector<const sal_uInt8*> aTSetBrcs, aTSetBrc90s;
1904 WW8_TablePos *pTabPos = nullptr;
1906 // search end of a tab row
1907 if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
1909 m_bOk = false;
1910 break;
1913 // Get the SPRM chains:
1914 // first from PAP and then from PCD (of the Piece Table)
1915 WW8PLCFxDesc aDesc;
1916 pPap->GetSprms( &aDesc );
1917 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
1919 for (int nLoop = 0; nLoop < 2; ++nLoop)
1921 bool bRepeatedSprm = false;
1922 const sal_uInt8* pParams;
1923 while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetAktParams()))
1925 sal_uInt16 nId = aSprmIter.GetAktId();
1926 wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
1927 switch (eSprm)
1929 case sprmTTableWidth:
1931 const sal_uInt8 b0 = pParams[0];
1932 const sal_uInt8 b1 = pParams[1];
1933 const sal_uInt8 b2 = pParams[2];
1934 if (b0 == 3) // Twips
1935 m_nPreferredWidth = b2 * 0x100 + b1;
1937 break;
1938 case sprmTTextFlow:
1939 pNewBand->ProcessDirection(pParams);
1940 break;
1941 case sprmTFCantSplit:
1942 pNewBand->bCantSplit = *pParams;
1943 m_bClaimLineFormat = true;
1944 break;
1945 case sprmTTableBorders:
1946 pTableBorders = pParams; // process at end
1947 break;
1948 case sprmTTableBorders90:
1949 pTableBorders90 = pParams; // process at end
1950 break;
1951 case sprmTTableHeader:
1952 if (!bRepeatedSprm)
1954 m_nRowsToRepeat++;
1955 bRepeatedSprm = true;
1957 break;
1958 case sprmTJc:
1959 // sprmTJc - Justification Code
1960 if (m_nRows == 0)
1961 m_eOri = aOriArr[*pParams & 0x3];
1962 break;
1963 case sprmTFBiDi:
1964 m_bIsBiDi = SVBT16ToShort(pParams) != 0;
1965 break;
1966 case sprmTDxaGapHalf:
1967 pNewBand->nGapHalf = (sal_Int16)SVBT16ToShort( pParams );
1968 break;
1969 case sprmTDyaRowHeight:
1970 pNewBand->nLineHeight = (sal_Int16)SVBT16ToShort( pParams );
1971 m_bClaimLineFormat = true;
1972 break;
1973 case sprmTDefTable:
1974 pNewBand->ReadDef(bOldVer, pParams);
1975 bTabRowJustRead = true;
1976 break;
1977 case sprmTDefTableShd:
1978 pShadeSprm = pParams;
1979 break;
1980 case sprmTDefTableNewShd:
1981 pNewShadeSprm = pParams;
1982 break;
1983 case sprmTDxaLeft:
1984 // our Writer cannot shift single table lines
1985 // horizontally so we have to find the smallest
1986 // parameter (meaning the left-most position) and then
1987 // shift the whole table to that margin (see below)
1989 short nDxaNew = (sal_Int16)SVBT16ToShort( pParams );
1990 m_nOrgDxaLeft = nDxaNew;
1991 if( nDxaNew < nTabeDxaNew )
1992 nTabeDxaNew = nDxaNew;
1994 break;
1995 case sprmTSetBrc:
1996 aTSetBrcs.push_back(pParams); // process at end
1997 break;
1998 case sprmTSetBrc90:
1999 aTSetBrc90s.push_back(pParams); // process at end
2000 break;
2001 case sprmTDxaCol:
2002 pNewBand->ProcessSprmTDxaCol(pParams);
2003 break;
2004 case sprmTInsert:
2005 pNewBand->ProcessSprmTInsert(pParams);
2006 break;
2007 case sprmTDelete:
2008 pNewBand->ProcessSprmTDelete(pParams);
2009 break;
2010 case sprmTCellPaddingDefault:
2011 pNewBand->ProcessSpacing(pParams);
2012 break;
2013 case sprmTCellPadding:
2014 pNewBand->ProcessSpecificSpacing(pParams);
2015 break;
2016 default:
2019 aSprmIter.advance();
2022 if( !nLoop )
2024 pPap->GetPCDSprms( aDesc );
2025 aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
2029 // WW-Tables can contain Fly-changes. For this abort tables here
2030 // and start again. *pPap is still before TabRowEnd, so TestApo()
2031 // can be called with the last parameter set to false and therefore
2032 // take effect.
2034 if (bTabRowJustRead)
2036 // Some SPRMs need to be processed *after* ReadDef is called
2037 // so they were saved up until here
2038 if (pShadeSprm)
2039 pNewBand->ReadShd(pShadeSprm);
2040 if (pNewShadeSprm)
2041 pNewBand->ReadNewShd(pNewShadeSprm, bOldVer);
2042 if (pTableBorders90)
2043 pNewBand->ProcessSprmTTableBorders(9, pTableBorders90);
2044 else if (pTableBorders)
2045 pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
2046 pTableBorders);
2047 std::vector<const sal_uInt8*>::const_iterator iter;
2048 for (iter = aTSetBrcs.begin(); iter != aTSetBrcs.end(); ++iter)
2049 pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, *iter);
2050 for (iter = aTSetBrc90s.begin(); iter != aTSetBrc90s.end(); ++iter)
2051 pNewBand->ProcessSprmTSetBRC(9, *iter);
2054 if( nTabeDxaNew < SHRT_MAX )
2056 short* pCenter = pNewBand->nCenter;
2057 short firstDxaCenter = *pCenter;
2058 for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
2060 // #i30298# Use sprmTDxaLeft to adjust the left indent
2061 // #i40461# Use dxaGapHalf during calculation
2062 *pCenter +=
2063 (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
2067 if (!m_pActBand)
2068 m_pActBand = m_pFirstBand = pNewBand;
2069 else
2071 m_pActBand->pNextBand = pNewBand;
2072 m_pActBand = pNewBand;
2074 m_nBands++;
2076 pNewBand = new WW8TabBandDesc;
2078 m_nRows++;
2079 m_pActBand->nRows++;
2081 //Seek our pap to its next block of properties
2082 WW8PLCFxDesc aRes;
2083 aRes.pMemPos = nullptr;
2084 aRes.nStartPos = nStartCp;
2086 if (!(pPap->SeekPos(aRes.nStartPos)))
2088 aRes.nEndPos = WW8_CP_MAX;
2089 pPap->SetDirty(true);
2091 pPap->GetSprms(&aRes);
2092 pPap->SetDirty(false);
2094 //Are we at the end of available properties
2095 if (
2096 !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
2097 aRes.nStartPos == WW8_CP_MAX
2100 m_bOk = false;
2101 break;
2104 //Are we still in a table cell
2105 const sal_uInt8* pParams = HasTabCellSprm(pPap, bOldVer);
2106 const sal_uInt8 *pLevel = pPap->HasSprm(0x6649);
2107 // InTable
2108 if (!pParams || (1 != *pParams) ||
2109 (pLevel && (*pLevel <= m_pIo->m_nInTable)))
2111 break;
2114 //Get the end of row new table positioning data
2115 WW8_CP nMyStartCp=nStartCp;
2116 if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
2117 if (m_pIo->ParseTabPos(&aTabPos, pPap))
2118 pTabPos = &aTabPos;
2120 //Move back to this cell
2121 aRes.pMemPos = nullptr;
2122 aRes.nStartPos = nStartCp;
2124 // PlcxMan currently points too far ahead so we need to bring
2125 // it back to where we are trying to make a table
2126 m_pIo->m_pPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
2127 m_pIo->m_pPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
2128 if (!(pPap->SeekPos(aRes.nStartPos)))
2130 aRes.nEndPos = WW8_CP_MAX;
2131 pPap->SetDirty(true);
2133 pPap->GetSprms(&aRes);
2134 pPap->SetDirty(false);
2136 //Does this row match up with the last row closely enough to be
2137 //considered part of the same table
2138 ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
2141 ##513##, #79474# If this is not sufficient, then we should look at
2142 sprmPD{y|x}aAbs as our indicator that the following set of rows is not
2143 part of this table, but instead is an absolutely positioned table
2144 outside of this one
2146 if (aApo.mbStopApo)
2147 break;
2148 if (aApo.mbStartApo)
2150 //if there really is a fly here, and not a "null" fly then break.
2151 WW8FlyPara *pNewFly = m_pIo->ConstructApo(aApo, pTabPos);
2152 if (pNewFly)
2153 delete pNewFly;
2154 else
2155 break;
2158 nStartCp = aRes.nEndPos;
2160 while(true);
2162 if( m_bOk )
2164 if( m_pActBand->nRows > 1 )
2166 // last band has more than 1 cell
2167 delete pNewBand;
2168 pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
2169 m_pActBand->nRows--; // wegen Sonderbehandlung Raender-Defaults
2170 pNewBand->nRows = 1;
2171 m_pActBand->pNextBand = pNewBand; // am Ende einschleifen
2172 m_nBands++;
2173 pNewBand = nullptr; // do not delete
2175 CalcDefaults();
2177 delete pNewBand;
2179 m_pIo->m_pPlcxMan->GetPap()->Restore( aSave );
2182 WW8TabDesc::~WW8TabDesc()
2184 WW8TabBandDesc* pR = m_pFirstBand;
2185 while(pR)
2187 WW8TabBandDesc* pR2 = pR->pNextBand;
2188 delete pR;
2189 pR = pR2;
2192 delete m_pParentPos;
2195 void WW8TabDesc::CalcDefaults()
2197 short nMinCols = SHRT_MAX;
2198 WW8TabBandDesc* pR;
2200 m_nMinLeft = SHRT_MAX;
2201 m_nMaxRight = SHRT_MIN;
2204 If we are an honestly inline centered table, then the normal rules of
2205 engagement for left and right margins do not apply. The multiple rows are
2206 centered regardless of the actual placement of rows, so we cannot have
2207 mismatched rows as is possible in other configurations.
2209 e.g. change the example bugdoc in word from text wrapping of none (inline)
2210 to around (in frame (bApo)) and the table splits into two very disjoint
2211 rows as the beginning point of each row are very different
2213 if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
2215 for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
2216 for( short i = pR->nWwCols; i >= 0; --i)
2217 pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
2220 // 1. Durchlauf: aeusserste L- und R-Grenzen finden
2221 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2223 if( pR->nCenter[0] < m_nMinLeft )
2224 m_nMinLeft = pR->nCenter[0];
2226 // Following adjustment moves a border and then uses it to find width
2227 // of next cell, so collect current widths, to avoid situation when width
2228 // adjustment to too narrow cell makes next cell have negative width
2229 short nOrigWidth[MAX_COL + 1];
2230 for( short i = 0; i < pR->nWwCols; i++ )
2232 nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
2235 for( short i = 0; i < pR->nWwCols; i++ )
2238 If the margins are so large as to make the displayable
2239 area inside them smaller than the minimum allowed then adjust the
2240 width to fit. But only do it if the two cells are not the exact
2241 same value, if they are then the cell does not really exist and will
2242 be blended together into the same cell through the use of the
2243 nTrans(late) array.
2244 #i28333# If the nGapHalf is greater than the cell width best to ignore it
2246 int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
2247 if (nCellWidth != nOrigWidth[i])
2249 if (nOrigWidth[i] == 0)
2250 nCellWidth = 0; // restore zero-width "cell"
2251 else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
2252 nCellWidth = pR->nGapHalf + 1; // avoid false ignore
2253 else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
2254 nCellWidth = 1; // minimal non-zero width to minimize distortion
2256 if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
2258 nCellWidth = MINLAY + pR->nGapHalf * 2;
2260 pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
2263 if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
2264 m_nMaxRight = pR->nCenter[pR->nWwCols];
2266 m_nSwWidth = m_nMaxRight - m_nMinLeft;
2268 // If the table is right aligned we need to align all rows to the
2269 // row that has the furthest right point
2271 if(m_eOri == text::HoriOrientation::RIGHT)
2273 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2275 int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
2276 for( short i = 0; i < pR->nWwCols + 1; i++ )
2278 pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
2284 // 2. pass: Detect number of writer columns. This can exceed the count
2285 // of columns in WW by 2, because SW in constrast to WW does not provide
2286 // fringed left and right borders and has to fill with empty boxes.
2287 // Non existent cells can reduce the number of columns.
2289 // 3. pass: Replace border with defaults if needed
2290 m_nConvertedLeft = m_nMinLeft;
2292 short nLeftMaxThickness = 0, nRightMaxThickness=0;
2293 for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
2295 if( !pR->pTCs )
2297 pR->pTCs = new WW8_TCell[ pR->nWwCols ];
2298 memset( pR->pTCs, 0, pR->nWwCols * sizeof( WW8_TCell ) );
2300 for (int k = 0; k < pR->nWwCols; ++k)
2302 WW8_TCell* pT = &pR->pTCs[k];
2303 int i, j;
2304 for( i = 0; i < 4; i ++ )
2306 if (pT->rgbrc[i].brcType()==0)
2308 // if shadow is set, its invalid
2309 j = i;
2310 switch( i )
2312 case 0:
2313 // outer top / horizontally inside
2314 j = (pR == m_pFirstBand) ? 0 : 4;
2315 break;
2316 case 1:
2317 // outer left / vertically inside
2318 j = k ? 5 : 1;
2319 break;
2320 case 2:
2321 // outer bottom / horizontally inside
2322 j = pR->pNextBand ? 4 : 2;
2323 break;
2324 case 3:
2325 // outer right / vertically inside
2326 j = (k == pR->nWwCols - 1) ? 3 : 5;
2327 break;
2329 // mangel mit Defaults ueber
2330 pT->rgbrc[i] = pR->aDefBrcs[j];
2334 if (pR->nWwCols)
2337 Similar to graphics and other elements word does not totally
2338 factor the width of the border into its calculations of size, we
2339 do so we must adjust out widths and other dimensions to fit. It
2340 appears that what occurs is that the last cell's right margin if
2341 the margin width that is not calculated into winwords table
2342 dimensions, so in that case increase the table to include the
2343 extra width of the right margin.
2345 if ( ! pR->pTCs[pR->nWwCols-1].rgbrc[3].fShadow() )
2347 short nThickness = pR->pTCs[pR->nWwCols-1].rgbrc[3].
2348 DetermineBorderProperties();
2349 pR->nCenter[pR->nWwCols] = pR->nCenter[pR->nWwCols] + nThickness;
2350 if (nThickness > nRightMaxThickness)
2351 nRightMaxThickness = nThickness;
2355 The left space of the table is in nMinLeft, but again this
2356 does not consider the margin thickness to its left in the
2357 placement value, so get the thickness of the left border,
2358 half is placed to the left of the nominal left side, and
2359 half to the right.
2361 if ( ! pR->pTCs[0].rgbrc[1].fShadow() )
2363 short nThickness = pR->pTCs[0].rgbrc[1].
2364 DetermineBorderProperties();
2365 if (nThickness > nLeftMaxThickness)
2366 nLeftMaxThickness = nThickness;
2370 m_nSwWidth = m_nSwWidth + nRightMaxThickness;
2371 m_nMaxRight = m_nMaxRight + nRightMaxThickness;
2372 m_nConvertedLeft = m_nMinLeft-(nLeftMaxThickness/2);
2374 for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
2376 pR->nSwCols = pR->nWwCols;
2377 pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
2378 pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols] - nRightMaxThickness) >= MINLAY;
2380 short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
2381 sal_uInt16 i;
2382 sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
2383 for (i = 0; i < pR->nWwCols; ++i)
2385 pR->nTransCell[i] = (sal_Int8)j;
2386 if ( pR->nCenter[i] < pR->nCenter[i+1] )
2388 pR->bExist[i] = true;
2389 j++;
2391 else
2393 pR->bExist[i] = false;
2394 nAddCols--;
2398 OSL_ENSURE(i,"no columns in row ?");
2401 If the last cell was "false" then there is no valid cell following it,
2402 so the default mapping forward won't work. So map it (and
2403 contiguous invalid cells backwards to the last valid cell instead.)
2405 if (i && !pR->bExist[i-1])
2407 sal_uInt16 k=i-1;
2408 while (k && !pR->bExist[k])
2409 k--;
2410 for (sal_uInt16 n=k+1;n<i;n++)
2411 pR->nTransCell[n] = pR->nTransCell[k];
2414 pR->nTransCell[i++] = (sal_Int8)(j++); // Can exceed by 2 among other
2415 pR->nTransCell[i] = (sal_Int8)j; // things because of bREmptyCol
2417 pR->nSwCols = pR->nSwCols + nAddCols;
2418 if( pR->nSwCols < nMinCols )
2419 nMinCols = pR->nSwCols;
2423 #i9718#
2424 Find the largest of the borders on cells that adjoin top bottom and remove
2425 the val from the top and put in on the bottom cell. I can't seem to make
2426 disjoint upper and lowers to see what happens there.
2429 if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
2430 (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
2431 m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
2433 m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
2434 if( m_nDefaultSwCols == 0 )
2435 m_bOk = false;
2436 m_pActBand = m_pFirstBand;
2437 m_nAktBandRow = 0;
2438 OSL_ENSURE( m_pActBand, "pActBand ist 0" );
2441 void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat)
2443 SwFrameFormat* pApply = pFrameFormat;
2444 if (!pApply )
2445 pApply = m_pTable->GetFrameFormat();
2446 OSL_ENSURE(pApply,"No frame");
2447 pApply->SetFormatAttr(m_aItemSet);
2448 if (pFrameFormat)
2450 SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
2451 aSize.SetHeightSizeType(ATT_MIN_SIZE);
2452 aSize.SetHeight(MINLAY);
2453 pFrameFormat->SetFormatAttr(aSize);
2454 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
2458 void wwSectionManager::PrependedInlineNode(const SwPosition &rPos,
2459 const SwNode &rNode)
2461 OSL_ENSURE(!maSegments.empty(),
2462 "should not be possible, must be at least one segment");
2463 if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.nNode))
2464 maSegments.back().maStart.Assign(rNode);
2467 void WW8TabDesc::CreateSwTable()
2469 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
2471 // if there is already some content on the Node append new node to ensure
2472 // that this content remains ABOVE the table
2473 SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
2474 bool bInsNode = pPoint->nContent.GetIndex() != 0;
2475 bool bSetMinHeight = false;
2478 #i8062#
2479 Set fly anchor to its anchor pos, so that if a table starts immediately
2480 at this position a new node will be inserted before inserting the table.
2482 if (!bInsNode && m_pIo->m_pFormatOfJustInsertedApo)
2484 const SwPosition* pAPos =
2485 m_pIo->m_pFormatOfJustInsertedApo->GetAnchor().GetContentAnchor();
2486 if (pAPos && &pAPos->nNode.GetNode() == &pPoint->nNode.GetNode())
2488 bInsNode = true;
2489 bSetMinHeight = true;
2491 SwFormatSurround aSur(m_pIo->m_pFormatOfJustInsertedApo->GetSurround());
2492 aSur.SetAnchorOnly(true);
2493 m_pIo->m_pFormatOfJustInsertedApo->SetFormatAttr(aSur);
2497 if (bSetMinHeight)
2499 // minimize Fontsize to minimize height growth of the header/footer
2500 // set font size to 1 point to minimize y-growth of Hd/Ft
2501 SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE);
2502 m_pIo->NewAttr( aSz );
2503 m_pIo->m_pCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
2506 if (bInsNode)
2507 m_pIo->AppendTextNode(*pPoint);
2509 m_pTmpPos = new SwPosition( *m_pIo->m_pPaM->GetPoint() );
2511 // The table is small: The number of columns is the lowest count of
2512 // columns of the origin, because inserting is faster than deleting.
2513 // The number of rows is the count of bands because (identically)
2514 // rows of a band can be duplicated easy.
2515 m_pTable = m_pIo->m_rDoc.InsertTable(
2516 SwInsertTableOptions( tabopts::HEADLINE_NO_BORDER, 0 ),
2517 *m_pTmpPos, m_nBands, m_nDefaultSwCols, m_eOri );
2519 OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
2520 if (!m_pTable || !m_pTable->GetFrameFormat())
2521 return;
2523 SwTableNode* pTableNode = m_pTable->GetTableNode();
2524 OSL_ENSURE(pTableNode, "no table node!");
2525 if (pTableNode)
2527 m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(),
2528 *pTableNode);
2531 // Check if the node into which the table should be inserted already
2532 // contains a Pagedesc. If so that Pagedesc would be moved to the
2533 // row after the table, whats wrong. So delete and
2534 // set later to the table format.
2535 if (SwTextNode *const pNd = m_pTmpPos->nNode.GetNode().GetTextNode())
2537 if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
2539 SfxPoolItem *pSetAttr = nullptr;
2540 const SfxPoolItem* pItem;
2541 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false, &pItem))
2543 pSetAttr = new SvxFormatBreakItem( *static_cast<const SvxFormatBreakItem*>(pItem) );
2544 pNd->ResetAttr( RES_BREAK );
2547 // eventually set the PageDesc/Break now to the table
2548 if (pSetAttr)
2550 m_aItemSet.Put(*pSetAttr);
2551 delete pSetAttr;
2556 // total width of table
2557 if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols )
2559 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatFrameSize(ATT_FIX_SIZE, m_nSwWidth));
2560 m_aItemSet.Put(SwFormatFrameSize(ATT_FIX_SIZE, m_nSwWidth));
2563 SvxFrameDirectionItem aDirection(
2564 m_bIsBiDi ? FRMDIR_HORI_RIGHT_TOP : FRMDIR_HORI_LEFT_TOP, RES_FRAMEDIR );
2565 m_pTable->GetFrameFormat()->SetFormatAttr(aDirection);
2567 if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
2569 if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_pSFlyPara->pFlyFormat &&
2570 GetMinLeft())
2572 //If we are inside a frame and we have a border, the frames
2573 //placement does not consider the tables border, which word
2574 //displays outside the frame, so adjust here.
2575 SwFormatHoriOrient aHori(m_pIo->m_pSFlyPara->pFlyFormat->GetHoriOrient());
2576 sal_Int16 eHori = aHori.GetHoriOrient();
2577 if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
2578 (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
2580 //With multiple table, use last table settings. Perhaps
2581 //the maximum is what word does ?
2582 aHori.SetPos(m_pIo->m_pSFlyPara->nXPos + GetMinLeft());
2583 aHori.SetHoriOrient(text::HoriOrientation::NONE);
2584 m_pIo->m_pSFlyPara->pFlyFormat->SetFormatAttr(aHori);
2587 else
2589 //If bApo is set, then this table is being placed in a floating
2590 //frame, and the frame matches the left and right *lines* of the
2591 //table, so the space to the left of the table isn't to be used
2592 //inside the frame, in word the dialog involved greys out the
2593 //ability to set the margin.
2594 SvxLRSpaceItem aL( RES_LR_SPACE );
2595 // set right to original DxaLeft (i28656)
2597 long nLeft = 0;
2598 if (!m_bIsBiDi)
2599 nLeft = GetMinLeft();
2600 else
2602 if (m_nPreferredWidth)
2604 nLeft = m_pIo->m_aSectionManager.GetTextAreaWidth();
2605 nLeft = nLeft - m_nPreferredWidth - m_nOrgDxaLeft;
2607 else
2608 nLeft = -GetMinLeft();
2611 aL.SetLeft(nLeft);
2613 m_aItemSet.Put(aL);
2617 mpOldRedlineStack = m_pIo->m_pRedlineStack;
2618 m_pIo->m_pRedlineStack = new sw::util::RedlineStack(m_pIo->m_rDoc);
2621 void WW8TabDesc::UseSwTable()
2623 // init global Vars
2624 m_pTabLines = &m_pTable->GetTabLines();
2625 m_nAktRow = m_nAktCol = m_nAktBandRow = 0;
2627 m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
2628 GetSttNd()->FindTableNode());
2629 OSL_ENSURE( m_pTableNd, "wo ist mein TabellenNode" );
2631 // #i69519# - Restrict rows to repeat to a decent value
2632 if ( m_nRowsToRepeat == static_cast<sal_uInt16>(m_nRows) )
2633 m_nRowsToRepeat = 1;
2635 m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat );
2636 // insert extra cells if needed and something like this
2637 AdjustNewBand();
2639 WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_pCtrlStck);
2640 m_pIo->m_pCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
2642 // now set the correct PaM and prepare first merger group if any
2643 SetPamInCell(m_nAktCol, true);
2644 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2646 m_pIo->m_bWasTabRowEnd = false;
2647 m_pIo->m_bWasTabCellEnd = false;
2650 void WW8TabDesc::MergeCells()
2652 short nRow;
2654 for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand)
2656 // insert current box into merge group if appropriate.
2657 // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
2658 if( m_pActBand->pTCs )
2660 for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
2661 for( short i = 0; i < m_pActBand->nWwCols; i++ )
2663 WW8SelBoxInfo* pActMGroup = nullptr;
2665 // start a new merge group if appropriate
2667 OSL_ENSURE(nRow < (sal_uInt16)m_pTabLines->size(),
2668 "Too few lines, table ended early");
2669 if (nRow >= (sal_uInt16)m_pTabLines->size())
2670 return;
2671 m_pTabLine = (*m_pTabLines)[ nRow ];
2672 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2674 sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
2675 if (!m_pActBand->bExist[i])
2676 continue;
2677 OSL_ENSURE(nCol < m_pTabBoxes->size(),
2678 "Too few columns, table ended early");
2679 if (nCol >= m_pTabBoxes->size())
2680 return;
2681 m_pTabBox = (*m_pTabBoxes)[nCol];
2682 WW8_TCell& rCell = m_pActBand->pTCs[ i ];
2683 // is this the left upper cell of a merge group ?
2685 bool bMerge = false;
2686 if ( rCell.bVertRestart && !rCell.bMerged )
2687 bMerge = true;
2688 else if (rCell.bFirstMerged && m_pActBand->bExist[i])
2690 // Some tests to avoid merging cells which previously were
2691 // declared invalid because of sharing the exact same dimensions
2692 // as their previous cell
2694 //If theres anything underneath/above we're ok.
2695 if (rCell.bVertMerge || rCell.bVertRestart)
2696 bMerge = true;
2697 else
2699 //If it's a hori merge only, and the only things in
2700 //it are invalid cells then it's already taken care
2701 //of, so don't merge.
2702 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2703 if (m_pActBand->pTCs[ i2 ].bMerged &&
2704 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2706 if (m_pActBand->bExist[i2])
2708 bMerge = true;
2709 break;
2712 else
2713 break;
2717 if (bMerge)
2719 short nX1 = m_pActBand->nCenter[ i ];
2720 short nWidth = m_pActBand->nWidth[ i ];
2722 // 2. create current merge group
2723 pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
2725 // determine size of new merge group
2726 // before inserted the new merge group.
2727 // Needed to correctly locked previously created merge groups.
2728 // Calculate total width and set
2729 short nSizCell = m_pActBand->nWidth[ i ];
2730 for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
2731 if (m_pActBand->pTCs[ i2 ].bMerged &&
2732 !m_pActBand->pTCs[ i2 ].bFirstMerged )
2734 nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
2736 else
2737 break;
2738 pActMGroup->nGroupWidth = nSizCell;
2740 // locked previously created merge groups,
2741 // after determining the size for the new merge group.
2742 // 1. If necessary close old merge group(s) that overlap
2743 // the X-area of the new group
2744 for (;;)
2746 WW8SelBoxInfo* p = FindMergeGroup(
2747 nX1, pActMGroup->nGroupWidth, false );
2748 if (p == nullptr)
2750 break;
2752 p->bGroupLocked = true;
2755 // 3. push to group array
2756 m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
2759 // if necessary add the current box to a merge group
2760 // (that can be a newly created or another group)
2761 UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
2767 //There is a limbo area in word at the end of the row marker
2768 //where properties can live in word, there is no location in
2769 //writer equivalent, so try and park the cursor in the best
2770 //match, see #i23022#/#i18644#
2771 void WW8TabDesc::ParkPaM()
2773 SwTableBox *pTabBox2 = nullptr;
2774 short nRow = m_nAktRow + 1;
2775 if (nRow < (sal_uInt16)m_pTabLines->size())
2777 if (SwTableLine *pLine = (*m_pTabLines)[nRow])
2779 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
2780 pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
2784 if (!pTabBox2 || !pTabBox2->GetSttNd())
2786 MoveOutsideTable();
2787 return;
2790 sal_uLong nSttNd = pTabBox2->GetSttIdx() + 1,
2791 nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
2793 if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd)
2797 m_pIo->m_pPaM->GetPoint()->nNode = nSttNd;
2799 while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
2801 m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
2802 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
2806 void WW8TabDesc::MoveOutsideTable()
2808 OSL_ENSURE(m_pTmpPos && m_pIo, "I've forgotten where the table is anchored");
2809 if (m_pTmpPos && m_pIo)
2810 *m_pIo->m_pPaM->GetPoint() = *m_pTmpPos;
2813 void WW8TabDesc::FinishSwTable()
2815 m_pIo->m_pRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
2816 delete m_pIo->m_pRedlineStack;
2817 m_pIo->m_pRedlineStack = mpOldRedlineStack;
2818 mpOldRedlineStack = nullptr;
2820 WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_pCtrlStck);
2821 m_pIo->m_pCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
2823 MoveOutsideTable();
2824 delete m_pTmpPos;
2825 m_pTmpPos = nullptr;
2827 aDup.Insert(*m_pIo->m_pPaM->GetPoint());
2829 m_pIo->m_bWasTabRowEnd = false;
2830 m_pIo->m_bWasTabCellEnd = false;
2832 m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM);
2834 MergeCells();
2836 // if needed group cells together that should be merged
2837 if (!m_MergeGroups.empty())
2839 // process all merge groups one by one
2840 for (auto const& groupIt : m_MergeGroups)
2842 if((1 < groupIt->size()) && groupIt->row(0)[0])
2844 SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
2845 pNewFormat->SetFormatAttr(SwFormatFrameSize(ATT_VAR_SIZE, groupIt->nGroupWidth, 0));
2846 const sal_uInt16 nRowSpan = groupIt->rowsCount();
2847 for (sal_uInt16 n = 0; n < nRowSpan; ++n)
2849 auto& rRow = groupIt->row(n);
2850 for (size_t i = 0; i<rRow.size(); ++i)
2852 const long nRowSpanSet = (n == 0) && (i == 0) ?
2853 nRowSpan :
2854 ((-1) * (nRowSpan - n));
2855 SwTableBox* pCurrentBox = rRow[i];
2856 pCurrentBox->setRowSpan(nRowSpanSet);
2858 if (i == 0)
2859 pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
2860 else
2862 SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
2863 pFormat->SetFormatAttr(SwFormatFrameSize(ATT_VAR_SIZE, 0, 0));
2869 m_pIo->m_pFormatOfJustInsertedApo = nullptr;
2870 m_MergeGroups.clear();
2874 // browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
2876 // Parameter: nXcenter = center position of asking box
2877 // nWidth = width of asking box
2878 // bExact = flag, if box has to fit into group
2879 // or only has to touch
2881 WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
2883 if (!m_MergeGroups.empty())
2885 // still valid area near the boundery
2886 const short nToleranz = 4;
2887 // box boundery
2888 short nX2 = nX1 + nWidth;
2889 // approximate group boundery
2890 short nGrX1;
2891 short nGrX2;
2893 // improvement: search backwards
2894 for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
2896 // the currently inspected group
2897 WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
2898 if (!rActGroup.bGroupLocked)
2900 // approximate group boundery with room (tolerance) to the *outside*
2901 nGrX1 = rActGroup.nGroupXStart - nToleranz;
2902 nGrX2 = rActGroup.nGroupXStart
2903 +rActGroup.nGroupWidth + nToleranz;
2905 // If box fits report success
2907 if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
2909 return &rActGroup;
2912 // does the box share areas with the group?
2914 if( !bExact )
2916 // successful if nX1 *or* nX2 are inside the group
2917 if( ( ( nX1 > nGrX1 )
2918 && ( nX1 < nGrX2 - 2*nToleranz ) )
2919 || ( ( nX2 > nGrX1 + 2*nToleranz )
2920 && ( nX2 < nGrX2 ) )
2921 // or nX1 and nX2 surround the group
2922 || ( ( nX1 <=nGrX1 )
2923 && ( nX2 >=nGrX2 ) ) )
2925 return &rActGroup;
2931 return nullptr;
2934 bool WW8TabDesc::IsValidCell(short nCol) const
2936 return (static_cast<size_t>(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) &&
2937 m_pActBand->bExist[nCol] &&
2938 (sal_uInt16)m_nAktRow < m_pTabLines->size();
2941 bool WW8TabDesc::InFirstParaInCell() const
2943 //e.g. #i19718#
2944 if (!m_pTabBox || !m_pTabBox->GetSttNd())
2946 OSL_FAIL("Problem with table");
2947 return false;
2950 if (!IsValidCell(GetAktCol()))
2951 return false;
2953 if (m_pIo->m_pPaM->GetPoint()->nNode == m_pTabBox->GetSttIdx() + 1)
2954 return true;
2956 return false;
2959 void WW8TabDesc::StartMiserableHackForUnsupportedDirection(short nWwCol)
2961 OSL_ENSURE(m_pActBand, "Impossible");
2962 if (m_pActBand && m_pActBand->maDirections[nWwCol] == 3)
2964 m_pIo->m_pCtrlStck->NewAttr(*m_pIo->m_pPaM->GetPoint(),
2965 SvxCharRotateItem(900, false, RES_CHRATR_ROTATE));
2969 void WW8TabDesc::EndMiserableHackForUnsupportedDirection(short nWwCol)
2971 OSL_ENSURE(m_pActBand, "Impossible");
2972 if (m_pActBand && m_pActBand->maDirections[nWwCol] == 3)
2973 m_pIo->m_pCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), RES_CHRATR_ROTATE);
2976 void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
2978 OSL_ENSURE( m_pActBand, "pActBand ist 0" );
2979 if (!m_pActBand)
2980 return;
2982 sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
2984 if ((sal_uInt16)m_nAktRow >= m_pTabLines->size())
2986 OSL_ENSURE(false, "Actual row bigger than expected." );
2987 if (bPam)
2988 MoveOutsideTable();
2989 return;
2992 m_pTabLine = (*m_pTabLines)[m_nAktRow];
2993 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
2995 if (nCol >= m_pTabBoxes->size())
2997 if (bPam)
2999 // The first paragraph in a cell with upper autospacing has upper
3000 // spacing set to 0
3001 if (
3002 m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara &&
3003 !m_pIo->m_pWDop->fDontUseHTMLAutoSpacing
3006 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
3009 // The last paragraph in a cell with lower autospacing has lower
3010 // spacing set to 0
3011 if (m_pIo->m_bParaAutoAfter && !m_pIo->m_pWDop->fDontUseHTMLAutoSpacing)
3012 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
3014 ParkPaM();
3016 return;
3018 m_pTabBox = (*m_pTabBoxes)[nCol];
3019 if( !m_pTabBox->GetSttNd() )
3021 OSL_ENSURE(m_pTabBox->GetSttNd(), "Probleme beim Aufbau der Tabelle");
3022 if (bPam)
3023 MoveOutsideTable();
3024 return;
3026 if (bPam)
3028 m_pAktWWCell = &m_pActBand->pTCs[ nWwCol ];
3030 // The first paragraph in a cell with upper autospacing has upper spacing set to 0
3031 if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_pWDop->fDontUseHTMLAutoSpacing)
3032 m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
3034 // The last paragraph in a cell with lower autospacing has lower spacing set to 0
3035 if(m_pIo->m_bParaAutoAfter && !m_pIo->m_pWDop->fDontUseHTMLAutoSpacing)
3036 m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
3038 //We need to set the pPaM on the first cell, invalid
3039 //or not so that we can collect paragraph properties over
3040 //all the cells, but in that case on the valid cell we do not
3041 //want to reset the fmt properties
3042 sal_uLong nSttNd = m_pTabBox->GetSttIdx() + 1,
3043 nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
3044 if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd)
3048 m_pIo->m_pPaM->GetPoint()->nNode = nSttNd;
3050 while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
3051 m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
3052 // Precautionally set now, otherwise the style is not set for cells
3053 // that are inserted for margin balancing.
3054 m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
3055 // because this cells are invisible helper constructions only to simulate
3056 // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
3059 // Better to turn Snap to Grid off for all paragraphs in tables
3060 if(SwTextNode *pNd = m_pIo->m_pPaM->GetNode().GetTextNode())
3062 const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
3063 const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
3065 if(rSnapToGrid.GetValue())
3067 SvxParaGridItem aGridItem( rSnapToGrid );
3068 aGridItem.SetValue(false);
3070 SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
3072 const sal_Int32 nEnd = pGridPos->nContent.GetIndex();
3073 pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0);
3074 m_pIo->m_pCtrlStck->NewAttr(*pGridPos, aGridItem);
3075 pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), nEnd);
3076 m_pIo->m_pCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
3080 StartMiserableHackForUnsupportedDirection(nWwCol);
3084 void WW8TabDesc::InsertCells( short nIns )
3086 m_pTabLine = (*m_pTabLines)[m_nAktRow];
3087 m_pTabBoxes = &m_pTabLine->GetTabBoxes();
3088 m_pTabBox = (*m_pTabBoxes)[0];
3090 m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, static_cast<SwTableBoxFormat*>(m_pTabBox->GetFrameFormat()),
3091 const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
3092 // The third parameter contains the FrameFormat of the boxes.
3093 // Here it is possible to optimize to save (reduce) FrameFormats.
3096 void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
3098 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3099 return; // faked cells -> no border
3101 SvxBoxItem aFormatBox( RES_BOX );
3102 if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
3104 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3105 if (SwWW8ImplReader::IsBorder(pT->rgbrc))
3106 SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
3109 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP))
3111 aFormatBox.SetDistance(
3112 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP],
3113 SvxBoxItemLine::TOP);
3115 else
3116 aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
3117 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM))
3119 aFormatBox.SetDistance(
3120 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM],
3121 SvxBoxItemLine::BOTTOM);
3123 else
3124 aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
3126 // nGapHalf for WW is a *horizontal* gap between table cell and content.
3127 short nLeftDist =
3128 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf;
3129 short nRightDist =
3130 m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf;
3131 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT))
3133 aFormatBox.SetDistance(
3134 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT],
3135 SvxBoxItemLine::LEFT);
3137 else
3138 aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
3139 if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT))
3141 aFormatBox.SetDistance(
3142 m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT],
3143 SvxBoxItemLine::RIGHT);
3145 else
3146 aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
3148 pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
3151 void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
3153 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3154 return; // faked cells -> no color
3156 bool bFound=false;
3157 if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
3159 Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
3160 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND));
3161 bFound = true;
3164 //If there was no new shades, or no new shade setting
3165 if (m_pActBand->pSHDs && !bFound)
3167 WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
3168 if (!rSHD.GetValue()) // auto
3169 return;
3171 SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
3172 pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.aColor, RES_BACKGROUND));
3176 SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
3178 SvxFrameDirection eDir = FRMDIR_ENVIRONMENT;
3179 // 1: Asian layout with rotated CJK characters
3180 // 5: Asian layout
3181 // 3: Western layout rotated by 90 degrees
3182 // 4: Western layout
3183 switch (nCode)
3185 default:
3186 OSL_ENSURE(eDir == 4, "unknown direction code, maybe it's a bitfield");
3187 SAL_FALLTHROUGH;
3188 case 3:
3189 eDir = bIsBiDi ? FRMDIR_HORI_RIGHT_TOP : FRMDIR_HORI_LEFT_TOP; // #i38158# - Consider RTL tables
3190 break;
3191 case 5:
3192 eDir = FRMDIR_VERT_TOP_RIGHT;
3193 break;
3194 case 1:
3195 eDir = FRMDIR_VERT_TOP_RIGHT;
3196 break;
3197 case 4:
3198 eDir = bIsBiDi ? FRMDIR_HORI_RIGHT_TOP : FRMDIR_HORI_LEFT_TOP; // #i38158# - Consider RTL tables
3199 break;
3201 return eDir;
3204 void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx)
3206 if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
3207 return;
3208 SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR);
3209 pBox->GetFrameFormat()->SetFormatAttr(aItem);
3212 void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
3214 if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
3215 return;
3217 sal_Int16 eVertOri=text::VertOrientation::TOP;
3219 if( m_pActBand->pTCs )
3221 WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
3222 switch (pT->nVertAlign)
3224 case 0:
3225 default:
3226 eVertOri = text::VertOrientation::TOP;
3227 break;
3228 case 1:
3229 eVertOri = text::VertOrientation::CENTER;
3230 break;
3231 case 2:
3232 eVertOri = text::VertOrientation::BOTTOM;
3233 break;
3237 pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
3240 void WW8TabDesc::AdjustNewBand()
3242 if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
3243 InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols );
3245 SetPamInCell( 0, false);
3246 OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == (sal_uInt16)m_pActBand->nSwCols,
3247 "Falsche Spaltenzahl in Tabelle" );
3249 if( m_bClaimLineFormat )
3251 m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
3252 SwFormatFrameSize aF( ATT_MIN_SIZE, 0, 0 ); // default
3254 if (m_pActBand->nLineHeight == 0) // 0 = Auto
3255 aF.SetHeightSizeType( ATT_VAR_SIZE );
3256 else
3258 if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
3260 aF.SetHeightSizeType(ATT_FIX_SIZE);
3261 m_pActBand->nLineHeight = -m_pActBand->nLineHeight;
3263 if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
3264 m_pActBand->nLineHeight = MINLAY;
3266 aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
3268 m_pTabLine->GetFrameFormat()->SetFormatAttr(aF);
3271 //Word stores 1 for bCantSplit if the row cannot be split, we set true if
3272 //we can split the row
3273 bool bSetCantSplit = m_pActBand->bCantSplit;
3274 m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit));
3276 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
3277 if( bSetCantSplit && m_pTabLines->size() == 1 )
3278 m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit( !bSetCantSplit ));
3280 short i; // SW-Index
3281 short j; // WW-Index
3282 short nW; // Width
3283 SwFormatFrameSize aFS( ATT_FIX_SIZE );
3284 j = m_pActBand->bLEmptyCol ? -1 : 0;
3286 for( i = 0; i < m_pActBand->nSwCols; i++ )
3288 // set cell width
3289 if( j < 0 )
3290 nW = m_pActBand->nCenter[0] - m_nMinLeft;
3291 else
3293 //Set j to first non invalid cell
3294 while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
3295 j++;
3297 if( j < m_pActBand->nWwCols )
3298 nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3299 else
3300 nW = m_nMaxRight - m_pActBand->nCenter[j];
3301 m_pActBand->nWidth[ j ] = nW;
3304 SwTableBox* pBox = (*m_pTabBoxes)[i];
3305 // could be reduced further by intelligent moving of FrameFormats
3306 pBox->ClaimFrameFormat();
3308 SetTabBorders(pBox, j);
3310 // #i18128# word has only one line between adjoining vertical cells
3311 // we have to mimic this in the filter by picking the larger of the
3312 // sides and using that one on one side of the line (right)
3313 SvxBoxItem aCurrentBox(sw::util::ItemGet<SvxBoxItem>(*(pBox->GetFrameFormat()), RES_BOX));
3314 if (i != 0)
3316 SwTableBox* pBox2 = (*m_pTabBoxes)[i-1];
3317 SvxBoxItem aOldBox(sw::util::ItemGet<SvxBoxItem>(*(pBox2->GetFrameFormat()), RES_BOX));
3318 if( aOldBox.CalcLineWidth(SvxBoxItemLine::RIGHT) > aCurrentBox.CalcLineWidth(SvxBoxItemLine::LEFT) )
3319 aCurrentBox.SetLine(aOldBox.GetLine(SvxBoxItemLine::RIGHT), SvxBoxItemLine::LEFT);
3321 aOldBox.SetLine(nullptr, SvxBoxItemLine::RIGHT);
3322 pBox2->GetFrameFormat()->SetFormatAttr(aOldBox);
3325 pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
3327 SetTabVertAlign(pBox, j);
3328 SetTabDirection(pBox, j);
3329 if( m_pActBand->pSHDs || m_pActBand->pNewSHDs)
3330 SetTabShades(pBox, j);
3331 j++;
3333 aFS.SetWidth( nW );
3334 pBox->GetFrameFormat()->SetFormatAttr( aFS );
3336 // skip non existing cells
3337 while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
3339 m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
3340 j++;
3345 void WW8TabDesc::TableCellEnd()
3347 ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
3349 EndMiserableHackForUnsupportedDirection(m_nAktCol);
3351 // new line/row
3352 if( m_pIo->m_bWasTabRowEnd )
3354 // bWasTabRowEnd will be deactivated in
3355 // SwWW8ImplReader::ProcessSpecial()
3357 sal_uInt16 iCol = GetLogicalWWCol();
3358 if (iCol < m_aNumRuleNames.size())
3360 m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
3361 m_aNumRuleNames.end());
3364 m_nAktCol = 0;
3365 m_nAktRow++;
3366 m_nAktBandRow++;
3367 OSL_ENSURE( m_pActBand , "pActBand ist 0" );
3368 if( m_pActBand )
3370 if( m_nAktRow >= m_nRows ) // nothing to at end of table
3371 return;
3373 bool bNewBand = m_nAktBandRow >= m_pActBand->nRows;
3374 if( bNewBand )
3375 { // new band needed ?
3376 m_pActBand = m_pActBand->pNextBand;
3377 m_nAktBandRow = 0;
3378 OSL_ENSURE( m_pActBand, "pActBand ist 0" );
3379 AdjustNewBand();
3381 else
3383 SwTableBox* pBox = (*m_pTabBoxes)[0];
3384 SwSelBoxes aBoxes;
3385 m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
3389 else
3390 { // new column ( cell )
3391 m_nAktCol++;
3393 SetPamInCell(m_nAktCol, true);
3395 // finish Annotated Level Numbering ?
3396 if (m_pIo->m_bAnl && !m_pIo->m_bAktAND_fNumberAcross && m_pActBand)
3397 m_pIo->StopAllAnl(IsValidCell(m_nAktCol));
3400 // if necessary register the box for the merge group for this column
3401 void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell& rCell,
3402 WW8SelBoxInfo* pActGroup,
3403 SwTableBox* pActBox,
3404 sal_uInt16 nCol )
3406 // check if the box has to be merged
3407 // If cell is the first one to be merged, a new merge group has to be provided.
3408 // E.g., it could be that a cell is the first one to be merged, but no
3409 // new merge group is provided, because the potential other cell to be merged
3410 // doesn't exist - see method <WW8TabDesc::MergeCells>.
3411 if ( m_pActBand->bExist[ nCol ] &&
3412 ( ( rCell.bFirstMerged && pActGroup ) ||
3413 rCell.bMerged ||
3414 rCell.bVertMerge ||
3415 rCell.bVertRestart ) )
3417 // detect appropriate merge group
3418 WW8SelBoxInfo* pTheMergeGroup = nullptr;
3419 if( pActGroup )
3420 // assign group
3421 pTheMergeGroup = pActGroup;
3422 else
3424 // find group
3425 pTheMergeGroup = FindMergeGroup(
3426 m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
3428 if( pTheMergeGroup )
3430 // add current box to merge group
3431 pTheMergeGroup->push_back(pActBox);
3436 sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
3438 sal_uInt16 nCol = 0;
3439 if( m_pActBand && m_pActBand->pTCs)
3441 for( sal_uInt16 iCol = 1; iCol <= m_nAktCol && iCol <= m_pActBand->nWwCols; ++iCol )
3443 if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
3444 ++nCol;
3447 return nCol;
3450 // find name of numrule valid for current WW-COL
3451 OUString WW8TabDesc::GetNumRuleName() const
3453 sal_uInt16 nCol = GetLogicalWWCol();
3454 if (nCol < m_aNumRuleNames.size())
3455 return m_aNumRuleNames[nCol];
3456 return OUString();
3459 void WW8TabDesc::SetNumRuleName( const OUString& rName )
3461 sal_uInt16 nCol = GetLogicalWWCol();
3462 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
3463 m_aNumRuleNames.push_back(OUString());
3464 m_aNumRuleNames[nCol] = rName;
3467 bool SwWW8ImplReader::StartTable(WW8_CP nStartCp)
3469 // Entering a table so make sure the FirstPara flag gets set
3470 m_bFirstPara = true;
3471 // keine rekursiven Tabellen Nicht bei EinfuegenDatei in Tabelle oder
3472 // Fussnote
3473 if (m_bReadNoTable)
3474 return false;
3476 if (m_pTableDesc)
3477 m_aTableStack.push(m_pTableDesc);
3479 // #i33818# - determine absolute position object attributes,
3480 // if possible. It's needed for nested tables.
3481 WW8FlyPara* pTableWFlyPara( nullptr );
3482 WW8SwFlyPara* pTableSFlyPara( nullptr );
3483 // #i45301# - anchor nested table inside Writer fly frame
3484 // only at-character, if absolute position object attributes are available.
3485 // Thus, default anchor type is as-character anchored.
3486 RndStdIds eAnchor( FLY_AS_CHAR );
3487 if ( m_nInTable )
3489 WW8_TablePos* pNestedTabPos( nullptr );
3490 WW8_TablePos aNestedTabPos;
3491 WW8PLCFxSave1 aSave;
3492 m_pPlcxMan->GetPap()->Save( aSave );
3493 WW8PLCFx_Cp_FKP* pPap = m_pPlcxMan->GetPapPLCF();
3494 WW8_CP nMyStartCp = nStartCp;
3495 if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
3496 ParseTabPos( &aNestedTabPos, pPap ) )
3498 pNestedTabPos = &aNestedTabPos;
3500 m_pPlcxMan->GetPap()->Restore( aSave );
3501 if ( pNestedTabPos )
3503 ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
3504 pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
3505 if ( pTableWFlyPara )
3507 // <WW8SwFlyPara> constructor has changed - new 4th parameter
3508 // containing WW8 page top margin.
3509 pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
3510 m_aSectionManager.GetWWPageTopMargin(),
3511 m_aSectionManager.GetPageLeft(), m_aSectionManager.GetTextAreaWidth(),
3512 m_nIniFlyDx, m_nIniFlyDy);
3514 // #i45301# - anchor nested table Writer fly frame at-character
3515 eAnchor = FLY_AT_CHAR;
3519 // if first paragraph in table has break-before-page, transfer that setting to the table itself.
3520 else if( StyleExists(m_nAktColl) )
3522 const SwFormat* pStyleFormat = m_vColl[m_nAktColl].m_pFormat;
3523 if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
3524 NewAttr( pStyleFormat->GetBreak() );
3527 m_pTableDesc = new WW8TabDesc( this, nStartCp );
3529 if( m_pTableDesc->Ok() )
3531 int nNewInTable = m_nInTable + 1;
3533 if ((eAnchor == FLY_AT_CHAR)
3534 && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
3536 m_pTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
3537 SfxItemSet aItemSet(m_rDoc.GetAttrPool(),
3538 RES_FRMATR_BEGIN, RES_FRMATR_END-1);
3539 // #i33818# - anchor the Writer fly frame for the nested table at-character.
3540 // #i45301#
3541 SwFormatAnchor aAnchor( eAnchor );
3542 aAnchor.SetAnchor( m_pTableDesc->m_pParentPos );
3543 aItemSet.Put( aAnchor );
3544 m_pTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
3545 m_pTableDesc->m_pParentPos, &aItemSet);
3546 OSL_ENSURE( m_pTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
3547 "Not the anchor type requested!" );
3548 MoveInsideFly(m_pTableDesc->m_pFlyFormat);
3550 m_pTableDesc->CreateSwTable();
3551 if (m_pTableDesc->m_pFlyFormat)
3553 m_pTableDesc->SetSizePosition(m_pTableDesc->m_pFlyFormat);
3554 // #i33818# - Use absolute position object attributes,
3555 // if existing, and apply them to the created Writer fly frame.
3556 if ( pTableWFlyPara && pTableSFlyPara )
3558 WW8FlySet aFlySet( *this, pTableWFlyPara, pTableSFlyPara, false );
3559 SwFormatAnchor aAnchor( FLY_AT_CHAR );
3560 aAnchor.SetAnchor( m_pTableDesc->m_pParentPos );
3561 aFlySet.Put( aAnchor );
3562 m_pTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
3564 else
3566 SwFormatHoriOrient aHori =
3567 m_pTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
3568 m_pTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
3569 m_pTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( SURROUND_NONE ) );
3571 // #i33818# - The nested table doesn't have to leave
3572 // the table cell. Thus, the Writer fly frame has to follow the text flow.
3573 m_pTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
3575 else
3576 m_pTableDesc->SetSizePosition(nullptr);
3577 m_pTableDesc->UseSwTable();
3579 else
3580 PopTableDesc();
3582 // #i33818#
3583 delete pTableWFlyPara;
3584 delete pTableSFlyPara;
3586 return nullptr != m_pTableDesc;
3589 void SwWW8ImplReader::TabCellEnd()
3591 if (m_nInTable && m_pTableDesc)
3592 m_pTableDesc->TableCellEnd();
3594 m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
3595 m_bReadTable = false;
3596 m_pTableEndPaM.reset();
3599 void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
3601 if( ( nLen > 0 ) && ( *pData == 1 ) )
3602 m_bWasTabCellEnd = true;
3605 void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
3607 if( ( nLen > 0 ) && ( *pData == 1 ) )
3608 m_bWasTabRowEnd = true;
3611 void SwWW8ImplReader::PopTableDesc()
3613 if (m_pTableDesc && m_pTableDesc->m_pFlyFormat)
3615 MoveOutsideFly(m_pTableDesc->m_pFlyFormat,*m_pTableDesc->m_pParentPos);
3618 delete m_pTableDesc;
3619 if (m_aTableStack.empty())
3620 m_pTableDesc = nullptr;
3621 else
3623 m_pTableDesc = m_aTableStack.top();
3624 m_aTableStack.pop();
3628 void SwWW8ImplReader::StopTable()
3630 OSL_ENSURE(m_pTableDesc, "Panic, stop table with no table!");
3631 if (!m_pTableDesc)
3632 return;
3634 // We are leaving a table so make sure the next paragraph doesn't think
3635 // it's the first paragraph
3636 m_bFirstPara = false;
3638 m_pTableDesc->FinishSwTable();
3639 PopTableDesc();
3641 m_bReadTable = true;
3642 // #i101116# - Keep PaM on table end only for nested tables
3643 if ( m_nInTable > 1 )
3645 m_pTableEndPaM.reset(new SwPaM(*m_pPaM, m_pPaM));
3649 bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const
3651 if( !m_pTableDesc )
3652 return false;
3654 const WW8_TCell* pCell = m_pTableDesc->GetAktWWCell();
3656 return !m_pTableDesc->IsValidCell( m_pTableDesc->GetAktCol() )
3657 || ( pCell
3658 && ( !pCell->bFirstMerged
3659 && ( pCell->bMerged
3660 || ( pCell->bVertMerge
3661 && !pCell->bVertRestart
3668 sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
3670 sal_uInt16 nRes = USHRT_MAX;
3671 if( !m_vColl.empty() )
3673 for(sal_uInt16 nI = 0; nI < m_pStyles->GetCount(); nI++ )
3674 if( m_vColl[ nI ].m_bValid
3675 && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
3676 nRes = nI;
3678 return nRes;
3681 const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( OUString& rName ) const
3683 SwFormat* pRet = nullptr;
3684 if( !m_vColl.empty() )
3686 for(sal_uInt16 nI = 0; nI < m_pStyles->GetCount(); nI++ )
3687 if( m_vColl[ nI ].m_bValid
3688 && (rName.equals( m_vColl[ nI ].GetOrgWWName())) )
3690 pRet = m_vColl[ nI ].m_pFormat;
3691 break;
3694 return pRet;
3697 // class WW8RStyle
3699 const sal_uInt8* WW8RStyle::HasParaSprm( sal_uInt16 nId ) const
3701 if( !pParaSprms || !nSprmsLen )
3702 return nullptr;
3704 return maSprmParser.findSprmData(nId, pParaSprms, nSprmsLen);
3707 void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
3709 if (!nLen)
3710 return;
3712 if( bPap )
3714 pParaSprms = pSprms; // for HasParaSprms()
3715 nSprmsLen = nLen;
3718 WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
3719 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
3721 #ifdef DEBUGSPRMREADER
3722 fprintf(stderr, "id is %x\n", aIter.GetAktId());
3723 #endif
3724 pIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetAktId());
3725 aSprmIter.advance();
3728 pParaSprms = nullptr;
3729 nSprmsLen = 0;
3732 void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
3734 if (!nLen)
3735 return;
3737 if (checkSeek(*pStStrm, nPosFc))
3739 std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
3740 nLen = pStStrm->ReadBytes(pSprms.get(), nLen);
3741 ImportSprms(pSprms.get(), nLen, bPap);
3745 static inline short WW8SkipOdd(SvStream* pSt )
3747 if ( pSt->Tell() & 0x1 )
3749 sal_uInt8 c;
3750 return pSt->ReadBytes( &c, 1 );
3752 return 0;
3755 static inline short WW8SkipEven(SvStream* pSt )
3757 if (!(pSt->Tell() & 0x1))
3759 sal_uInt8 c;
3760 return pSt->ReadBytes( &c, 1 );
3762 return 0;
3765 short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
3767 if( 0 < nLen ) // Empty ?
3769 if (bOdd)
3770 nLen = nLen - WW8SkipEven( pStStrm );
3771 else
3772 nLen = nLen - WW8SkipOdd( pStStrm );
3774 sal_Int16 cbUPX(0);
3775 pStStrm->ReadInt16( cbUPX );
3777 nLen-=2;
3779 if ( cbUPX > nLen )
3780 cbUPX = nLen; // shrink cbUPX to nLen
3782 if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
3784 if( bPAP )
3786 sal_uInt16 id;
3787 pStStrm->ReadUInt16( id );
3789 cbUPX-= 2;
3790 nLen-= 2;
3793 if( 0 < cbUPX )
3795 sal_uInt64 const nPos = pStStrm->Tell(); // if something is interpreted wrong,
3796 // this should make it work again
3797 ImportSprms( nPos, cbUPX, bPAP );
3799 if ( pStStrm->Tell() != nPos + cbUPX )
3800 pStStrm->Seek( nPos+cbUPX );
3802 nLen = nLen - cbUPX;
3806 return nLen;
3809 void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
3811 if( nLen <= 0 )
3812 return;
3813 if (bOdd)
3814 nLen = nLen - WW8SkipEven( pStStrm );
3815 else
3816 nLen = nLen - WW8SkipOdd( pStStrm );
3818 if( bPara ) // Grupx.Papx
3819 nLen = ImportUPX(nLen, true, bOdd);
3820 ImportUPX(nLen, false, bOdd); // Grupx.Chpx
3823 WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI)
3824 : WW8Style(*pI->m_pTableStream, _rFib)
3825 , maSprmParser(_rFib)
3826 , pIo(pI)
3827 , pStStrm(pI->m_pTableStream)
3828 , pStyRule(nullptr)
3829 , pParaSprms(nullptr)
3830 , nSprmsLen(0)
3831 , nWwNumLevel(0)
3832 , bTextColChanged(false)
3833 , bFontChanged(false)
3834 , bCJKFontChanged(false)
3835 , bCTLFontChanged(false)
3836 , bFSizeChanged(false)
3837 , bFCTLSizeChanged(false)
3838 , bWidowsChanged(false)
3840 pIo->m_vColl.resize(cstd);
3843 void WW8RStyle::Set1StyleDefaults()
3845 // see #i25247#, #i25561#, #i48064#, #i92341# for default font
3846 if (!bCJKFontChanged) // Style no CJK Font? set the default
3847 pIo->SetNewFontAttr(ftcFE, true, RES_CHRATR_CJK_FONT);
3849 if (!bCTLFontChanged) // Style no CTL Font? set the default
3850 pIo->SetNewFontAttr(ftcBi, true, RES_CHRATR_CTL_FONT);
3852 // western 2nd to make western charset conversion the default
3853 if (!bFontChanged) // Style has no Font? set the default,
3854 pIo->SetNewFontAttr(ftcAsci, true, RES_CHRATR_FONT);
3856 if( !pIo->m_bNoAttrImport )
3858 // Style has no text color set, winword default is auto
3859 if ( !bTextColChanged )
3860 pIo->m_pAktColl->SetFormatAttr(SvxColorItem(Color(COL_AUTO), RES_CHRATR_COLOR));
3862 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3863 if( !bFSizeChanged )
3865 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3866 pIo->m_pAktColl->SetFormatAttr(aAttr);
3867 aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
3868 pIo->m_pAktColl->SetFormatAttr(aAttr);
3871 // Style has no FontSize ? WinWord Default is 10pt for western and asian
3872 if( !bFCTLSizeChanged )
3874 SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
3875 aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
3876 pIo->m_pAktColl->SetFormatAttr(aAttr);
3879 if( !bWidowsChanged ) // Widows ?
3881 pIo->m_pAktColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) );
3882 pIo->m_pAktColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) );
3887 bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle, sal_uInt16 nNextStyle)
3889 SwFormat* pColl;
3890 bool bStyExist;
3892 if (rSI.m_bColl)
3894 // Para-Style
3895 sw::util::ParaStyleMapper::StyleResult aResult =
3896 pIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti);
3897 pColl = aResult.first;
3898 bStyExist = aResult.second;
3900 else
3902 // Char-Style
3903 sw::util::CharStyleMapper::StyleResult aResult =
3904 pIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti);
3905 pColl = aResult.first;
3906 bStyExist = aResult.second;
3909 bool bImport = !bStyExist || pIo->m_bNewDoc; // import content ?
3911 // Do not override character styles the list import code created earlier.
3912 if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
3913 bImport = false;
3915 bool bOldNoImp = pIo->m_bNoAttrImport;
3916 rSI.m_bImportSkipped = !bImport;
3918 if( !bImport )
3919 pIo->m_bNoAttrImport = true;
3920 else
3922 if (bStyExist)
3924 pColl->ResetAllFormatAttr(); // #i73790# - method renamed
3926 pColl->SetAuto(false); // suggested by JP
3927 } // but changes the UI
3928 pIo->m_pAktColl = pColl;
3929 rSI.m_pFormat = pColl; // remember translation WW->SW
3930 rSI.m_bImportSkipped = !bImport;
3932 // Set Based on style
3933 sal_uInt16 j = rSI.m_nBase;
3934 if (j != nThisStyle && j < cstd )
3936 SwWW8StyInf* pj = &pIo->m_vColl[j];
3937 if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
3939 rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
3940 rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet;
3941 rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet;
3942 rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet;
3943 rSI.m_n81Flags = pj->m_n81Flags;
3944 rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags;
3945 if (!rSI.IsWW8BuiltInHeadingStyle())
3947 rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel;
3949 rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore;
3950 rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter;
3952 if (pj->m_xWWFly)
3953 rSI.m_xWWFly.reset(new WW8FlyPara(pIo->m_bVer67, pj->m_xWWFly.get()));
3956 else if( pIo->m_bNewDoc && bStyExist )
3957 rSI.m_pFormat->SetDerivedFrom();
3959 rSI.m_nFollow = nNextStyle; // remember Follow
3961 pStyRule = nullptr; // recreate if necessary
3962 bTextColChanged = bFontChanged = bCJKFontChanged = bCTLFontChanged =
3963 bFSizeChanged = bFCTLSizeChanged = bWidowsChanged = false;
3964 pIo->SetNAktColl( nThisStyle );
3965 pIo->m_bStyNormal = nThisStyle == 0;
3966 return bOldNoImp;
3969 void WW8RStyle::PostStyle(SwWW8StyInf &rSI, bool bOldNoImp)
3971 // Reset attribute flags, because there are no style-ends.
3973 pIo->m_bHasBorder = pIo->m_bSpec = pIo->m_bObj = pIo->m_bSymbol = false;
3974 pIo->m_nCharFormat = -1;
3976 // If Style basiert auf Nichts oder Basis ignoriert
3977 if ((rSI.m_nBase >= cstd || pIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
3979 // If Char-Styles does not work
3980 // -> set hard WW-Defaults
3981 Set1StyleDefaults();
3984 pStyRule = nullptr; // to be on the safe side
3985 pIo->m_bStyNormal = false;
3986 pIo->SetNAktColl( 0 );
3987 pIo->m_bNoAttrImport = bOldNoImp;
3988 // reset the list-remember-fields, if used when reading styles
3989 pIo->m_nLFOPosition = USHRT_MAX;
3990 pIo->m_nListLevel = WW8ListManager::nMaxLevel;
3993 void WW8RStyle::Import1Style( sal_uInt16 nNr )
3995 if (nNr >= pIo->m_vColl.size())
3996 return;
3998 SwWW8StyInf &rSI = pIo->m_vColl[nNr];
4000 if( rSI.m_bImported || !rSI.m_bValid )
4001 return;
4003 rSI.m_bImported = true; // set flag now to avoid endless loops
4005 // valid and not NUL and not yet imported
4007 if( rSI.m_nBase < cstd && !pIo->m_vColl[rSI.m_nBase].m_bImported )
4008 Import1Style( rSI.m_nBase );
4010 pStStrm->Seek( rSI.m_nFilePos );
4012 short nSkip, cbStd;
4013 OUString sName;
4015 std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName, &cbStd));// read Style
4017 if (xStd)
4018 rSI.SetOrgWWIdent( sName, xStd->sti );
4020 // either no Name or unused Slot or unknown Style
4022 if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
4024 pStStrm->SeekRel( nSkip );
4025 return;
4028 bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti), nNr, xStd->istdNext);
4030 // if something is interpreted wrong, this should make it work again
4031 long nPos = pStStrm->Tell();
4033 //Variable parts of the STD start at even byte offsets, but "inside
4034 //the STD", which I take to meaning even in relation to the starting
4035 //position of the STD, which matches findings in #89439#, generally it
4036 //doesn't matter as the STSHI starts off nearly always on an even
4037 //offset
4039 //Import of the Style Contents
4040 ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
4042 PostStyle(rSI, bOldNoImp);
4044 pStStrm->Seek( nPos+nSkip );
4047 void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
4049 if (nNr >= pIo->m_vColl.size())
4050 return;
4052 SwWW8StyInf &rSI = pIo->m_vColl[nNr];
4053 if( rSI.m_bImported || !rSI.m_bValid )
4054 return;
4056 rSI.m_bImported = true;
4058 if( rSI.m_nBase < cstd && !pIo->m_vColl[rSI.m_nBase].m_bImported )
4059 RecursiveReg(rSI.m_nBase);
4061 pIo->RegisterNumFormatOnStyle(nNr);
4066 After all styles are imported then we can recursively apply numbering
4067 styles to them, and change their tab stop settings if they turned out
4068 to have special first line indentation.
4070 void WW8RStyle::PostProcessStyles()
4072 sal_uInt16 i;
4074 Clear all imported flags so that we can recursively apply numbering
4075 formats and use it to mark handled ones
4077 for (i=0; i < cstd; ++i)
4078 pIo->m_vColl[i].m_bImported = false;
4081 Register the num formats and tabstop changes on the styles recursively.
4085 In the same loop apply the tabstop changes required because we need to
4086 change their location if theres a special indentation for the first line,
4087 By avoiding making use of each styles margins during reading of their
4088 tabstops we don't get problems with doubly adjusting tabstops that
4089 are inheritied.
4091 for (i=0; i < cstd; ++i)
4093 if (pIo->m_vColl[i].m_bValid)
4095 RecursiveReg(i);
4100 void WW8RStyle::ScanStyles() // investigate style dependencies
4101 { // and detect Filepos for each Style
4102 for (sal_uInt16 i = 0; i < cstd; ++i)
4104 short nSkip;
4105 SwWW8StyInf &rSI = pIo->m_vColl[i];
4107 rSI.m_nFilePos = pStStrm->Tell(); // remember FilePos
4108 WW8_STD* pStd = Read1Style( nSkip, nullptr, nullptr ); // read STD
4109 rSI.m_bValid = (nullptr != pStd);
4110 if (rSI.m_bValid)
4112 rSI.m_nBase = pStd->istdBase; // remember Basis
4113 rSI.m_bColl = ( pStd->sgc == 1 ); // Para-Style
4115 else
4116 rSI = SwWW8StyInf();
4118 delete pStd;
4119 pStStrm->SeekRel( nSkip ); // skip Names and Sprms
4123 std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
4125 std::vector<sal_uInt8> aRet;
4127 aRet.push_back(60);
4128 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBold) );
4130 aRet.push_back(61);
4131 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalic) );
4133 aRet.push_back(62);
4134 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fStrike) );
4136 aRet.push_back(63);
4137 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fOutline) );
4139 aRet.push_back(65);
4140 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps) );
4142 aRet.push_back(66);
4143 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fCaps) );
4145 aRet.push_back(67);
4146 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fVanish) );
4148 if (rChpx.fsFtc)
4150 aRet.push_back(68);
4151 SVBT16 a;
4152 ShortToSVBT16(rChpx.ftc, a);
4153 aRet.push_back(a[1]);
4154 aRet.push_back(a[0]);
4157 if (rChpx.fsKul)
4159 aRet.push_back(69);
4160 aRet.push_back(rChpx.kul);
4163 if (rChpx.fsLid)
4165 aRet.push_back(72);
4166 SVBT16 a;
4167 ShortToSVBT16(rChpx.lid, a);
4168 aRet.push_back(a[1]);
4169 aRet.push_back(a[0]);
4172 if (rChpx.fsIco)
4174 aRet.push_back(73);
4175 aRet.push_back(rChpx.ico);
4178 if (rChpx.fsHps)
4180 aRet.push_back(74);
4182 SVBT16 a;
4183 ShortToSVBT16(rChpx.hps, a);
4184 aRet.push_back(a[0]);
4187 if (rChpx.fsPos)
4189 aRet.push_back(76);
4190 aRet.push_back(rChpx.hpsPos);
4193 aRet.push_back(80);
4194 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
4196 aRet.push_back(81);
4197 aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
4199 if (rChpx.fsFtcBi)
4201 aRet.push_back(82);
4202 SVBT16 a;
4203 ShortToSVBT16(rChpx.fsFtcBi, a);
4204 aRet.push_back(a[1]);
4205 aRet.push_back(a[0]);
4208 if (rChpx.fsLidBi)
4210 aRet.push_back(83);
4211 SVBT16 a;
4212 ShortToSVBT16(rChpx.lidBi, a);
4213 aRet.push_back(a[1]);
4214 aRet.push_back(a[0]);
4217 if (rChpx.fsIcoBi)
4219 aRet.push_back(84);
4220 aRet.push_back(rChpx.icoBi);
4223 if (rChpx.fsHpsBi)
4225 aRet.push_back(85);
4226 SVBT16 a;
4227 ShortToSVBT16(rChpx.hpsBi, a);
4228 aRet.push_back(a[1]);
4229 aRet.push_back(a[0]);
4232 return aRet;
4235 Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
4237 Word2CHPX aChpx;
4239 if (!nSize)
4240 return aChpx;
4242 rSt.Seek(nOffset);
4244 sal_uInt8 nCount=0;
4246 while (true)
4248 sal_uInt8 nFlags8;
4249 rSt.ReadUChar( nFlags8 );
4250 nCount++;
4252 aChpx.fBold = nFlags8 & 0x01;
4253 aChpx.fItalic = (nFlags8 & 0x02) >> 1;
4254 aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
4255 aChpx.fOutline = (nFlags8 & 0x08) >> 3;
4256 aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
4257 aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
4258 aChpx.fCaps = (nFlags8 & 0x40) >> 6;
4259 aChpx.fVanish = (nFlags8 & 0x80) >> 7;
4261 if (nCount >= nSize) break;
4262 rSt.ReadUChar( nFlags8 );
4263 nCount++;
4265 aChpx.fRMark = nFlags8 & 0x01;
4266 aChpx.fSpec = (nFlags8 & 0x02) >> 1;
4267 aChpx.fStrike = (nFlags8 & 0x04) >> 2;
4268 aChpx.fObj = (nFlags8 & 0x08) >> 3;
4269 aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
4270 aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
4271 aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
4272 aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
4274 if (nCount >= nSize) break;
4275 rSt.ReadUChar( nFlags8 );
4276 nCount++;
4278 aChpx.fsIco = nFlags8 & 0x01;
4279 aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
4280 aChpx.fsHps = (nFlags8 & 0x04) >> 2;
4281 aChpx.fsKul = (nFlags8 & 0x08) >> 3;
4282 aChpx.fsPos = (nFlags8 & 0x10) >> 4;
4283 aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
4284 aChpx.fsLid = (nFlags8 & 0x40) >> 6;
4285 aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
4287 if (nCount >= nSize) break;
4288 rSt.ReadUChar( nFlags8 );
4289 nCount++;
4291 aChpx.fsFtcBi = nFlags8 & 0x01;
4292 aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
4293 aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
4295 if (nCount >= nSize) break;
4296 rSt.ReadUInt16( aChpx.ftc );
4297 nCount+=2;
4299 if (nCount >= nSize) break;
4300 rSt.ReadUInt16( aChpx.hps );
4301 nCount+=2;
4303 if (nCount >= nSize) break;
4304 rSt.ReadUChar( nFlags8 );
4305 nCount++;
4307 aChpx.qpsSpace = nFlags8 & 0x3F;
4308 aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
4309 aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
4311 if (nCount >= nSize) break;
4312 rSt.ReadUChar( nFlags8 );
4313 nCount++;
4315 aChpx.ico = nFlags8 & 0x1F;
4316 aChpx.kul = (nFlags8 & 0xE0) >> 5;
4318 if (nCount >= nSize) break;
4319 rSt.ReadUChar( aChpx.hpsPos );
4320 nCount++;
4322 if (nCount >= nSize) break;
4323 rSt.ReadUChar( aChpx.icoBi );
4324 nCount++;
4326 if (nCount >= nSize) break;
4327 rSt.ReadUInt16( aChpx.lid );
4328 nCount+=2;
4330 if (nCount >= nSize) break;
4331 rSt.ReadUInt16( aChpx.ftcBi );
4332 nCount+=2;
4334 if (nCount >= nSize) break;
4335 rSt.ReadUInt16( aChpx.hpsBi );
4336 nCount+=2;
4338 if (nCount >= nSize) break;
4339 rSt.ReadUInt16( aChpx.lidBi );
4340 nCount+=2;
4342 if (nCount >= nSize) break;
4343 rSt.ReadUInt32( aChpx.fcPic );
4344 nCount+=4;
4346 break;
4349 rSt.SeekRel(nSize-nCount);
4350 return aChpx;
4353 namespace
4355 struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
4358 void WW8RStyle::ImportOldFormatStyles()
4360 for (sal_uInt16 i=0; i < cstd; ++i)
4362 pIo->m_vColl[i].m_bColl = true;
4363 //every chain must end eventually at the null style (style code 222)
4364 pIo->m_vColl[i].m_nBase = 222;
4367 rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
4368 pIo->m_pWwFib->m_chseTables, pIo->m_pWwFib->m_lid);
4370 sal_uInt16 cstcStd(0);
4371 rSt.ReadUInt16( cstcStd );
4373 size_t nMaxByteCount = rSt.remainingSize();
4374 sal_uInt16 cbName(0);
4375 rSt.ReadUInt16(cbName);
4376 if (cbName > nMaxByteCount)
4378 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4379 << cbName << " to " << nMaxByteCount);
4380 cbName = nMaxByteCount;
4382 sal_uInt16 nByteCount = 2;
4383 sal_uInt16 stcp=0;
4384 while (nByteCount < cbName)
4386 sal_uInt8 nCount(0);
4387 rSt.ReadUChar( nCount );
4388 nByteCount++;
4390 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4391 if (stc >=pIo->m_vColl.size())
4392 continue;
4394 SwWW8StyInf &rSI = pIo->m_vColl[stc];
4395 OUString sName;
4397 if (nCount != 0xFF) // undefined style
4399 if (nCount != 0) // user style
4401 OString aTmp = read_uInt8s_ToOString(rSt, nCount);
4402 nByteCount += aTmp.getLength();
4403 sName = OStringToOUString(aTmp, eStructChrSet);
4405 rSI.m_bImported = true;
4408 if (sName.isEmpty())
4410 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4411 if (const sal_Char *pStr = GetEnglishNameFromSti(eSti))
4412 sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
4415 if (sName.isEmpty())
4416 sName = "Unknown Style: " + OUString::number(stc);
4418 rSI.SetOrgWWIdent(sName, stc);
4419 stcp++;
4422 sal_uInt16 nStyles=stcp;
4424 std::vector<pxoffset> aCHPXOffsets(stcp);
4425 nMaxByteCount = rSt.remainingSize();
4426 sal_uInt16 cbChpx(0);
4427 rSt.ReadUInt16(cbChpx);
4428 if (cbChpx > nMaxByteCount)
4430 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4431 << cbChpx << " to " << nMaxByteCount);
4432 cbChpx = nMaxByteCount;
4434 nByteCount = 2;
4435 stcp=0;
4436 std::vector< std::vector<sal_uInt8> > aConvertedChpx;
4437 while (nByteCount < cbChpx)
4439 if (stcp == aCHPXOffsets.size())
4441 //more data than style slots, skip remainder
4442 rSt.SeekRel(cbChpx-nByteCount);
4443 break;
4446 sal_uInt8 cb(0);
4447 rSt.ReadUChar( cb );
4448 nByteCount++;
4450 aCHPXOffsets[stcp].mnSize = 0;
4452 if (cb != 0xFF)
4454 sal_uInt8 nRemainder = cb;
4456 aCHPXOffsets[stcp].mnOffset = rSt.Tell();
4457 aCHPXOffsets[stcp].mnSize = nRemainder;
4459 Word2CHPX aChpx = ReadWord2Chpx(rSt, aCHPXOffsets[stcp].mnOffset,
4460 aCHPXOffsets[stcp].mnSize);
4461 aConvertedChpx.push_back( ChpxToSprms(aChpx) );
4463 nByteCount += nRemainder;
4465 else
4466 aConvertedChpx.push_back( std::vector<sal_uInt8>() );
4468 ++stcp;
4471 std::vector<pxoffset> aPAPXOffsets(stcp);
4472 nMaxByteCount = rSt.remainingSize();
4473 sal_uInt16 cbPapx(0);
4474 rSt.ReadUInt16(cbPapx);
4475 if (cbPapx > nMaxByteCount)
4477 SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
4478 << cbPapx << " to " << nMaxByteCount);
4479 cbPapx = nMaxByteCount;
4481 nByteCount = 2;
4482 stcp=0;
4483 while (nByteCount < cbPapx)
4485 if (stcp == aPAPXOffsets.size())
4487 rSt.SeekRel(cbPapx-nByteCount);
4488 break;
4491 sal_uInt8 cb(0);
4492 rSt.ReadUChar( cb );
4493 nByteCount++;
4495 aPAPXOffsets[stcp].mnSize = 0;
4497 if (cb != 0xFF)
4499 sal_uInt8 stc2(0);
4500 rSt.ReadUChar( stc2 );
4501 rSt.SeekRel(6);
4502 nByteCount+=7;
4503 sal_uInt8 nRemainder = cb-7;
4505 aPAPXOffsets[stcp].mnOffset = rSt.Tell();
4506 aPAPXOffsets[stcp].mnSize = nRemainder;
4508 rSt.SeekRel(nRemainder);
4509 nByteCount += nRemainder;
4512 ++stcp;
4515 sal_uInt16 iMac(0);
4516 rSt.ReadUInt16( iMac );
4518 if (iMac > nStyles) iMac = nStyles;
4520 for (stcp = 0; stcp < iMac; ++stcp)
4522 sal_uInt8 stcNext(0), stcBase(0);
4523 rSt.ReadUChar( stcNext );
4524 rSt.ReadUChar( stcBase );
4526 sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
4529 #i64557# style based on itself
4530 every chain must end eventually at the null style (style code 222)
4532 if (stc == stcBase)
4533 stcBase = 222;
4535 SwWW8StyInf &rSI = pIo->m_vColl[stc];
4536 rSI.m_nBase = stcBase;
4538 ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
4540 if (eSti == ww::stiNil)
4541 continue;
4543 if (stcp >= aPAPXOffsets.size())
4544 continue;
4546 rSI.m_bValid = true;
4548 if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
4549 pIo->m_vColl[stc].m_bColl = false;
4551 bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext);
4553 ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
4554 true);
4556 if (aConvertedChpx[stcp].size() > 0)
4557 ImportSprms(&(aConvertedChpx[stcp][0]),
4558 static_cast< short >(aConvertedChpx[stcp].size()),
4559 false);
4561 PostStyle(rSI, bOldNoImp);
4565 void WW8RStyle::ImportNewFormatStyles()
4567 ScanStyles(); // Scan Based On
4569 for (sal_uInt16 i = 0; i < cstd; ++i) // import Styles
4570 if (pIo->m_vColl[i].m_bValid)
4571 Import1Style( i );
4574 void WW8RStyle::Import()
4576 pIo->m_pDfltTextFormatColl = pIo->m_rDoc.GetDfltTextFormatColl();
4577 pIo->m_pStandardFormatColl =
4578 pIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
4580 if( pIo->m_nIniFlags & WW8FL_NO_STYLES )
4581 return;
4583 if (pIo->m_pWwFib->GetFIBVersion() <= ww::eWW2)
4584 ImportOldFormatStyles();
4585 else
4586 ImportNewFormatStyles();
4588 for (sal_uInt16 i = 0; i < cstd; ++i)
4590 // Follow chain
4591 SwWW8StyInf* pi = &pIo->m_vColl[i];
4592 sal_uInt16 j = pi->m_nFollow;
4593 if( j < cstd )
4595 SwWW8StyInf* pj = &pIo->m_vColl[j];
4596 if ( j != i // rational Index ?
4597 && pi->m_pFormat // Format ok ?
4598 && pj->m_pFormat // Derived-Format ok ?
4599 && pi->m_bColl // only possible for paragraph templates (WW)
4600 && pj->m_bColl ){ // identical Typ ?
4601 static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
4602 *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
4607 // Missing special handling for default character template
4608 // "Absatz-Standardschriftart" ( Style-ID 65 ).
4609 // That is empty by default ( WW6 dt and US ) and not changeable
4610 // via WW-UI so this does not matter.
4611 // This could be done by:
4612 // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
4614 // for e.g. tables an always valid Std-Style is necessary
4616 if( pIo->StyleExists(0) && !pIo->m_vColl.empty() &&
4617 pIo->m_vColl[0].m_pFormat && pIo->m_vColl[0].m_bColl && pIo->m_vColl[0].m_bValid )
4618 pIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(pIo->m_vColl[0].m_pFormat);
4619 else
4620 pIo->m_pDfltTextFormatColl = pIo->m_rDoc.GetDfltTextFormatColl();
4622 // set Hyphenation flag on BASIC para-style
4623 if (pIo->m_bNewDoc && pIo->m_pStandardFormatColl)
4625 if (pIo->m_pWDop->fAutoHyphen
4626 && SfxItemState::SET != pIo->m_pStandardFormatColl->GetItemState(
4627 RES_PARATR_HYPHENZONE, false) )
4629 SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE);
4630 aAttr.GetMinLead() = 2;
4631 aAttr.GetMinTrail() = 2;
4632 aAttr.GetMaxHyphens() = 0;
4634 pIo->m_pStandardFormatColl->SetFormatAttr( aAttr );
4638 Word defaults to ltr not from environment like writer. Regardless of
4639 the page/sections rtl setting the standard style lack of rtl still
4640 means ltr
4642 if (SfxItemState::SET != pIo->m_pStandardFormatColl->GetItemState(RES_FRAMEDIR,
4643 false))
4645 pIo->m_pStandardFormatColl->SetFormatAttr(
4646 SvxFrameDirectionItem(FRMDIR_HORI_LEFT_TOP, RES_FRAMEDIR));
4650 // we do not read styles anymore:
4651 pIo->m_pAktColl = nullptr;
4654 rtl_TextEncoding SwWW8StyInf::GetCharSet() const
4656 if ((m_pFormat) && (m_pFormat->GetFrameDir().GetValue() == FRMDIR_HORI_RIGHT_TOP))
4657 return m_eRTLFontSrcCharSet;
4658 return m_eLTRFontSrcCharSet;
4661 rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
4663 if ((m_pFormat) && (m_pFormat->GetFrameDir().GetValue() == FRMDIR_HORI_RIGHT_TOP))
4664 return m_eRTLFontSrcCharSet;
4665 return m_eCJKFontSrcCharSet;
4668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */