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