1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 #include <comphelper/string.hxx>
22 #include <editeng/tstpitem.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <IDocumentSettingAccess.hxx>
25 #include <doc.hxx>
26 #include <SwPortionHandler.hxx>
27 
28 #include <viewopt.hxx>
29 #include "portab.hxx"
30 #include "inftxt.hxx"
31 #include "itrform2.hxx"
32 #include <txtfrm.hxx>
33 #include "porfld.hxx"
34 #include <memory>
35 
36 /**
37  * #i24363# tab stops relative to indent
38  *
39  * Return the first tab stop that is > nSearchPos.
40  * If the tab stop is outside the print area, we
41  * return 0 if it is not the first tab stop.
42  */
GetTabStop(const SwTwips nSearchPos,const SwTwips nRight) const43 const SvxTabStop *SwLineInfo::GetTabStop( const SwTwips nSearchPos, const SwTwips nRight ) const
44 {
45     for( sal_uInt16 i = 0; i < m_pRuler->Count(); ++i )
46     {
47         const SvxTabStop &rTabStop = m_pRuler->operator[](i);
48         if( rTabStop.GetTabPos() > SwTwips(nRight) )
49             return i ? nullptr : &rTabStop;
50 
51         if( rTabStop.GetTabPos() > nSearchPos )
52             return &rTabStop;
53     }
54     return nullptr;
55 }
56 
NumberOfTabStops() const57 sal_uInt16 SwLineInfo::NumberOfTabStops() const
58 {
59     return m_pRuler->Count();
60 }
61 
NewTabPortion(SwTextFormatInfo & rInf,bool bAuto) const62 SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const
63 {
64     // Update search location - since Center/Decimal tabstops' width is dependent on the following text.
65     SwTabPortion* pTmpLastTab = rInf.GetLastTab();
66     if (pTmpLastTab && (pTmpLastTab->IsTabCenterPortion() || pTmpLastTab->IsTabDecimalPortion()))
67         pTmpLastTab->PostFormat(rInf);
68 
69     sal_Unicode cFill = 0;
70     sal_Unicode cDec = 0;
71     SvxTabAdjust eAdj;
72 
73     sal_uInt16 nNewTabPos;
74     bool bAutoTabStop = true;
75     {
76         const bool bRTL = m_pFrame->IsRightToLeft();
77         // #i24363# tab stops relative to indent
78         // nTabLeft: The absolute value, the tab stops are relative to: Tabs origin.
79 
80         // #i91133#
81         const bool bTabsRelativeToIndent =
82             m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT);
83         const SwTwips nTabLeft = bRTL
84                                  ? m_pFrame->getFrameArea().Right() -
85                                    ( bTabsRelativeToIndent ? GetTabLeft() : 0 )
86                                  : m_pFrame->getFrameArea().Left() +
87                                    ( bTabsRelativeToIndent ? GetTabLeft() : 0 );
88 
89         // The absolute position, where we started the line formatting
90         SwTwips nLinePos = GetLeftMargin();
91         if ( bRTL )
92         {
93             Point aPoint( nLinePos, 0 );
94             m_pFrame->SwitchLTRtoRTL( aPoint );
95             nLinePos = aPoint.X();
96         }
97 
98         // The current position, relative to the line start
99         SwTwips nTabPos = rInf.GetLastTab() ? rInf.GetLastTab()->GetTabPos() : 0;
100         if( nTabPos < rInf.X() )
101         {
102             nTabPos = rInf.X();
103         }
104 
105         // The current position in absolute coordinates
106         const SwTwips nCurrentAbsPos = bRTL ?
107                                        nLinePos - nTabPos :
108                                        nLinePos + nTabPos;
109 
110         SwTwips nMyRight;
111         if ( m_pFrame->IsVertLR() )
112            nMyRight = Left();
113         else
114            nMyRight = Right();
115 
116         if ( m_pFrame->IsVertical() )
117         {
118             Point aRightTop( nMyRight, m_pFrame->getFrameArea().Top() );
119             m_pFrame->SwitchHorizontalToVertical( aRightTop );
120             nMyRight = aRightTop.Y();
121         }
122 
123         SwTwips nNextPos = 0;
124 
125         // #i24363# tab stops relative to indent
126         // nSearchPos: The current position relative to the tabs origin
127         const SwTwips nSearchPos = bRTL ?
128                                    nTabLeft - nCurrentAbsPos :
129                                    nCurrentAbsPos - nTabLeft;
130 
131         // First, we examine the tab stops set at the paragraph style or
132         // any hard set tab stops:
133         // Note: If there are no user defined tab stops, there is always a
134         // default tab stop.
135         const SvxTabStop* pTabStop = m_aLineInf.GetTabStop( nSearchPos, nMyRight );
136         if ( pTabStop )
137         {
138             cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0;
139             cDec = pTabStop->GetDecimal();
140             eAdj = pTabStop->GetAdjustment();
141             nNextPos = pTabStop->GetTabPos();
142             if(!bTabsRelativeToIndent && eAdj == SvxTabAdjust::Default && nSearchPos < 0)
143             {
144                 //calculate default tab position of default tabs in negative indent
145                 nNextPos = ( nSearchPos / nNextPos ) * nNextPos;
146             }
147             bAutoTabStop = false;
148         }
149         else
150         {
151             sal_uInt16 nDefTabDist = m_aLineInf.GetDefTabStop();
152             if( USHRT_MAX == nDefTabDist )
153             {
154                 const SvxTabStopItem& rTab =
155                     m_pFrame->GetAttrSet()->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
156                 if( rTab.Count() )
157                     nDefTabDist = o3tl::narrowing<sal_uInt16>(rTab[0].GetTabPos());
158                 else
159                     nDefTabDist = SVX_TAB_DEFDIST;
160                 m_aLineInf.SetDefTabStop( nDefTabDist );
161             }
162             SwTwips nCount = nSearchPos;
163 
164             // Minimum tab stop width is 1
165             if (nDefTabDist <= 0)
166                 nDefTabDist = 1;
167 
168             nCount /= nDefTabDist;
169             nNextPos = ( nCount < 0 || ( !nCount && nSearchPos <= 0 ) )
170                        ? ( nCount * nDefTabDist )
171                        : ( ( nCount + 1 ) * nDefTabDist );
172 
173             // --> FME 2004-09-21 #117919 Minimum tab stop width is 1 or 51 twips:
174             const SwTwips nMinimumTabWidth = m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) ? 0 : 50;
175             if( (  bRTL && nTabLeft - nNextPos >= nCurrentAbsPos - nMinimumTabWidth ) ||
176                  ( !bRTL && nNextPos + nTabLeft <= nCurrentAbsPos + nMinimumTabWidth  ) )
177             {
178                 nNextPos += nDefTabDist;
179             }
180             cFill = 0;
181             eAdj = SvxTabAdjust::Left;
182         }
183 
184         // #i115705# - correction and refactoring:
185         // overrule determined next tab stop position in order to apply
186         // a tab stop at the left margin under the following conditions:
187         // - the new tab portion is inside the hanging indent
188         // - a tab stop at the left margin is allowed
189         // - the determined next tab stop is a default tab stop position OR
190         //   the determined next tab stop is beyond the left margin
191         {
192             tools::Long nLeftMarginTabPos = 0;
193             {
194                 if ( !bTabsRelativeToIndent )
195                 {
196                     if ( bRTL )
197                     {
198                         Point aPoint( Left(), 0 );
199                         m_pFrame->SwitchLTRtoRTL( aPoint );
200                         nLeftMarginTabPos = m_pFrame->getFrameArea().Right() - aPoint.X();
201                     }
202                     else
203                     {
204                         nLeftMarginTabPos = Left() - m_pFrame->getFrameArea().Left();
205                     }
206                 }
207                 if( m_pCurr->HasForcedLeftMargin() )
208                 {
209                     SwLinePortion* pPor = m_pCurr->GetNextPortion();
210                     while( pPor && !pPor->IsFlyPortion() )
211                     {
212                         pPor = pPor->GetNextPortion();
213                     }
214                     if ( pPor )
215                     {
216                         nLeftMarginTabPos += pPor->Width();
217                     }
218                 }
219             }
220             const bool bNewTabPortionInsideHangingIndent =
221                         bRTL ? nCurrentAbsPos > nTabLeft - nLeftMarginTabPos
222                              : nCurrentAbsPos < nTabLeft + nLeftMarginTabPos;
223             if ( bNewTabPortionInsideHangingIndent )
224             {
225                 // If the paragraph is not inside a list having a list tab stop following
226                 // the list label or no further tab stop found in such a paragraph or
227                 // the next tab stop position does not equal the list tab stop,
228                 // a tab stop at the left margin can be applied. If this condition is
229                 // not hold, it is overruled by compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST.
230                 const bool bTabAtLeftMarginAllowed =
231                     ( !m_aLineInf.IsListTabStopIncluded() ||
232                       !pTabStop ||
233                       nNextPos != m_aLineInf.GetListTabStopPosition() ) ||
234                     // compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST:
235                     m_pFrame->GetDoc().getIDocumentSettingAccess().
236                         get(DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST);
237                 if ( bTabAtLeftMarginAllowed )
238                 {
239                     if ( !pTabStop || eAdj == SvxTabAdjust::Default ||
240                          ( nNextPos > nLeftMarginTabPos ) )
241                     {
242                         eAdj = SvxTabAdjust::Default;
243                         cFill = 0;
244                         nNextPos = nLeftMarginTabPos;
245                     }
246                 }
247             }
248         }
249 
250         nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos;
251         OSL_ENSURE( nNextPos >= 0, "GetTabStop: Don't go back!" );
252         nNewTabPos = sal_uInt16(nNextPos);
253     }
254 
255     SwTabPortion *pTabPor = nullptr;
256     if ( bAuto )
257     {
258         if ( SvxTabAdjust::Decimal == eAdj &&
259              1 == m_aLineInf.NumberOfTabStops() )
260             pTabPor = new SwAutoTabDecimalPortion( nNewTabPos, cDec, cFill );
261     }
262     else
263     {
264         switch( eAdj )
265         {
266         case SvxTabAdjust::Right :
267             {
268                 pTabPor = new SwTabRightPortion( nNewTabPos, cFill );
269                 break;
270             }
271         case SvxTabAdjust::Center :
272             {
273                 pTabPor = new SwTabCenterPortion( nNewTabPos, cFill );
274                 break;
275             }
276         case SvxTabAdjust::Decimal :
277             {
278                 pTabPor = new SwTabDecimalPortion( nNewTabPos, cDec, cFill );
279                 break;
280             }
281         default:
282             {
283                 OSL_ENSURE( SvxTabAdjust::Left == eAdj || SvxTabAdjust::Default == eAdj,
284                     "+SwTextFormatter::NewTabPortion: unknown adjustment" );
285                 pTabPor = new SwTabLeftPortion( nNewTabPos, cFill, bAutoTabStop );
286                 break;
287             }
288         }
289     }
290 
291     return pTabPor;
292 }
293 
294 /**
295  * The base class is initialized without setting anything
296  */
SwTabPortion(const sal_uInt16 nTabPosition,const sal_Unicode cFillChar,const bool bAutoTab)297 SwTabPortion::SwTabPortion( const sal_uInt16 nTabPosition, const sal_Unicode cFillChar, const bool bAutoTab )
298     : SwFixPortion(), m_nTabPos(nTabPosition), m_cFill(cFillChar), m_bAutoTabStop( bAutoTab )
299 {
300     mnLineLength = TextFrameIndex(1);
301     OSL_ENSURE(!IsFilled() || ' ' != m_cFill, "SwTabPortion::CTOR: blanks ?!");
302     SetWhichPor( PortionType::Table );
303 }
304 
Format(SwTextFormatInfo & rInf)305 bool SwTabPortion::Format( SwTextFormatInfo &rInf )
306 {
307     SwTabPortion *pLastTab = rInf.GetLastTab();
308     if( pLastTab == this )
309         return PostFormat( rInf );
310     if( pLastTab )
311         pLastTab->PostFormat( rInf );
312     return PreFormat( rInf );
313 }
314 
FormatEOL(SwTextFormatInfo & rInf)315 void SwTabPortion::FormatEOL( SwTextFormatInfo &rInf )
316 {
317     if( rInf.GetLastTab() == this )
318         PostFormat( rInf );
319 }
320 
PreFormat(SwTextFormatInfo & rInf)321 bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
322 {
323     OSL_ENSURE( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
324 
325     // Here we settle down ...
326     SetFix( o3tl::narrowing<sal_uInt16>(rInf.X()) );
327 
328     IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
329     const bool bTabCompat = rIDSA.get(DocumentSettingId::TAB_COMPAT);
330     const bool bTabOverflow = rIDSA.get(DocumentSettingId::TAB_OVERFLOW);
331     const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
332     const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
333 
334     // The minimal width of a tab is one blank at least.
335     // #i37686# In compatibility mode, the minimum width
336     // should be 1, even for non-left tab stops.
337     sal_uInt16 nMinimumTabWidth = 1;
338     if ( !bTabCompat )
339     {
340         // #i89179#
341         // tab portion representing the list tab of a list label gets the
342         // same font as the corresponding number portion
343         std::unique_ptr< SwFontSave > pSave;
344         if ( GetLen() == TextFrameIndex(0) &&
345              rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
346              static_cast<SwNumberPortion*>(rInf.GetLast())->HasFont() )
347         {
348             const SwFont* pNumberPortionFont =
349                     static_cast<SwNumberPortion*>(rInf.GetLast())->GetFont();
350             pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
351         }
352         OUString aTmp( ' ' );
353         SwTextSizeInfo aInf( rInf, &aTmp );
354         nMinimumTabWidth = aInf.GetTextSize().Width();
355     }
356     PrtWidth( nMinimumTabWidth );
357 
358     // Break tab stop to next line if:
359     // 1. Minimal width does not fit to line anymore.
360     // 2. An underflow event was called for the tab portion.
361     bool bFull = ( bTabCompat && rInf.IsUnderflow() ) ||
362                      ( rInf.Width() <= rInf.X() + PrtWidth() && rInf.X() <= rInf.Width() ) ;
363 
364     // #95477# Rotated tab stops get the width of one blank
365     const Degree10 nDir = rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() );
366 
367     if( ! bFull && 0_deg10 == nDir )
368     {
369         const PortionType nWhich = GetWhichPor();
370         switch( nWhich )
371         {
372             case PortionType::TabRight:
373             case PortionType::TabDecimal:
374             case PortionType::TabCenter:
375             {
376                 if( PortionType::TabDecimal == nWhich )
377                     rInf.SetTabDecimal(
378                         static_cast<SwTabDecimalPortion*>(this)->GetTabDecimal());
379                 rInf.SetLastTab( this );
380                 break;
381             }
382             case PortionType::TabLeft:
383             {
384                 // handle this case in PostFormat
385                 if ((bTabOverMargin || bTabOverSpacing) && !m_bAutoTabStop && GetTabPos() > rInf.Width())
386                 {
387                     if (bTabOverMargin || GetTabPos() < rInf.GetTextFrame()->getFrameArea().Width())
388                     {
389                         rInf.SetLastTab(this);
390                         break;
391                     }
392                 }
393 
394                 PrtWidth( o3tl::narrowing<sal_uInt16>(GetTabPos() - rInf.X()) );
395                 bFull = rInf.Width() <= rInf.X() + PrtWidth();
396 
397                 // In tabulator compatibility mode, we reset the bFull flag
398                 // if the tabulator is at the end of the paragraph and the
399                 // tab stop position is outside the frame:
400                 bool bAtParaEnd = rInf.GetIdx() + GetLen() == TextFrameIndex(rInf.GetText().getLength());
401                 if ( bFull && bTabCompat &&
402                      ( ( bTabOverflow && ( rInf.IsTabOverflow() || !m_bAutoTabStop ) ) || bAtParaEnd ) &&
403                      GetTabPos() >= rInf.GetTextFrame()->getFrameArea().Width() )
404                 {
405                     bFull = false;
406                     if ( bTabOverflow && !m_bAutoTabStop )
407                         rInf.SetTabOverflow( true );
408                 }
409 
410                 break;
411             }
412             default: OSL_ENSURE( false, "SwTabPortion::PreFormat: unknown adjustment" );
413         }
414     }
415 
416     if( bFull )
417     {
418         // We have to look for endless loops, if the width is smaller than one blank
419         if( rInf.GetIdx() == rInf.GetLineStart() &&
420             // #119175# TabStop should be forced to current
421             // line if there is a fly reducing the line width:
422             !rInf.GetFly() )
423         {
424             PrtWidth( o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X()) );
425             SetFixWidth( PrtWidth() );
426         }
427         else
428         {
429             Height( 0 );
430             Width( 0 );
431             SetLen( TextFrameIndex(0) );
432             SetAscent( 0 );
433             SetNextPortion( nullptr ); //?????
434         }
435         return true;
436     }
437     else
438     {
439         // A trick with impact: The new Tabportions now behave like
440         // FlyFrames, located in the line - including adjustment !
441         SetFixWidth( PrtWidth() );
442         return false;
443     }
444 }
445 
PostFormat(SwTextFormatInfo & rInf)446 bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
447 {
448     bool bTabOverMargin = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
449         DocumentSettingId::TAB_OVER_MARGIN);
450     bool bTabOverSpacing = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
451         DocumentSettingId::TAB_OVER_SPACING);
452     if (rInf.GetTextFrame()->IsInSct())
453         bTabOverMargin = false;
454 
455     // If the tab position is larger than the right margin, it gets scaled down by default.
456     // However, if compat mode enabled, we allow tabs to go over the margin: the rest of the paragraph is not broken into lines.
457     const sal_uInt16 nRight
458         = bTabOverMargin
459               ? GetTabPos()
460               : bTabOverSpacing
461                     ? std::min<long>(GetTabPos(), rInf.GetTextFrame()->getFrameArea().Right())
462                     : std::min(GetTabPos(), rInf.Width());
463     const SwLinePortion *pPor = GetNextPortion();
464 
465     sal_uInt16 nPorWidth = 0;
466     while( pPor )
467     {
468         nPorWidth = nPorWidth + pPor->Width();
469         pPor = pPor->GetNextPortion();
470     }
471 
472     const PortionType nWhich = GetWhichPor();
473     const bool bTabCompat = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
474 
475     if ((bTabOverMargin || bTabOverSpacing) && PortionType::TabLeft == nWhich)
476     {
477         nPorWidth = 0;
478     }
479 
480     // #127428# Abandon dec. tab position if line is full
481     if ( bTabCompat && PortionType::TabDecimal == nWhich )
482     {
483         sal_uInt16 nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition();
484 
485         // no value was set => no decimal character was found
486         if ( USHRT_MAX != nPrePorWidth )
487         {
488             if ( !bTabOverMargin && nPrePorWidth && nPorWidth - nPrePorWidth > rInf.Width() - nRight )
489             {
490                 nPrePorWidth += nPorWidth - nPrePorWidth - ( rInf.Width() - nRight );
491             }
492 
493             nPorWidth = nPrePorWidth - 1;
494         }
495     }
496 
497     if( PortionType::TabCenter == nWhich )
498     {
499         // centered tabs are problematic:
500         // We have to detect how much fits into the line.
501         sal_uInt16 nNewWidth = nPorWidth /2;
502         if (!bTabOverMargin && !bTabOverSpacing && nNewWidth > rInf.Width() - nRight)
503             nNewWidth = nPorWidth - (rInf.Width() - nRight);
504         nPorWidth = nNewWidth;
505     }
506 
507     const sal_uInt16 nDiffWidth = nRight - GetFix();
508 
509     if( nDiffWidth > nPorWidth )
510     {
511         const sal_uInt16 nOldWidth = GetFixWidth();
512         const sal_uInt16 nAdjDiff = nDiffWidth - nPorWidth;
513         if( nAdjDiff > GetFixWidth() )
514             PrtWidth( nAdjDiff );
515         // Don't be afraid: we have to move rInf further.
516         // The right-tab till now only had the width of one blank.
517         // Now that we stretched, the difference had to be added to rInf.X() !
518         rInf.X( rInf.X() + PrtWidth() - nOldWidth );
519     }
520     SetFixWidth( PrtWidth() );
521     // reset last values
522     rInf.SetLastTab(nullptr);
523     if( PortionType::TabDecimal == nWhich )
524         rInf.SetTabDecimal(0);
525 
526     return rInf.Width() <= rInf.X();
527 }
528 
529 /**
530  * Ex: LineIter::DrawTab()
531  */
Paint(const SwTextPaintInfo & rInf) const532 void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
533 {
534     // #i89179#
535     // tab portion representing the list tab of a list label gets the
536     // same font as the corresponding number portion
537     std::unique_ptr< SwFontSave > pSave;
538     bool bAfterNumbering = false;
539     if (GetLen() == TextFrameIndex(0))
540     {
541         const SwLinePortion* pPrevPortion =
542             const_cast<SwTabPortion*>(this)->FindPrevPortion( rInf.GetParaPortion() );
543         if ( pPrevPortion &&
544              pPrevPortion->InNumberGrp() &&
545              static_cast<const SwNumberPortion*>(pPrevPortion)->HasFont() )
546         {
547             const SwFont* pNumberPortionFont =
548                     static_cast<const SwNumberPortion*>(pPrevPortion)->GetFont();
549             pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
550             bAfterNumbering = true;
551         }
552     }
553     rInf.DrawBackBrush( *this );
554     if( !bAfterNumbering )
555         rInf.DrawBorder( *this );
556 
557     // do we have to repaint a post it portion?
558     if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
559         mpNextPortion->PrePaint( rInf, this );
560 
561     // display special characters
562     if( rInf.OnWin() && rInf.GetOpt().IsTab() )
563     {
564         // filled tabs are shaded in gray
565         if( IsFilled() )
566             rInf.DrawViewOpt( *this, PortionType::Table );
567         else
568             rInf.DrawTab( *this );
569     }
570 
571     // Tabs should be underlined at once
572     if( rInf.GetFont()->IsPaintBlank() )
573     {
574         // Tabs with filling/filled tabs
575         const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(' ')).Width();
576 
577         // Robust:
578         if( nCharWidth )
579         {
580             // Always with kerning, also on printer!
581             sal_uInt16 nChar = Width() / nCharWidth;
582             OUStringBuffer aBuf;
583             comphelper::string::padToLength(aBuf, nChar, ' ');
584             rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
585                             TextFrameIndex(nChar), true);
586         }
587     }
588 
589     // Display fill characters
590     if( !IsFilled() )
591         return;
592 
593     // Tabs with filling/filled tabs
594     const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(m_cFill)).Width();
595     OSL_ENSURE( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" );
596 
597     // Robust:
598     if( nCharWidth )
599     {
600         // Always with kerning, also on printer!
601         sal_uInt16 nChar = Width() / nCharWidth;
602         if ( m_cFill == '_' )
603             ++nChar; // to avoid gaps
604         OUStringBuffer aBuf;
605         comphelper::string::padToLength(aBuf, nChar, m_cFill);
606         rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
607                         TextFrameIndex(nChar), true);
608     }
609 }
610 
Paint(const SwTextPaintInfo &) const611 void SwAutoTabDecimalPortion::Paint( const SwTextPaintInfo & ) const
612 {
613 }
614 
HandlePortion(SwPortionHandler & rPH) const615 void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const
616 {
617     rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
618 }
619 
620 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
621