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