xref: /AOO41X/main/sw/source/core/text/porfld.cxx (revision 54628ca40d27d15cc98fe861da7fff7e60c2f7d6)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include <hintids.hxx>
29 
30 #include <com/sun/star/i18n/ScriptType.hdl>
31 #include <vcl/graph.hxx>
32 #include <editeng/brshitem.hxx>
33 #include <vcl/metric.hxx>
34 #include <vcl/outdev.hxx>
35 #include <viewopt.hxx>  // SwViewOptions
36 #include <txtcfg.hxx>
37 #include <SwPortionHandler.hxx>
38 #include <porlay.hxx>
39 #include <porfld.hxx>
40 #include <inftxt.hxx>
41 #include <blink.hxx>    // pBlink
42 #include <frmtool.hxx>  // DrawGraphic
43 #include <viewsh.hxx>
44 #include <docsh.hxx>
45 #include <doc.hxx>
46 #include "rootfrm.hxx"
47 #include <breakit.hxx>
48 #include <porrst.hxx>
49 #include <porftn.hxx>   // SwFtnPortion
50 #include <accessibilityoptions.hxx>
51 #include <editeng/lrspitem.hxx>
52 
53 #include <unicode/ubidi.h>
54 
55 using namespace ::com::sun::star;
56 
57 /*************************************************************************
58  *                      class SwFldPortion
59  *************************************************************************/
60 
61 SwLinePortion *SwFldPortion::Compress()
62 { return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; }
63 
64 SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const
65 {
66     SwFont *pNewFnt;
67     if( 0 != ( pNewFnt = pFnt ) )
68     {
69         pNewFnt = new SwFont( *pFnt );
70     }
71     // --> OD 2009-11-25 #i107143#
72     // pass placeholder property to created <SwFldPortion> instance.
73     SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
74     // <--
75     pClone->SetNextOffset( nNextOffset );
76     pClone->m_bNoLength = this->m_bNoLength;
77     return pClone;
78 }
79 
80 void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
81 {
82     ASSERT( pFld, "TakeNextOffset: Missing Source" );
83     nNextOffset = pFld->GetNextOffset();
84     aExpand.Erase( 0, nNextOffset );
85     bFollow = sal_True;
86 }
87 
88 SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
89     : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
90       bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
91     , m_bNoLength( sal_False )
92 {
93     SetWhichPor( POR_FLD );
94 }
95 
96 SwFldPortion::SwFldPortion( const SwFldPortion& rFld )
97     : SwExpandPortion( rFld ),
98       aExpand( rFld.GetExp() ),
99       nNextOffset( rFld.GetNextOffset() ),
100       nNextScriptChg( rFld.GetNextScriptChg() ),
101       bFollow( rFld.IsFollow() ),
102       bLeft( rFld.IsLeft() ),
103       bHide( rFld.IsHide() ),
104       bCenter( rFld.IsCenter() ),
105       bHasFollow( rFld.HasFollow() ),
106       bPlaceHolder( rFld.bPlaceHolder )
107     , m_bNoLength( rFld.m_bNoLength )
108 {
109     if ( rFld.HasFont() )
110         pFnt = new SwFont( *rFld.GetFont() );
111     else
112         pFnt = 0;
113 
114     SetWhichPor( POR_FLD );
115 }
116 
117 SwFldPortion::~SwFldPortion()
118 {
119     delete pFnt;
120     if( pBlink )
121         pBlink->Delete( this );
122 }
123 
124 /*************************************************************************
125  *               virtual SwFldPortion::GetViewWidth()
126  *************************************************************************/
127 
128 KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
129 {
130     // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
131     // Moment errechnet werden:
132     SwFldPortion* pThis = (SwFldPortion*)this;
133     if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
134             !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
135     {
136         if( !nViewWidth )
137             pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width();
138     }
139     else
140         pThis->nViewWidth = 0;
141     return nViewWidth;
142 }
143 
144 /*************************************************************************
145  *                 virtual SwFldPortion::Format()
146  *************************************************************************/
147 
148 // 8653: in keinem Fall nur SetLen(0);
149 
150 /*************************************************************************
151  *   Hilfsklasse SwFldSlot
152  **************************************************************************/
153 
154 class SwFldSlot
155 {
156     const XubString *pOldTxt;
157     XubString aTxt;
158     xub_StrLen nIdx;
159     xub_StrLen nLen;
160     sal_Bool bOn;
161     SwTxtFormatInfo *pInf;
162 public:
163     SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor );
164     ~SwFldSlot();
165 };
166 
167 SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor )
168 {
169     bOn = pPor->GetExpTxt( *pNew, aTxt );
170 
171     // Der Text wird ausgetauscht...
172     if( bOn )
173     {
174         pInf = (SwTxtFormatInfo*)pNew;
175         nIdx = pInf->GetIdx();
176         nLen = pInf->GetLen();
177         pOldTxt = &(pInf->GetTxt());
178         pInf->SetLen( aTxt.Len() );
179         if( pPor->IsFollow() )
180         {
181             pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
182             pInf->SetIdx( 0 );
183         }
184         else
185         {
186             XubString aTmp( aTxt );
187             aTxt = *pOldTxt;
188             aTxt.Erase( nIdx, 1 );
189             aTxt.Insert( aTmp, nIdx );
190         }
191         pInf->SetTxt( aTxt );
192     }
193 }
194 
195 SwFldSlot::~SwFldSlot()
196 {
197     if( bOn )
198     {
199         pInf->SetTxt( *pOldTxt );
200         pInf->SetIdx( nIdx );
201         pInf->SetLen( nLen );
202         pInf->SetFakeLineStart( sal_False );
203     }
204 }
205 
206 void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf )
207 {
208     String aTxt;
209     if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() )
210     {
211         sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
212         sal_uInt16 nScript;
213         {
214             nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 );
215             xub_StrLen nChg = 0;
216             if( i18n::ScriptType::WEAK == nScript )
217             {
218                 nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript);
219                 if( nChg < aTxt.Len() )
220                     nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg );
221             }
222 
223             //
224             // nNextScriptChg will be evaluated during SwFldPortion::Format()
225             //
226             if ( nChg < aTxt.Len() )
227                 nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript );
228             else
229                 nNextScriptChg = aTxt.Len();
230 
231         }
232         sal_uInt8 nTmp;
233         switch ( nScript ) {
234             case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
235             case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
236             case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
237             default: nTmp = nActual;
238         }
239 
240         // #i16354# Change script type for RTL text to CTL.
241         const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
242         // --> OD 2009-01-29 #i98418#
243 //        const sal_uInt8 nFldDir = IsNumberPortion() ?
244         const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
245                              rSI.GetDefaultDir() :
246                              rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
247         // <--
248         if ( UBIDI_RTL == nFldDir )
249         {
250             UErrorCode nError = U_ZERO_ERROR;
251             UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError );
252             ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError );
253             int32_t nEnd;
254             UBiDiLevel nCurrDir;
255             ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
256             ubidi_close( pBidi );
257             const xub_StrLen nNextDirChg = (xub_StrLen)nEnd;
258             nNextScriptChg = Min( nNextScriptChg, nNextDirChg );
259 
260             // #i89825# change the script type also to CTL
261             // if there is no strong LTR char in the LTR run (numbers)
262             if ( nCurrDir != UBIDI_RTL )
263             {
264                 nCurrDir = UBIDI_RTL;
265                 for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
266                 {
267                     UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx ));
268                     if ( nCharDir == U_LEFT_TO_RIGHT ||
269                          nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
270                          nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
271                     {
272                         nCurrDir = UBIDI_LTR;
273                         break;
274                     }
275                 }
276             }
277 
278             if ( nCurrDir == UBIDI_RTL )
279                 nTmp = SW_CTL;
280         }
281 
282         // --> OD 2009-01-29 #i98418#
283         // keep determined script type for footnote portions as preferred script type.
284         // For footnote portions a font can not be created directly - see footnote
285         // portion format method.
286 //         if( !IsFtnPortion() && nTmp != nActual )
287         if ( IsFtnPortion() )
288         {
289             dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp );
290         }
291         else if ( nTmp != nActual )
292         {
293             if( !pFnt )
294                 pFnt = new SwFont( *rInf.GetFont() );
295             pFnt->SetActual( nTmp );
296         }
297         // <--
298     }
299 }
300 
301 sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf )
302 {
303     // Scope wegen aDiffTxt::DTOR!
304     xub_StrLen nRest;
305     sal_Bool bFull;
306     sal_Bool bEOL = sal_False;
307     long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx();
308     {
309         SwFldSlot aDiffTxt( &rInf, this );
310         SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
311         aLayoutModeModifier.SetAuto();
312 
313         // Field portion has to be split in several parts if
314         // 1. There are script/direction changes inside the field
315         // 2. There are portion breaks (tab, break) inside the field:
316         const xub_StrLen nOldFullLen = rInf.GetLen();
317         xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
318         if ( nNextScriptChg < nFullLen )
319         {
320             nFullLen = nNextScriptChg;
321             rInf.SetHookChar( 0 );
322         }
323         rInf.SetLen( nFullLen );
324 
325         if ( STRING_LEN != rInf.GetUnderScorePos() &&
326              rInf.GetUnderScorePos() > rInf.GetIdx() )
327              rInf.SetUnderScorePos( rInf.GetIdx() );
328 
329         if( pFnt )
330             pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );
331 
332         SwFontSave aSave( rInf, pFnt );
333 
334         // 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge
335         // gesetzt und wird in nRest uebertragen. Ansonsten bleibt die
336         // Laenge erhalten und wuerde auch in nRest einfliessen!
337         SetLen(0);
338         const MSHORT nFollow = IsFollow() ? 0 : 1;
339 
340         // So komisch es aussieht, die Abfrage auf GetLen() muss wegen der
341         // ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs)
342         // sal_False returnen wegen SetFull ...
343         if( !nFullLen )
344         {
345             // nicht Init(), weil wir Hoehe und Ascent brauchen
346             Width(0);
347             bFull = rInf.Width() <= rInf.GetPos().X();
348         }
349         else
350         {
351             xub_StrLen nOldLineStart = rInf.GetLineStart();
352             if( IsFollow() )
353                 rInf.SetLineStart( 0 );
354             rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow );
355 
356             // the height depending on the fields font is set,
357             // this is required for SwTxtGuess::Guess
358             Height( rInf.GetTxtHeight() );
359             // If a kerning portion is inserted after our field portion,
360             // the ascent and height must be known
361             SetAscent( rInf.GetAscent() );
362             bFull = SwTxtPortion::Format( rInf );
363             rInf.SetNotEOL( sal_False );
364             rInf.SetLineStart( nOldLineStart );
365         }
366         xub_StrLen nTmpLen = GetLen();
367         bEOL = !nTmpLen && nFollow && bFull;
368         nRest = nOldFullLen - nTmpLen;
369 
370         // Das Zeichen wird in der ersten Portion gehalten.
371         // Unbedingt nach Format!
372         SetLen( (m_bNoLength) ? 0 : nFollow );
373 
374         if( nRest )
375         {
376             // aExpand ist noch nicht gekuerzt worden, der neue Ofst
377             // ergibt sich durch nRest.
378             xub_StrLen nNextOfst = aExpand.Len() - nRest;
379 
380             if ( IsQuoVadisPortion() )
381                 nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len();
382 
383             XubString aNew( aExpand, nNextOfst, STRING_LEN );
384             aExpand.Erase( nNextOfst, STRING_LEN );
385 
386             // These characters should not be contained in the follow
387             // field portion. They are handled via the HookChar mechanism.
388             switch( aNew.GetChar( 0 ))
389             {
390                 case CH_BREAK  : bFull = sal_True;
391                             // kein break;
392                 case ' ' :
393                 case CH_TAB    :
394                 case CHAR_HARDHYPHEN:               // non-breaking hyphen
395                 case CHAR_SOFTHYPHEN:
396                 case CHAR_HARDBLANK:
397                 // --> FME 2006-01-11 #i59759# Erase additional control
398                 // characters from field string, otherwise we get stuck in
399                 // a loop.
400                 case CHAR_ZWSP :
401                 case CHAR_ZWNBSP :
402         //        case CHAR_RLM :
403         //        case CHAR_LRM :
404                 // <--
405                 // --> OD 2010-06-03 #i111750#
406                 // - Erasing further control characters from field string in
407                 // to avoid loop.
408                 case CH_TXTATR_BREAKWORD:
409                 case CH_TXTATR_INWORD:
410                 // <--
411                 {
412                     aNew.Erase( 0, 1 );
413                     ++nNextOfst;
414                     break;
415                 }
416                 default: ;
417             }
418 
419             // Even if there is no more text left for a follow field,
420             // we have to build a follow field portion (without font),
421             // otherwise the HookChar mechanism would not work.
422             SwFldPortion *pFld = Clone( aNew );
423             if( aNew.Len() && !pFld->GetFont() )
424             {
425                 SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
426                 pFld->SetFont( pNewFnt );
427             }
428             pFld->SetFollow( sal_True );
429             SetHasFollow( sal_True );
430             // In nNextOffset steht bei einem neuangelegten Feld zunaechst
431             // der Offset, an dem es selbst im Originalstring beginnt.
432             // Wenn beim Formatieren ein FollowFeld angelegt wird, wird
433             // der Offset dieses FollowFelds in nNextOffset festgehalten.
434             nNextOffset = nNextOffset + nNextOfst;
435             pFld->SetNextOffset( nNextOffset );
436             rInf.SetRest( pFld );
437         }
438     }
439 
440     if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
441         rInf.GetLast()->FormatEOL( rInf );
442     return bFull;
443 }
444 
445 /*************************************************************************
446  *               virtual SwFldPortion::Paint()
447  *************************************************************************/
448 
449 void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
450 {
451     SwFontSave aSave( rInf, pFnt );
452 
453     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
454     if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
455     {
456         // Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
457         rInf.DrawViewOpt( *this, POR_FLD );
458         SwExpandPortion::Paint( rInf );
459     }
460 }
461 
462 /*************************************************************************
463  *              virtual SwFldPortion::GetExpTxt()
464  *************************************************************************/
465 
466 sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
467 {
468     rTxt = aExpand;
469     if( !rTxt.Len() && rInf.OnWin() &&
470         !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
471             SwViewOption::IsFieldShadings() &&
472             !HasFollow() )
473         rTxt = ' ';
474     return sal_True;
475 }
476 
477 /*************************************************************************
478  *              virtual SwFldPortion::HandlePortion()
479  *************************************************************************/
480 
481 void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
482 {
483     rPH.Special( GetLen(), aExpand, GetWhichPor() );
484 }
485 
486 /*************************************************************************
487  *                virtual SwFldPortion::GetTxtSize()
488  *************************************************************************/
489 
490 SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
491 {
492     SwFontSave aSave( rInf, pFnt );
493     SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
494     return aSize;
495 }
496 
497 /*************************************************************************
498  *                      class SwHiddenPortion
499  *************************************************************************/
500 
501 SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
502 {
503     SwFont *pNewFnt;
504     if( 0 != ( pNewFnt = pFnt ) )
505         pNewFnt = new SwFont( *pFnt );
506     return new SwHiddenPortion( rExpand, pNewFnt );
507 }
508 
509 /*************************************************************************
510  *               virtual SwHiddenPortion::Paint()
511  *************************************************************************/
512 
513 void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
514 {
515     if( Width() )
516     {
517         SwFontSave aSave( rInf, pFnt );
518         rInf.DrawViewOpt( *this, POR_HIDDEN );
519         SwExpandPortion::Paint( rInf );
520     }
521 }
522 
523 /*************************************************************************
524  *              virtual SwHiddenPortion::GetExpTxt()
525  *************************************************************************/
526 
527 sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
528 {
529     // Nicht auf IsHidden() abfragen !
530     return SwFldPortion::GetExpTxt( rInf, rTxt );
531 }
532 
533 /*************************************************************************
534  *                      class SwNumberPortion
535  *************************************************************************/
536 
537 // --> OD 2008-01-23 #newlistlevelattrs#
538 SwNumberPortion::SwNumberPortion( const XubString &rExpand,
539                                   SwFont *pFont,
540                                   const sal_Bool bLft,
541                                   const sal_Bool bCntr,
542                                   const KSHORT nMinDst,
543                                   const bool bLabelAlignmentPosAndSpaceModeActive )
544         : SwFldPortion( rExpand, pFont ),
545           nFixWidth(0),
546           nMinDist( nMinDst ),
547           // --> OD 2008-01-23 #newlistlevelattrs#
548           mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
549           // <--
550 {
551     SetWhichPor( POR_NUMBER );
552     SetLeft( bLft );
553     SetHide( sal_False );
554     SetCenter( bCntr );
555 }
556 
557 xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
558 {
559     return 0;
560 }
561 
562 SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
563 {
564     SwFont *pNewFnt;
565     if( 0 != ( pNewFnt = pFnt ) )
566         pNewFnt = new SwFont( *pFnt );
567     // --> OD 2008-01-23 #newlistlevelattrs#
568     return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
569                                 nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
570     // <--
571 }
572 
573 /*************************************************************************
574  *                 virtual SwNumberPortion::Format()
575  *************************************************************************/
576 
577 // 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
578 // 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
579 // eingibt, bis die Zeile ueberlaeuft.
580 // Man muss die Fly-Ausweichmanoever beachten!
581 
582 sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
583 {
584     SetHide( sal_False );
585     const sal_Bool bFull = SwFldPortion::Format( rInf );
586     SetLen( 0 );
587     // a numbering portion can be contained in a rotated portion!!!
588     nFixWidth = rInf.IsMulti() ? Height() : Width();
589     rInf.SetNumDone( !rInf.GetRest() );
590     if( rInf.IsNumDone() )
591     {
592 //        SetAscent( rInf.GetAscent() );
593         ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );
594 
595         long nDiff( 0 );
596         // --> OD 2008-01-23 #newlistlevelattrs#
597         if ( !mbLabelAlignmentPosAndSpaceModeActive )
598         {
599             if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
600                  // --> FME 2004-08-13 #i32902#
601                  !IsFtnNumPortion() )
602                  // <--
603             {
604                 nDiff = rInf.Left()
605                     + rInf.GetTxtFrm()->GetTxtNode()->
606                     GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
607                     - rInf.First()
608                     + rInf.ForcedLeftMargin();
609             }
610             else
611             {
612                 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
613             }
614         }
615         // <--
616         // Ein Vorschlag von Juergen und Volkmar:
617         // Der Textteil hinter der Numerierung sollte immer
618         // mindestens beim linken Rand beginnen.
619         if( nDiff < 0 )
620             nDiff = 0;
621         else if ( nDiff > rInf.X() )
622             nDiff -= rInf.X();
623         else
624             nDiff = 0;
625 
626         if( nDiff < nFixWidth + nMinDist )
627             nDiff = nFixWidth + nMinDist;
628         // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
629         // fieser Sonderfall: FlyFrm liegt in dem Bereich,
630         // den wir uns gerade unter den Nagel reissen wollen.
631         // Die NumberPortion wird als verborgen markiert.
632         const sal_Bool bFly = rInf.GetFly() ||
633             ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
634         if( nDiff > rInf.Width() )
635         {
636             nDiff = rInf.Width();
637             if ( bFly )
638                 SetHide( sal_True );
639         }
640 
641         // A numbering portion can be inside a SwRotatedPortion. Then the
642         // Height has to be changed
643         if ( rInf.IsMulti() )
644         {
645             if ( Height() < nDiff )
646                 Height( KSHORT( nDiff ) );
647         }
648         else if( Width() < nDiff )
649             Width( KSHORT(nDiff) );
650     }
651     return bFull;
652 }
653 
654 void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
655 {
656 /*  Ein FormatEOL deutet daraufhin, dass der folgende Text
657  *  nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
658  *  wird diese NumberPortion verborgen.
659  */
660 
661     // This caused trouble with flys anchored as characters.
662     // If one of these is numbered but does not fit to the line,
663     // it calls this function, causing a loop because both the number
664     // portion and the fly portion go to the next line
665 //    SetHide( sal_True );
666 }
667 
668 /*************************************************************************
669  *               virtual SwNumberPortion::Paint()
670  *************************************************************************/
671 
672 void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
673 {
674 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
675  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
676  */
677 
678     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
679     {
680         SwLinePortion *pTmp = GetPortion();
681         while ( pTmp && !pTmp->InTxtGrp() )
682             pTmp = pTmp->GetPortion();
683         if ( !pTmp )
684             return;
685     }
686 
687     // calculate the width of the number portion, including follows
688     const KSHORT nOldWidth = Width();
689     sal_uInt16 nSumWidth = 0;
690     sal_uInt16 nOffset = 0;
691 
692     const SwLinePortion* pTmp = this;
693     while ( pTmp && pTmp->InNumberGrp() )
694     {
695         nSumWidth = nSumWidth + pTmp->Width();
696         if ( ((SwNumberPortion*)pTmp)->HasFollow() )
697             pTmp = pTmp->GetPortion();
698         else
699         {
700             nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
701             break;
702         }
703     }
704 
705     // The master portion takes care for painting the background of the
706     // follow field portions
707     if ( ! IsFollow() )
708     {
709         SwLinePortion *pThis = (SwLinePortion*)this;
710         pThis->Width( nSumWidth );
711         rInf.DrawViewOpt( *this, POR_NUMBER );
712         pThis->Width( nOldWidth );
713     }
714 
715     if( aExpand.Len() )
716     {
717         const SwFont *pTmpFnt = rInf.GetFont();
718         sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
719                                  UNDERLINE_NONE != pTmpFnt->GetOverline()  ||
720                                  STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
721                                  !pTmpFnt->IsWordLineMode();
722         if( bPaintSpace && pFnt )
723             bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
724                             UNDERLINE_NONE != pFnt->GetOverline()  ||
725                             STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
726                             !pFnt->IsWordLineMode();
727 
728         SwFontSave aSave( rInf, pFnt );
729 
730         if( nFixWidth == Width() && ! HasFollow() )
731             SwExpandPortion::Paint( rInf );
732         else
733         {
734             // logisches const: Width wird wieder zurueckgesetzt
735             SwLinePortion *pThis = (SwLinePortion*)this;
736             bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
737             KSHORT nSpaceOffs = nFixWidth;
738             pThis->Width( nFixWidth );
739 
740             if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
741                 ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
742                 SwExpandPortion::Paint( rInf );
743             else
744             {
745                 SwTxtPaintInfo aInf( rInf );
746                 if( nOffset < nMinDist )
747                     nOffset = 0;
748                 else
749                 {
750                     if( IsCenter() )
751                     {
752                         /* #110778# a / 2 * 2 == a is not a tautology */
753                         KSHORT nTmpOffset = nOffset;
754                         nOffset /= 2;
755                         if( nOffset < nMinDist )
756                             nOffset = nTmpOffset - nMinDist;
757                     }
758                     else
759                         nOffset = nOffset - nMinDist;
760                 }
761                 aInf.X( aInf.X() + nOffset );
762                 SwExpandPortion::Paint( aInf );
763                 if( bPaintSpace )
764                     nSpaceOffs = nSpaceOffs + nOffset;
765             }
766             if( bPaintSpace && nOldWidth > nSpaceOffs )
767             {
768                 SwTxtPaintInfo aInf( rInf );
769 static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
770                 aInf.X( aInf.X() + nSpaceOffs );
771 
772                 // --> FME 2005-08-12 #i53199# Adjust position of underline:
773                 if ( rInf.GetUnderFnt() )
774                 {
775                     const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
776                     rInf.GetUnderFnt()->SetPos( aNewPos );
777                 }
778                 // <--
779 
780                 pThis->Width( nOldWidth - nSpaceOffs + 12 );
781                 {
782                     SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
783                     aInf.DrawText( *this, aInf.GetLen(), sal_True );
784                 }
785             }
786             pThis->Width( nOldWidth );
787         }
788     }
789 }
790 
791 
792 /*************************************************************************
793  *                      class SwBulletPortion
794  *************************************************************************/
795 
796 // --> OD 2008-01-23 #newlistlevelattrs#
797 SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
798                                   const XubString& rBulletFollowedBy,
799                                   SwFont *pFont,
800                                   const sal_Bool bLft,
801                                   const sal_Bool bCntr,
802                                   const KSHORT nMinDst,
803                                   const bool bLabelAlignmentPosAndSpaceModeActive )
804     : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
805                        pFont, bLft, bCntr, nMinDst,
806                        bLabelAlignmentPosAndSpaceModeActive )
807 // <--
808 {
809     SetWhichPor( POR_BULLET );
810 }
811 
812 /*************************************************************************
813  *                      class SwGrfNumPortion
814  *************************************************************************/
815 
816 #define GRFNUM_SECURE 10
817 
818 // --> OD 2008-01-23 #newlistlevelattrs#
819 SwGrfNumPortion::SwGrfNumPortion(
820         SwFrm*,
821         const XubString& rGraphicFollowedBy,
822         const SvxBrushItem* pGrfBrush,
823         const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
824         const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
825         const bool bLabelAlignmentPosAndSpaceModeActive ) :
826     SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
827                      bLabelAlignmentPosAndSpaceModeActive ),
828 // <--
829     pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
830 {
831     SetWhichPor( POR_GRFNUM );
832     SetAnimated( sal_False );
833     bReplace = sal_False;
834     if( pGrfBrush )
835     {
836         *pBrush = *pGrfBrush;
837         const Graphic* pGraph = pGrfBrush->GetGraphic();
838         if( pGraph )
839             SetAnimated( pGraph->IsAnimated() );
840         else
841             bReplace = sal_True;
842     }
843     if( pGrfOrient )
844     {
845         nYPos = pGrfOrient->GetPos();
846         eOrient = pGrfOrient->GetVertOrient();
847     }
848     else
849     {
850         nYPos = 0;
851         eOrient = text::VertOrientation::TOP;
852     }
853     Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
854     nFixWidth = Width();
855     nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
856     Height( KSHORT(nGrfHeight) );
857     bNoPaint = sal_False;
858 }
859 
860 SwGrfNumPortion::~SwGrfNumPortion()
861 {
862     if ( IsAnimated() )
863         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
864     delete pBrush;
865 }
866 
867 void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
868 {
869     if ( IsAnimated() )
870         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
871 }
872 
873 sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
874 {
875     SetHide( sal_False );
876     // --> OD 2008-01-29 #newlistlevelattrs#
877 //    Width( nFixWidth );
878     KSHORT nFollowedByWidth( 0 );
879     if ( mbLabelAlignmentPosAndSpaceModeActive )
880     {
881         SwFldPortion::Format( rInf );
882         nFollowedByWidth = Width();
883         SetLen( 0 );
884     }
885     Width( nFixWidth + nFollowedByWidth );
886     // <--
887     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
888     const sal_Bool bFly = rInf.GetFly() ||
889         ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
890     SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
891     if( GetAscent() > Height() )
892         Height( GetAscent() );
893 
894     if( bFull )
895     {
896         Width( rInf.Width() - (KSHORT)rInf.X() );
897         if( bFly )
898         {
899             SetLen( 0 );
900             SetNoPaint( sal_True );
901             rInf.SetNumDone( sal_False );
902             return sal_True;
903         }
904     }
905     rInf.SetNumDone( sal_True );
906     // --> OD 2008-01-23 #newlistlevelattrs#
907 //    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
908     long nDiff = mbLabelAlignmentPosAndSpaceModeActive
909                  ? 0
910                  : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
911     // <--
912     // Ein Vorschlag von Juergen und Volkmar:
913     // Der Textteil hinter der Numerierung sollte immer
914     // mindestens beim linken Rand beginnen.
915     if( nDiff < 0 )
916         nDiff = 0;
917     else if ( nDiff > rInf.X() )
918         nDiff -= rInf.X();
919     if( nDiff < nFixWidth + nMinDist )
920         nDiff = nFixWidth + nMinDist;
921     // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
922     // fieser Sonderfall: FlyFrm liegt in dem Bereich,
923     // den wir uns gerade unter den Nagel reissen wollen.
924     // Die NumberPortion wird als verborgen markiert.
925     if( nDiff > rInf.Width() )
926     {
927         nDiff = rInf.Width();
928         if( bFly )
929             SetHide( sal_True );
930     }
931 
932     if( Width() < nDiff )
933         Width( KSHORT(nDiff) );
934     return bFull;
935 }
936 
937 void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
938 {
939     if( DontPaint() )
940         return;
941 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
942  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
943  */
944     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
945     {
946         SwLinePortion *pTmp = GetPortion();
947         while ( pTmp && !pTmp->InTxtGrp() )
948             pTmp = pTmp->GetPortion();
949         if ( !pTmp )
950             return;
951     }
952     Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
953     long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
954     Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
955 
956     // --> OD 2008-02-05 #newlistlevelattrs#
957     const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
958                               ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
959                               ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
960     // <--
961 
962     if( nFixWidth < Width() && !bTmpLeft )
963     {
964         KSHORT nOffset = Width() - nFixWidth;
965         if( nOffset < nMinDist )
966             nOffset = 0;
967         else
968         {
969             if( IsCenter() )
970             {
971                 nOffset /= 2;
972                 if( nOffset < nMinDist )
973                     nOffset = Width() - nFixWidth - nMinDist;
974             }
975             else
976                 nOffset = nOffset - nMinDist;
977         }
978         aPos.X() += nOffset;
979     }
980 
981     if( bReplace )
982     {
983         KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
984         aSize = Size( nTmpH, nTmpH );
985         aPos.Y() = rInf.Y() - nTmpH;
986     }
987     SwRect aTmp( aPos, aSize );
988 
989     sal_Bool bDraw = sal_True;
990 
991     if ( IsAnimated() )
992     {
993         bDraw = !rInf.GetOpt().IsGraphic();
994         if( !nId )
995         {
996             SetId( long( rInf.GetTxtFrm() ) );
997             rInf.GetTxtFrm()->SetAnimation();
998         }
999         if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
1000         {
1001             rInf.NoteAnimation();
1002             const ViewShell* pViewShell = rInf.GetVsh();
1003 
1004             // virtual device, not pdf export
1005             if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
1006                 pViewShell && pViewShell->GetWin()  )
1007             {
1008                 ( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
1009                 rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
1010             }
1011 
1012 
1013             else if ( pViewShell &&
1014                      !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
1015                      !pViewShell->IsPreView() &&
1016                       // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
1017                       pViewShell->GetWin() )
1018                       // <--
1019             {
1020                 ( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
1021                     (OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
1022             }
1023 
1024             // pdf export, printing, preview, stop animations...
1025             else
1026                 bDraw = sal_True;
1027         }
1028         if( bDraw )
1029             ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
1030     }
1031 
1032     SwRect aRepaint( rInf.GetPaintRect() );
1033     const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
1034     if( rFrm.IsVertical() )
1035     {
1036         rFrm.SwitchHorizontalToVertical( aTmp );
1037         rFrm.SwitchHorizontalToVertical( aRepaint );
1038     }
1039 
1040     if( rFrm.IsRightToLeft() )
1041     {
1042         rFrm.SwitchLTRtoRTL( aTmp );
1043         rFrm.SwitchLTRtoRTL( aRepaint );
1044     }
1045 
1046     if( bDraw && aTmp.HasArea() )
1047         DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
1048             aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1049 }
1050 
1051 void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1052                                long nFlyAsc, long nFlyDesc )
1053 {
1054     if ( GetOrient() != text::VertOrientation::NONE )
1055     {
1056         SetRelPos( 0 );
1057         if ( GetOrient() == text::VertOrientation::CENTER )
1058             SetRelPos( GetGrfHeight() / 2 );
1059         else if ( GetOrient() == text::VertOrientation::TOP )
1060             SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1061         else if ( GetOrient() == text::VertOrientation::BOTTOM )
1062             ;
1063         else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1064             SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1065         else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1066             SetRelPos( nLnAscent );
1067         else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1068             SetRelPos( GetGrfHeight() - nLnDescent );
1069         else
1070         {
1071             if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1072             {
1073                 // wenn ich genauso gross bin wie die Zeile, brauche ich mich
1074                 // nicht an der Zeile nicht weiter ausrichten, ich lasse
1075                 // dann auch den max. Ascent der Zeile unveraendert
1076 
1077                 SetRelPos( nFlyAsc );
1078             }
1079             else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1080                 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1081             else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1082                 SetRelPos( nFlyAsc );
1083             else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1084                 SetRelPos( GetGrfHeight() - nFlyDesc );
1085         }
1086     }
1087 }
1088 
1089 void SwTxtFrm::StopAnimation( OutputDevice* pOut )
1090 {
1091     ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
1092     if( HasPara() )
1093     {
1094         SwLineLayout *pLine = GetPara();
1095         while( pLine )
1096         {
1097             SwLinePortion *pPor = pLine->GetPortion();
1098             while( pPor )
1099             {
1100                 if( pPor->IsGrfNumPortion() )
1101                     ((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
1102                 // Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
1103                 // deshalb koennen wir abbrechen, sobald wir eine Portion mit
1104                 // einer Laenge > 0 erreicht haben.
1105                 pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
1106             }
1107             pLine = pLine->GetLen() ? 0 : pLine->GetNext();
1108         }
1109     }
1110 }
1111 
1112 /*************************************************************************
1113  * SwCombinedPortion::SwCombinedPortion(..)
1114  * initializes the script array and clears the width array
1115  *************************************************************************/
1116 
1117 SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
1118      : SwFldPortion( rTxt )
1119 {
1120     SetLen(1);
1121     SetWhichPor( POR_COMBINED );
1122     if( aExpand.Len() > 6 )
1123         aExpand.Erase( 6 );
1124     // Initialization of the scripttype array,
1125     // the arrays of width and position are filled by the format function
1126     if( pBreakIt->GetBreakIter().is() )
1127     {
1128         sal_uInt8 nScr = SW_SCRIPTS;
1129         for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
1130         {
1131             sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
1132             switch ( nScript ) {
1133                 case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
1134                 case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
1135                 case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
1136             }
1137             aScrType[i] = nScr;
1138         }
1139     }
1140     else
1141     {
1142         for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
1143             ; // nothing
1144     }
1145     memset( &aWidth, 0, sizeof(aWidth) );
1146 }
1147 
1148 /*************************************************************************
1149  * SwCombinedPortion::Paint(..)
1150  *************************************************************************/
1151 
1152 void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
1153 {
1154     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
1155     if( Width() )
1156     {
1157         rInf.DrawBackBrush( *this );
1158         rInf.DrawViewOpt( *this, POR_FLD );
1159 
1160         // do we have to repaint a post it portion?
1161         if( rInf.OnWin() && pPortion && !pPortion->Width() )
1162             pPortion->PrePaint( rInf, this );
1163 
1164         sal_uInt16 nCount = aExpand.Len();
1165         if( !nCount )
1166             return;
1167         ASSERT( nCount < 7, "Too much combined characters" );
1168 
1169         // the first character of the second row
1170         sal_uInt16 nTop = ( nCount + 1 ) / 2;
1171 
1172         SwFont aTmpFont( *rInf.GetFont() );
1173         aTmpFont.SetProportion( nProportion );  // a smaller font
1174         SwFontSave aFontSave( rInf, &aTmpFont );
1175 
1176         sal_uInt16 i = 0;
1177         Point aOldPos = rInf.GetPos();
1178         Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1179         while( i < nCount )
1180         {
1181             if( i == nTop ) // change the row
1182                 aOutPos.Y() = aOldPos.Y() + nLowPos;    // Y of the second row
1183             aOutPos.X() = aOldPos.X() + aPos[i];        // X position
1184             const sal_uInt8 nAct = aScrType[i];             // script type
1185             aTmpFont.SetActual( nAct );
1186             // if there're more than 4 characters to display, we choose fonts
1187             // with 2/3 of the original font width.
1188             if( aWidth[ nAct ] )
1189             {
1190                 Size aTmpSz = aTmpFont.GetSize( nAct );
1191                 if( aTmpSz.Width() != aWidth[ nAct ] )
1192                 {
1193                     aTmpSz.Width() = aWidth[ nAct ];
1194                     aTmpFont.SetSize( aTmpSz, nAct );
1195                 }
1196             }
1197             ((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
1198             rInf.DrawText( aExpand, *this, i, 1 );
1199             ++i;
1200         }
1201         // rInf is const, so we have to take back our manipulations
1202         ((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
1203     }
1204 }
1205 
1206 /*************************************************************************
1207  * SwCombinedPortion::Format(..)
1208  *************************************************************************/
1209 
1210 sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
1211 {
1212     sal_uInt16 nCount = aExpand.Len();
1213     if( !nCount )
1214     {
1215         Width( 0 );
1216         return sal_False;
1217     }
1218 
1219     ASSERT( nCount < 7, "Too much combined characters" );
1220     // If there are leading "weak"-scripttyped characters in this portion,
1221     // they get the actual scripttype.
1222     sal_uInt16 i = 0;
1223     while( i < nCount && SW_SCRIPTS == aScrType[i] )
1224         aScrType[i++] = rInf.GetFont()->GetActual();
1225     if( nCount > 4 )
1226     {
1227         // more than four? Ok, then we need the 2/3 font width
1228         i = 0;
1229         while( i < aExpand.Len() )
1230         {
1231             ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1232             if( !aWidth[ aScrType[i] ] )
1233             {
1234                 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1235                 aWidth[ aScrType[i] ] =
1236                         static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
1237             }
1238             ++i;
1239         }
1240     }
1241 
1242     sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1243     ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
1244     SwFont aTmpFont( *rInf.GetFont() );
1245     SwFontSave aFontSave( rInf, &aTmpFont );
1246     nProportion = 55;
1247     // In nMainAscent/Descent we store the ascent and descent
1248     // of the original surrounding font
1249     sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1250     sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1251     const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1252     nMainDescent = nMainDescent - nMainAscent;
1253     // we start with a 50% font, but if we notice that the combined portion
1254     // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1255     do
1256     {
1257         nProportion -= 5;
1258         aTmpFont.SetProportion( nProportion );
1259         i = 0;
1260         memset( &aPos, 0, sizeof(aPos) );
1261         nMaxDescent = 0;
1262         nMaxAscent = 0;
1263         nMaxWidth = 0;
1264         nUpPos = nLowPos = 0;
1265 
1266         // Now we get the width of all characters.
1267         // The ascent and the width of the first line are stored in the
1268         // ascent member of the portion, the descent in nLowPos.
1269         // The ascent, descent and width of the second line are stored in the
1270         // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1271         while( i < nCount )
1272         {
1273             sal_uInt8 nScrp = aScrType[i];
1274             aTmpFont.SetActual( nScrp );
1275             if( aWidth[ nScrp ] )
1276             {
1277                 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1278                 aFontSize.Width() = aWidth[ nScrp ];
1279                 aTmpFont.SetSize( aFontSize, nScrp );
1280             }
1281 
1282             SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
1283             Size aSize = aTmpFont._GetTxtSize( aDrawInf );
1284             sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1285             aPos[ i ] = (sal_uInt16)aSize.Width();
1286             if( i == nTop ) // enter the second line
1287             {
1288                 nLowPos = nMaxDescent;
1289                 Height( nMaxDescent + nMaxAscent );
1290                 Width( nMaxWidth );
1291                 SetAscent( nMaxAscent );
1292                 nMaxAscent = 0;
1293                 nMaxDescent = 0;
1294                 nMaxWidth = 0;
1295             }
1296             nMaxWidth = nMaxWidth + aPos[ i++ ];
1297             if( nAsc > nMaxAscent )
1298                 nMaxAscent = nAsc;
1299             if( aSize.Height() - nAsc > nMaxDescent )
1300                 nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1301         }
1302         // for one or two characters we double the width of the portion
1303         if( nCount < 3 )
1304         {
1305             nMaxWidth *= 2;
1306             Width( 2*Width() );
1307             if( nCount < 2 )
1308             {
1309                 Height( nMaxAscent + nMaxDescent );
1310                 nLowPos = nMaxDescent;
1311             }
1312         }
1313         Height( Height() + nMaxDescent + nMaxAscent );
1314         nUpPos = nMaxAscent;
1315         SetAscent( Height() - nMaxDescent - nLowPos );
1316     } while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1317                                     Height() - GetAscent() > nMainDescent ) );
1318     // if the combined portion is smaller than the surrounding text,
1319     // the portion grows. This looks better, if there's a character background.
1320     if( GetAscent() < nMainAscent )
1321     {
1322         Height( Height() + nMainAscent - GetAscent() );
1323         SetAscent( nMainAscent );
1324     }
1325     if( Height() < nMainAscent + nMainDescent )
1326         Height( nMainAscent + nMainDescent );
1327 
1328     // We calculate the x positions of the characters in both lines..
1329     sal_uInt16 nTopDiff = 0;
1330     sal_uInt16 nBotDiff = 0;
1331     if( nMaxWidth > Width() )
1332     {
1333         nTopDiff = ( nMaxWidth - Width() ) / 2;
1334         Width( nMaxWidth );
1335     }
1336     else
1337         nBotDiff = ( Width() - nMaxWidth ) / 2;
1338     switch( nTop)
1339     {
1340         case 3: aPos[1] = aPos[0] + nTopDiff;  // no break
1341         case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1342     }
1343     aPos[0] = 0;
1344     switch( nCount )
1345     {
1346         case 5: aPos[4] = aPos[3] + nBotDiff;   // no break
1347         case 3: aPos[nTop] = nBotDiff;          break;
1348         case 6: aPos[4] = aPos[3] + nBotDiff;   // no break
1349         case 4: aPos[nTop] = 0;                 // no break
1350         case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1351     }
1352 
1353     // Does the combined portion fit the line?
1354     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
1355     if( bFull )
1356     {
1357         if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFldGrp()
1358             || !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
1359             Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
1360         else
1361         {
1362             Truncate();
1363             Width( 0 );
1364             SetLen( 0 );
1365             if( rInf.GetLast() )
1366                 rInf.GetLast()->FormatEOL( rInf );
1367         }
1368     }
1369     return bFull;
1370 }
1371 
1372 /*************************************************************************
1373  * SwCombinedPortion::GetViewWidth(..)
1374  *************************************************************************/
1375 
1376 KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
1377 {
1378     if( !GetLen() ) // for the dummy part at the end of the line, where
1379         return 0;   // the combined portion doesn't fit.
1380     return SwFldPortion::GetViewWidth( rInf );
1381 }
1382