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