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