xref: /trunk/main/sw/source/core/text/txthyph.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #include <hintids.hxx>
33 #include <editeng/unolingu.hxx>
34 #include <com/sun/star/i18n/WordType.hpp>
35 #include <EnhancedPDFExportHelper.hxx>
36 #include <viewopt.hxx>  // SwViewOptions
37 #include <viewsh.hxx>
38 #include <errhdl.hxx>
39 #include <txtcfg.hxx>
40 #include <SwPortionHandler.hxx>
41 #include <porhyph.hxx>  //
42 #include <inftxt.hxx>
43 #include <itrform2.hxx> //
44 #include <guess.hxx>    //
45 #include <splargs.hxx>  // SwInterHyphInfo
46 
47 #ifdef DBG_UTIL
48 extern const sal_Char *GetLangName( const MSHORT nLang );
49 #endif
50 
51 using ::rtl::OUString;
52 using namespace ::com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::beans;
55 using namespace ::com::sun::star::linguistic2;
56 using namespace ::com::sun::star::i18n;
57 
58 /*************************************************************************
59  *                      SwTxtFormatInfo::HyphWord()
60  *************************************************************************/
61 
62 Reference< XHyphenatedWord >  SwTxtFormatInfo::HyphWord(
63                                 const XubString &rTxt, const MSHORT nMinTrail )
64 {
65     if( rTxt.Len() < 4 || pFnt->IsSymbol(pVsh) )
66         return 0;
67 //  ASSERT( IsHyphenate(), "SwTxtFormatter::HyphWord: why?" );
68     Reference< XHyphenator >  xHyph = ::GetHyphenator();
69     Reference< XHyphenatedWord > xHyphWord;
70 
71     if( xHyph.is() )
72         xHyphWord = xHyph->hyphenate( OUString(rTxt),
73                             pBreakIt->GetLocale( pFnt->GetLanguage() ),
74                             rTxt.Len() - nMinTrail, GetHyphValues() );
75     return xHyphWord;
76 
77 }
78 
79 /*************************************************************************
80  *                      SwTxtFrm::Hyphenate
81  *
82  * Wir formatieren eine Zeile fuer die interaktive Trennung
83  *************************************************************************/
84 
85 sal_Bool SwTxtFrm::Hyphenate( SwInterHyphInfo &rHyphInf )
86 {
87     ASSERT( ! IsVertical() || ! IsSwapped(),"swapped frame at SwTxtFrm::Hyphenate" );
88 
89     if( !pBreakIt->GetBreakIter().is() )
90         return sal_False;;
91     // Wir machen den Laden erstmal dicht:
92     ASSERT( !IsLocked(), "SwTxtFrm::Hyphenate: this is locked" );
93     // 4935: Der frame::Frame muss eine gueltige SSize haben!
94     Calc();
95     GetFormatted();
96 
97     sal_Bool bRet = sal_False;
98     if( !IsEmpty() )
99     {
100         // Wir muessen die Trennung immer einschalten.
101         // Keine Angst, der SwTxtIter sichert im Hyphenate die alte Zeile.
102         SwTxtFrmLocker aLock( this );
103 
104         if ( IsVertical() )
105             SwapWidthAndHeight();
106 
107         SwTxtFormatInfo aInf( this, sal_True );     // sal_True fuer interactive hyph!
108         SwTxtFormatter aLine( this, &aInf );
109         aLine.CharToLine( rHyphInf.nStart );
110         // Wenn wir innerhalb des ersten Wortes einer Zeile stehen, so koennte
111         // dieses in der vorherigen getrennt werden, deshalb gehen wir ein Zeile
112         // zurueck.
113         if( aLine.Prev() )
114         {
115             SwLinePortion *pPor = aLine.GetCurr()->GetFirstPortion();
116             while( pPor->GetPortion() )
117                 pPor = pPor->GetPortion();
118             if( pPor->GetWhichPor() == POR_SOFTHYPH ||
119                 pPor->GetWhichPor() == POR_SOFTHYPHSTR )
120                 aLine.Next();
121         }
122 
123         const xub_StrLen nEnd = rHyphInf.GetEnd();
124         while( !bRet && aLine.GetStart() < nEnd )
125         {
126             DBG_LOOP;
127             bRet = aLine.Hyphenate( rHyphInf );
128             if( !aLine.Next() )
129                 break;
130         }
131 
132         if ( IsVertical() )
133             SwapWidthAndHeight();
134     }
135     return bRet;
136 }
137 
138 /*************************************************************************
139  *                      SwTxtFormatter::Hyphenate
140  *
141  * Wir formatieren eine Zeile fuer die interaktive Trennung
142  *************************************************************************/
143 // Wir koennen davon ausgehen, dass bereits formatiert wurde.
144 // Fuer die CeBIT'93 gehen wir den einfachen, sicheren Weg:
145 // Die Zeile wird einfach neu formatiert, der Hyphenator wird dann
146 // so vorbereitet, wie ihn die UI erwartet.
147 // Hier stehen natuerlich enorme Optimierungsmoeglichkeiten offen.
148 
149 void SetParaPortion( SwTxtInfo *pInf, SwParaPortion *pRoot )
150 {
151     ASSERT( pRoot, "SetParaPortion: no root anymore" );
152     pInf->pPara = pRoot;
153 }
154 
155 sal_Bool SwTxtFormatter::Hyphenate( SwInterHyphInfo &rHyphInf )
156 {
157     SwTxtFormatInfo &rInf = GetInfo();
158     sal_Bool bRet = sal_False;
159 
160     // In der letzten Zeile gibt es nie etwas zu trennen.
161     // Es sei denn, es befindet sich eine FlyPortion darin,
162     // oder es ist die letzte Zeile des Masters
163     if( !GetNext() && !rInf.GetTxtFly()->IsOn() && !pFrm->GetFollow() )
164         return bRet;
165 
166     xub_StrLen nWrdStart = nStart;
167 
168     // Wir muessen die alte Zeile erhalten. Ein Beispiel:
169     // Das Attribut fuer Trennung wurde nicht gesetzt,
170     // in SwTxtFrm::Hyphenate wird es jedoch immer gesetzt,
171     // weil wir Trennpositionen im Hyphenator einstellen wollen.
172     SwLineLayout *pOldCurr = pCurr;
173 
174     InitCntHyph();
175 
176     // 5298: IsParaLine() (ex.IsFirstLine) fragt auf GetParaPortion() ab.
177     // wir muessen gleiche Bedingungen schaffen: in der ersten
178     // Zeile formatieren wir SwParaPortions...
179     if( pOldCurr->IsParaPortion() )
180     {
181         SwParaPortion *pPara = new SwParaPortion();
182         SetParaPortion( &rInf, pPara );
183         pCurr = pPara;
184         ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: not the first" );
185     }
186     else
187         pCurr = new SwLineLayout();
188 
189     nWrdStart = FormatLine( nWrdStart );
190 
191     // Man muss immer im Hinterkopf behalten, dass es z.B.
192     // Felder gibt, die aufgetrennt werden koennen ...
193     if( pCurr->PrtWidth() && pCurr->GetLen() )
194     {
195         // Wir muessen uns darauf einstellen, dass in der Zeile
196         // FlyFrms haengen, an denen auch umgebrochen werden darf.
197         // Wir suchen also die erste HyphPortion in dem angegebenen
198         // Bereich.
199 
200         SwLinePortion *pPos = pCurr->GetPortion();
201         const xub_StrLen nPamStart = rHyphInf.nStart;
202         nWrdStart = nStart;
203         const xub_StrLen nEnd = rHyphInf.GetEnd();
204         while( pPos )
205         {
206             // Entweder wir liegen drueber oder wir laufen gerade auf eine
207             // Hyphportion die am Ende der Zeile oder vor einem Flys steht.
208             if( nWrdStart >= nEnd )
209             {
210                 nWrdStart = 0;
211                 break;
212             }
213 
214             if( nWrdStart >= nPamStart && pPos->InHyphGrp()
215                 && ( !pPos->IsSoftHyphPortion()
216                      || ((SwSoftHyphPortion*)pPos)->IsExpand() ) )
217             {
218                 nWrdStart = nWrdStart + pPos->GetLen();
219                 break;
220             }
221 
222             nWrdStart = nWrdStart + pPos->GetLen();
223             pPos = pPos->GetPortion();
224         }
225         // Wenn pPos 0 ist, wurde keine Trennstelle ermittelt.
226         if( !pPos )
227             nWrdStart = 0;
228     }
229 
230     // Das alte LineLayout wird wieder eingestellt ...
231     delete pCurr;
232     pCurr = pOldCurr;
233 
234     if( pOldCurr->IsParaPortion() )
235     {
236         SetParaPortion( &rInf, (SwParaPortion*)pOldCurr );
237         ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: even not the first" );
238     }
239 
240     if( nWrdStart )
241     {
242         // nWrdStart bezeichnet nun die Position im String, der
243         // fuer eine Trennung zur Debatte steht.
244         // Start() hangelt sich zum End()
245         rHyphInf.nWordStart = nWrdStart;
246 
247         xub_StrLen nLen = 0;
248         const xub_StrLen nEnd = nWrdStart;
249 
250         // Wir suchen vorwaerts
251         Reference< XHyphenatedWord > xHyphWord;
252 
253         Boundary aBound =
254             pBreakIt->GetBreakIter()->getWordBoundary( rInf.GetTxt(), nWrdStart,
255             pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), WordType::DICTIONARY_WORD, sal_True );
256         nWrdStart = static_cast<xub_StrLen>(aBound.startPos);
257         nLen = static_cast<xub_StrLen>(aBound.endPos - nWrdStart);
258         bRet = 0 != nLen;
259         if( bRet )
260         {
261             XubString aSelTxt( rInf.GetTxt().Copy(nWrdStart, nLen) );
262             xub_StrLen nCnt = 0;
263 
264 // these things should be handled by the dialog
265 //            for( xub_StrLen i = 0; i < nLen; ++i )
266 //            {
267 //                sal_Unicode cCh = aSelTxt.GetChar(i);
268 //                if( (CH_TXTATR_BREAKWORD == cCh || CH_TXTATR_INWORD == cCh )
269 //                     && rInf.HasHint( nWrdStart + i ) )
270 //                {
271 //                    aSelTxt.Erase( i , 1 );
272 //                    nCnt++;
273 //                    --nLen;
274 //                    if( i )
275 //                        --i;
276 //                }
277 //            }
278 
279             {
280                 MSHORT nMinTrail = 0;
281                 if( nWrdStart + nLen > nEnd )
282                     nMinTrail = nWrdStart + nLen - nEnd - 1;
283 
284                 //!! rHyphInf.SetHyphWord( ... ) mu??? hier geschehen
285                 xHyphWord = rInf.HyphWord( aSelTxt, nMinTrail );
286                 bRet = xHyphWord.is();
287                 if ( !rHyphInf.IsCheck() && sal_False == bRet )
288                     rHyphInf.SetNoLang( sal_True );
289             }
290 
291             if( bRet )
292             {
293                 rHyphInf.SetHyphWord( xHyphWord );
294                 rHyphInf.nWordStart = nWrdStart;
295                 rHyphInf.nWordLen   = nLen+nCnt;
296                 rHyphInf.SetNoLang( sal_False );
297                 rHyphInf.SetCheck( sal_True );
298             }
299 #ifdef DEBUGGY
300             if( OPTDBG( rInf ) )
301             {
302                 ASSERT( aSelTxt == aHyphWord,
303                         "!SwTxtFormatter::Hyphenate: different words, different planets" );
304                 aDbstream << "Diff: \"" << aSelTxt.GetStr() << "\" != \""
305                           << aHyphWord.GetStr() << "\"" << endl;
306                 ASSERT( bRet, "!SwTxtFormatter::Hyphenate: three of a perfect pair" );
307                 aDbstream << "Hyphenate: ";
308             }
309 #endif
310         }
311     }
312     return bRet;
313 }
314 
315 /*************************************************************************
316  *                      SwTxtPortion::CreateHyphen()
317  *************************************************************************/
318 
319 sal_Bool SwTxtPortion::CreateHyphen( SwTxtFormatInfo &rInf, SwTxtGuess &rGuess )
320 {
321     Reference< XHyphenatedWord >  xHyphWord = rGuess.HyphWord();
322 
323     ASSERT( !pPortion, "SwTxtPortion::CreateHyphen(): another portion, another planet..." )
324     ASSERT( xHyphWord.is(), "SwTxtPortion::CreateHyphen(): You are lucky! The code is robust here." )
325 
326     if( rInf.IsHyphForbud() ||
327         pPortion || // robust
328         !xHyphWord.is() || // more robust
329         // Mehrzeilige Felder duerfen nicht interaktiv getrennt werden.
330         ( rInf.IsInterHyph() && InFldGrp() ) )
331         return sal_False;
332 
333     SwHyphPortion *pHyphPor;
334     xub_StrLen nPorEnd;
335     SwTxtSizeInfo aInf( rInf );
336 
337     // first case: hyphenated word has alternative spelling
338     if ( xHyphWord->isAlternativeSpelling() )
339     {
340         SvxAlternativeSpelling aAltSpell;
341         aAltSpell = SvxGetAltSpelling( xHyphWord );
342         ASSERT( aAltSpell.bIsAltSpelling, "no alternatve spelling" );
343 
344         XubString  aAltTxt   = aAltSpell.aReplacement;
345         nPorEnd = aAltSpell.nChangedPos + rGuess.BreakStart() - rGuess.FieldDiff();
346         xub_StrLen nTmpLen = 0;
347 
348         // soft hyphen at alternative spelling position?
349         if( rInf.GetTxt().GetChar( rInf.GetSoftHyphPos() ) == CHAR_SOFTHYPHEN )
350         {
351             pHyphPor = new SwSoftHyphStrPortion( aAltTxt );
352             nTmpLen = 1;
353         }
354         else {
355             pHyphPor = new SwHyphStrPortion( aAltTxt );
356         }
357 
358         // length of pHyphPor is adjusted
359         pHyphPor->SetLen( aAltTxt.Len() + 1 );
360         (SwPosSize&)(*pHyphPor) = pHyphPor->GetTxtSize( rInf );
361         pHyphPor->SetLen( aAltSpell.nChangedLength + nTmpLen );
362     }
363     else
364     {
365         // second case: no alternative spelling
366         SwHyphPortion aHyphPor;
367         aHyphPor.SetLen( 1 );
368 
369         static const void* pLastMagicNo = 0;
370         static KSHORT aMiniCacheH = 0, aMiniCacheW = 0;
371         const void* pTmpMagic;
372         MSHORT nFntIdx;
373         rInf.GetFont()->GetMagic( pTmpMagic, nFntIdx, rInf.GetFont()->GetActual() );
374         if( !pLastMagicNo || pLastMagicNo != pTmpMagic ) {
375             pLastMagicNo = pTmpMagic;
376             (SwPosSize&)aHyphPor = aHyphPor.GetTxtSize( rInf );
377             aMiniCacheH = aHyphPor.Height(), aMiniCacheW = aHyphPor.Width();
378         } else {
379             aHyphPor.Height( aMiniCacheH ), aHyphPor.Width( aMiniCacheW );
380         }
381         aHyphPor.SetLen( 0 );
382         pHyphPor = new SwHyphPortion( aHyphPor );
383 
384         pHyphPor->SetWhichPor( POR_HYPH );
385 
386         // values required for this
387         nPorEnd = xHyphWord->getHyphenPos() + 1 + rGuess.BreakStart()
388                 - rGuess.FieldDiff();
389     }
390 
391     // portion end must be in front of us
392     // we do not put hyphens at start of line
393     if ( nPorEnd > rInf.GetIdx() ||
394          ( nPorEnd == rInf.GetIdx() && rInf.GetLineStart() != rInf.GetIdx() ) )
395     {
396         aInf.SetLen( nPorEnd - rInf.GetIdx() );
397         pHyphPor->SetAscent( GetAscent() );
398         SetLen( aInf.GetLen() );
399         CalcTxtSize( aInf );
400 
401         Insert( pHyphPor );
402 
403         short nKern = rInf.GetFont()->CheckKerning();
404         if( nKern )
405             new SwKernPortion( *this, nKern );
406 
407         return sal_True;
408     }
409 
410     // last exit for the lost
411     delete pHyphPor;
412     BreakCut( rInf, rGuess );
413     return sal_False;
414 }
415 
416 
417 /*************************************************************************
418  *              virtual SwHyphPortion::GetExpTxt()
419  *************************************************************************/
420 
421 sal_Bool SwHyphPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
422 {
423     // --> FME 2004-06-24 #i16816# tagged pdf support
424     const sal_Unicode cChar = rInf.GetVsh() &&
425                               rInf.GetVsh()->GetViewOptions()->IsPDFExport() &&
426                               SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut() ) ?
427                               0xad :
428                               '-';
429     // <--
430 
431     rTxt = cChar;
432     return sal_True;
433 }
434 
435 /*************************************************************************
436  *              virtual SwHyphPortion::HandlePortion()
437  *************************************************************************/
438 
439 void SwHyphPortion::HandlePortion( SwPortionHandler& rPH ) const
440 {
441     String aString( '-' );
442     rPH.Special( GetLen(), aString, GetWhichPor() );
443 }
444 
445 /*************************************************************************
446  *                 virtual SwHyphPortion::Format()
447  *************************************************************************/
448 
449 sal_Bool SwHyphPortion::Format( SwTxtFormatInfo &rInf )
450 {
451     const SwLinePortion *pLast = rInf.GetLast();
452     Height( pLast->Height() );
453     SetAscent( pLast->GetAscent() );
454     XubString aTxt;
455 
456     if( !GetExpTxt( rInf, aTxt ) )
457         return sal_False;
458 
459     PrtWidth( rInf.GetTxtSize( aTxt ).Width() );
460     const sal_Bool bFull = rInf.Width() <= rInf.X() + PrtWidth();
461     if( bFull && !rInf.IsUnderFlow() ) {
462         Truncate();
463         rInf.SetUnderFlow( this );
464     }
465 
466     return bFull;
467 }
468 
469 /*************************************************************************
470  *              virtual SwHyphStrPortion::GetExpTxt()
471  *************************************************************************/
472 
473 sal_Bool SwHyphStrPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const
474 {
475     rTxt = aExpand;
476     return sal_True;
477 }
478 
479 /*************************************************************************
480  *              virtual SwHyphStrPortion::HandlePortion()
481  *************************************************************************/
482 
483 void SwHyphStrPortion::HandlePortion( SwPortionHandler& rPH ) const
484 {
485     rPH.Special( GetLen(), aExpand, GetWhichPor() );
486 }
487 
488 /*************************************************************************
489  *                      class SwSoftHyphPortion
490  *************************************************************************/
491 
492 SwLinePortion *SwSoftHyphPortion::Compress() { return this; }
493 
494 SwSoftHyphPortion::SwSoftHyphPortion() :
495     bExpand(sal_False), nViewWidth(0), nHyphWidth(0)
496 {
497     SetLen(1);
498     SetWhichPor( POR_SOFTHYPH );
499 }
500 
501 KSHORT SwSoftHyphPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
502 {
503     // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
504     // Moment errechnet werden:
505     if( !Width() && rInf.OnWin() && rInf.GetOpt().IsSoftHyph() && !IsExpand() )
506     {
507         if( !nViewWidth )
508             ((SwSoftHyphPortion*)this)->nViewWidth
509                 = rInf.GetTxtSize( '-' ).Width();
510     }
511     else
512         ((SwSoftHyphPortion*)this)->nViewWidth = 0;
513     return nViewWidth;
514 }
515 
516 /*  Faelle:
517  *  1) SoftHyph steht in der Zeile, ViewOpt aus.
518  *     -> unsichtbar, Nachbarn unveraendert
519  *  2) SoftHyph steht in der Zeile, ViewOpt an.
520  *     -> sichtbar, Nachbarn veraendert
521  *  3) SoftHyph steht am Zeilenende, ViewOpt aus/an.
522  *     -> immer sichtbar, Nachbarn unveraendert
523  */
524 
525 void SwSoftHyphPortion::Paint( const SwTxtPaintInfo &rInf ) const
526 {
527     if( Width() )
528     {
529         rInf.DrawViewOpt( *this, POR_SOFTHYPH );
530         SwExpandPortion::Paint( rInf );
531     }
532 }
533 
534 /*************************************************************************
535  *                 virtual SwSoftHyphPortion::Format()
536  *************************************************************************/
537 
538 /* Die endgueltige Breite erhalten wir im FormatEOL().
539  * In der Underflow-Phase stellen wir fest, ob ueberhaupt ein
540  * alternatives Spelling vorliegt. Wenn ja ...
541  *
542  * Fall 1: "Au-to"
543  * 1) {Au}{-}{to}, {to} passt nicht mehr => Underflow
544  * 2) {-} ruft Hyphenate => keine Alternative
545  * 3) FormatEOL() und bFull = sal_True
546  *
547  * Fall 2: "Zuc-ker"
548  * 1) {Zuc}{-}{ker}, {ker} passt nicht mehr => Underflow
549  * 2) {-} ruft Hyphenate => Alternative!
550  * 3) Underflow() und bFull = sal_True
551  * 4) {Zuc} ruft Hyphenate => {Zuk}{-}{ker}
552  */
553 
554 sal_Bool SwSoftHyphPortion::Format( SwTxtFormatInfo &rInf )
555 {
556     sal_Bool bFull = sal_True;
557 
558     // special case for old german spelling
559     if( rInf.IsUnderFlow()  )
560     {
561         if( rInf.GetSoftHyphPos() )
562             return sal_True;
563 
564         const sal_Bool bHyph = rInf.ChgHyph( sal_True );
565         if( rInf.IsHyphenate() )
566         {
567             rInf.SetSoftHyphPos( rInf.GetIdx() );
568             Width(0);
569             // if the soft hyphend word has an alternative spelling
570             // when hyphenated (old german spelling), the soft hyphen
571             // portion has to trigger an underflow
572             SwTxtGuess aGuess;
573             bFull = rInf.IsInterHyph() ||
574                     !aGuess.AlternativeSpelling( rInf, rInf.GetIdx() - 1 );
575         }
576         rInf.ChgHyph( bHyph );
577 
578         if( bFull && !rInf.IsHyphForbud() )
579         {
580             rInf.SetSoftHyphPos(0);
581             FormatEOL( rInf );
582             if ( rInf.GetFly() )
583                 rInf.GetRoot()->SetMidHyph( sal_True );
584             else
585                 rInf.GetRoot()->SetEndHyph( sal_True );
586         }
587         else
588         {
589             rInf.SetSoftHyphPos( rInf.GetIdx() );
590             Truncate();
591             rInf.SetUnderFlow( this );
592         }
593         return sal_True;
594     }
595 
596     rInf.SetSoftHyphPos(0);
597     SetExpand( sal_True );
598     bFull = SwHyphPortion::Format( rInf );
599     SetExpand( sal_False );
600     if( !bFull )
601     {
602         // default-maessig besitzen wir keine Breite, aber eine Hoehe
603         nHyphWidth = Width();
604         Width(0);
605     }
606     return bFull;
607 }
608 
609 /*************************************************************************
610  *                 virtual SwSoftHyphPortion::FormatEOL()
611  *************************************************************************/
612 // Format end of Line
613 
614 void SwSoftHyphPortion::FormatEOL( SwTxtFormatInfo &rInf )
615 {
616     if( !IsExpand() )
617     {
618         SetExpand( sal_True );
619         if( rInf.GetLast() == this )
620             rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
621 
622         // 5964: alte Werte muessen wieder zurueckgesetzt werden.
623         const SwTwips nOldX  = rInf.X();
624         const xub_StrLen nOldIdx = rInf.GetIdx();
625         rInf.X( rInf.X() - PrtWidth() );
626         rInf.SetIdx( rInf.GetIdx() - GetLen() );
627         const sal_Bool bFull = SwHyphPortion::Format( rInf );
628         nHyphWidth = Width();
629 
630         // 6976: Eine truebe Sache: Wir werden erlaubterweise breiter,
631         // aber gleich wird noch ein Fly verarbeitet, der eine korrekte
632         // X-Position braucht.
633         if( bFull || !rInf.GetFly() )
634             rInf.X( nOldX );
635         else
636             rInf.X( nOldX + Width() );
637         rInf.SetIdx( nOldIdx );
638     }
639 }
640 
641 /*************************************************************************
642  *              virtual SwSoftHyphPortion::GetExpTxt()
643  *
644  * Wir expandieren:
645  * - wenn die Sonderzeichen sichtbar sein sollen
646  * - wenn wir am Ende der Zeile stehen.
647  * - wenn wir vor einem (echten/emuliertem) Zeilenumbruch stehen
648  *************************************************************************/
649 
650 sal_Bool SwSoftHyphPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
651 {
652     if( IsExpand() || ( rInf.OnWin() && rInf.GetOpt().IsSoftHyph() ) ||
653         ( GetPortion() && ( GetPortion()->InFixGrp() ||
654           GetPortion()->IsDropPortion() || GetPortion()->IsLayPortion() ||
655           GetPortion()->IsParaPortion() || GetPortion()->IsBreakPortion() ) ) )
656     {
657         return SwHyphPortion::GetExpTxt( rInf, rTxt );
658     }
659     return sal_False;
660 }
661 
662 /*************************************************************************
663  *              virtual SwSoftHyphPortion::HandlePortion()
664  *************************************************************************/
665 
666 void SwSoftHyphPortion::HandlePortion( SwPortionHandler& rPH ) const
667 {
668     const String aString( '-' );
669     const sal_uInt16 nWhich = ! Width() ?
670                           POR_SOFTHYPH_COMP :
671                           GetWhichPor();
672     rPH.Special( GetLen(), aString, nWhich );
673 }
674 
675 /*************************************************************************
676  *                      SwSoftHyphStrPortion::Paint
677  *************************************************************************/
678 
679 void SwSoftHyphStrPortion::Paint( const SwTxtPaintInfo &rInf ) const
680 {
681     // Bug oder feature?:
682     // {Zu}{k-}{ker}, {k-} wird grau statt {-}
683     rInf.DrawViewOpt( *this, POR_SOFTHYPH );
684     SwHyphStrPortion::Paint( rInf );
685 }
686 
687 SwSoftHyphStrPortion::SwSoftHyphStrPortion( const XubString &rStr )
688     : SwHyphStrPortion( rStr )
689 {
690     SetLen( 1 );
691     SetWhichPor( POR_SOFTHYPHSTR );
692 }
693 
694 
695 
696