1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: txthyph.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <hintids.hxx>
36 #include <svx/unolingu.hxx>
37 #include <com/sun/star/i18n/WordType.hpp>
38 #include <EnhancedPDFExportHelper.hxx>
39 #include <viewopt.hxx> // SwViewOptions
43 #include <SwPortionHandler.hxx>
44 #include <porhyph.hxx> //
46 #include <itrform2.hxx> //
47 #include <guess.hxx> //
48 #include <splargs.hxx> // SwInterHyphInfo
51 extern const sal_Char
*GetLangName( const MSHORT nLang
);
54 using ::rtl::OUString
;
55 using namespace ::com::sun::star
;
56 using namespace ::com::sun::star::uno
;
57 using namespace ::com::sun::star::beans
;
58 using namespace ::com::sun::star::linguistic2
;
59 using namespace ::com::sun::star::i18n
;
61 /*************************************************************************
62 * SwTxtFormatInfo::HyphWord()
63 *************************************************************************/
65 Reference
< XHyphenatedWord
> SwTxtFormatInfo::HyphWord(
66 const XubString
&rTxt
, const MSHORT nMinTrail
)
68 if( rTxt
.Len() < 4 || pFnt
->IsSymbol(pVsh
) )
70 // ASSERT( IsHyphenate(), "SwTxtFormatter::HyphWord: why?" );
71 Reference
< XHyphenator
> xHyph
= ::GetHyphenator();
72 Reference
< XHyphenatedWord
> xHyphWord
;
75 xHyphWord
= xHyph
->hyphenate( OUString(rTxt
),
76 pBreakIt
->GetLocale( pFnt
->GetLanguage() ),
77 rTxt
.Len() - nMinTrail
, GetHyphValues() );
82 /*************************************************************************
85 * Wir formatieren eine Zeile fuer die interaktive Trennung
86 *************************************************************************/
88 sal_Bool
SwTxtFrm::Hyphenate( SwInterHyphInfo
&rHyphInf
)
90 ASSERT( ! IsVertical() || ! IsSwapped(),"swapped frame at SwTxtFrm::Hyphenate" );
92 if( !pBreakIt
->GetBreakIter().is() )
94 // Wir machen den Laden erstmal dicht:
95 ASSERT( !IsLocked(), "SwTxtFrm::Hyphenate: this is locked" );
96 // 4935: Der frame::Frame muss eine gueltige SSize haben!
100 sal_Bool bRet
= sal_False
;
103 // Wir muessen die Trennung immer einschalten.
104 // Keine Angst, der SwTxtIter sichert im Hyphenate die alte Zeile.
105 SwTxtFrmLocker
aLock( this );
108 SwapWidthAndHeight();
110 SwTxtFormatInfo
aInf( this, sal_True
); // sal_True fuer interactive hyph!
111 SwTxtFormatter
aLine( this, &aInf
);
112 aLine
.CharToLine( rHyphInf
.nStart
);
113 // Wenn wir innerhalb des ersten Wortes einer Zeile stehen, so koennte
114 // dieses in der vorherigen getrennt werden, deshalb gehen wir ein Zeile
118 SwLinePortion
*pPor
= aLine
.GetCurr()->GetFirstPortion();
119 while( pPor
->GetPortion() )
120 pPor
= pPor
->GetPortion();
121 if( pPor
->GetWhichPor() == POR_SOFTHYPH
||
122 pPor
->GetWhichPor() == POR_SOFTHYPHSTR
)
126 const xub_StrLen nEnd
= rHyphInf
.GetEnd();
127 while( !bRet
&& aLine
.GetStart() < nEnd
)
130 bRet
= aLine
.Hyphenate( rHyphInf
);
136 SwapWidthAndHeight();
141 /*************************************************************************
142 * SwTxtFormatter::Hyphenate
144 * Wir formatieren eine Zeile fuer die interaktive Trennung
145 *************************************************************************/
146 // Wir koennen davon ausgehen, dass bereits formatiert wurde.
147 // Fuer die CeBIT'93 gehen wir den einfachen, sicheren Weg:
148 // Die Zeile wird einfach neu formatiert, der Hyphenator wird dann
149 // so vorbereitet, wie ihn die UI erwartet.
150 // Hier stehen natuerlich enorme Optimierungsmoeglichkeiten offen.
152 void SetParaPortion( SwTxtInfo
*pInf
, SwParaPortion
*pRoot
)
154 ASSERT( pRoot
, "SetParaPortion: no root anymore" );
158 sal_Bool
SwTxtFormatter::Hyphenate( SwInterHyphInfo
&rHyphInf
)
160 SwTxtFormatInfo
&rInf
= GetInfo();
161 sal_Bool bRet
= sal_False
;
163 // In der letzten Zeile gibt es nie etwas zu trennen.
164 // Es sei denn, es befindet sich eine FlyPortion darin,
165 // oder es ist die letzte Zeile des Masters
166 if( !GetNext() && !rInf
.GetTxtFly()->IsOn() && !pFrm
->GetFollow() )
169 xub_StrLen nWrdStart
= nStart
;
171 // Wir muessen die alte Zeile erhalten. Ein Beispiel:
172 // Das Attribut fuer Trennung wurde nicht gesetzt,
173 // in SwTxtFrm::Hyphenate wird es jedoch immer gesetzt,
174 // weil wir Trennpositionen im Hyphenator einstellen wollen.
175 SwLineLayout
*pOldCurr
= pCurr
;
179 // 5298: IsParaLine() (ex.IsFirstLine) fragt auf GetParaPortion() ab.
180 // wir muessen gleiche Bedingungen schaffen: in der ersten
181 // Zeile formatieren wir SwParaPortions...
182 if( pOldCurr
->IsParaPortion() )
184 SwParaPortion
*pPara
= new SwParaPortion();
185 SetParaPortion( &rInf
, pPara
);
187 ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: not the first" );
190 pCurr
= new SwLineLayout();
192 nWrdStart
= FormatLine( nWrdStart
);
194 // Man muss immer im Hinterkopf behalten, dass es z.B.
195 // Felder gibt, die aufgetrennt werden koennen ...
196 if( pCurr
->PrtWidth() && pCurr
->GetLen() )
198 // Wir muessen uns darauf einstellen, dass in der Zeile
199 // FlyFrms haengen, an denen auch umgebrochen werden darf.
200 // Wir suchen also die erste HyphPortion in dem angegebenen
203 SwLinePortion
*pPos
= pCurr
->GetPortion();
204 const xub_StrLen nPamStart
= rHyphInf
.nStart
;
206 const xub_StrLen nEnd
= rHyphInf
.GetEnd();
209 // Entweder wir liegen drueber oder wir laufen gerade auf eine
210 // Hyphportion die am Ende der Zeile oder vor einem Flys steht.
211 if( nWrdStart
>= nEnd
)
217 if( nWrdStart
>= nPamStart
&& pPos
->InHyphGrp()
218 && ( !pPos
->IsSoftHyphPortion()
219 || ((SwSoftHyphPortion
*)pPos
)->IsExpand() ) )
221 nWrdStart
= nWrdStart
+ pPos
->GetLen();
225 nWrdStart
= nWrdStart
+ pPos
->GetLen();
226 pPos
= pPos
->GetPortion();
228 // Wenn pPos 0 ist, wurde keine Trennstelle ermittelt.
233 // Das alte LineLayout wird wieder eingestellt ...
237 if( pOldCurr
->IsParaPortion() )
239 SetParaPortion( &rInf
, (SwParaPortion
*)pOldCurr
);
240 ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: even not the first" );
245 // nWrdStart bezeichnet nun die Position im String, der
246 // fuer eine Trennung zur Debatte steht.
247 // Start() hangelt sich zum End()
248 rHyphInf
.nWordStart
= nWrdStart
;
251 const xub_StrLen nEnd
= nWrdStart
;
253 // Wir suchen vorwaerts
254 Reference
< XHyphenatedWord
> xHyphWord
;
257 pBreakIt
->GetBreakIter()->getWordBoundary( rInf
.GetTxt(), nWrdStart
,
258 pBreakIt
->GetLocale( rInf
.GetFont()->GetLanguage() ), WordType::DICTIONARY_WORD
, sal_True
);
259 nWrdStart
= static_cast<xub_StrLen
>(aBound
.startPos
);
260 nLen
= static_cast<xub_StrLen
>(aBound
.endPos
- nWrdStart
);
264 XubString
aSelTxt( rInf
.GetTxt().Copy(nWrdStart
, nLen
) );
267 // these things should be handled by the dialog
268 // for( xub_StrLen i = 0; i < nLen; ++i )
270 // sal_Unicode cCh = aSelTxt.GetChar(i);
271 // if( (CH_TXTATR_BREAKWORD == cCh || CH_TXTATR_INWORD == cCh )
272 // && rInf.HasHint( nWrdStart + i ) )
274 // aSelTxt.Erase( i , 1 );
283 MSHORT nMinTrail
= 0;
284 if( nWrdStart
+ nLen
> nEnd
)
285 nMinTrail
= nWrdStart
+ nLen
- nEnd
- 1;
287 //!! rHyphInf.SetHyphWord( ... ) mu??? hier geschehen
288 xHyphWord
= rInf
.HyphWord( aSelTxt
, nMinTrail
);
289 bRet
= xHyphWord
.is();
290 if ( !rHyphInf
.IsCheck() && sal_False
== bRet
)
291 rHyphInf
.SetNoLang( sal_True
);
296 rHyphInf
.SetHyphWord( xHyphWord
);
297 rHyphInf
.nWordStart
= nWrdStart
;
298 rHyphInf
.nWordLen
= nLen
+nCnt
;
299 rHyphInf
.SetNoLang( sal_False
);
300 rHyphInf
.SetCheck( sal_True
);
305 ASSERT( aSelTxt
== aHyphWord
,
306 "!SwTxtFormatter::Hyphenate: different words, different planets" );
307 aDbstream
<< "Diff: \"" << aSelTxt
.GetStr() << "\" != \""
308 << aHyphWord
.GetStr() << "\"" << endl
;
309 ASSERT( bRet
, "!SwTxtFormatter::Hyphenate: three of a perfect pair" );
310 aDbstream
<< "Hyphenate: ";
318 /*************************************************************************
319 * SwTxtPortion::CreateHyphen()
320 *************************************************************************/
322 sal_Bool
SwTxtPortion::CreateHyphen( SwTxtFormatInfo
&rInf
, SwTxtGuess
&rGuess
)
324 Reference
< XHyphenatedWord
> xHyphWord
= rGuess
.HyphWord();
326 ASSERT( !pPortion
, "SwTxtPortion::CreateHyphen(): another portion, another planet..." )
327 ASSERT( xHyphWord
.is(), "SwTxtPortion::CreateHyphen(): You are lucky! The code is robust here." )
329 if( rInf
.IsHyphForbud() ||
330 pPortion
|| // robust
331 !xHyphWord
.is() || // more robust
332 // Mehrzeilige Felder duerfen nicht interaktiv getrennt werden.
333 ( rInf
.IsInterHyph() && InFldGrp() ) )
336 SwHyphPortion
*pHyphPor
;
338 SwTxtSizeInfo
aInf( rInf
);
340 // first case: hyphenated word has alternative spelling
341 if ( xHyphWord
->isAlternativeSpelling() )
343 SvxAlternativeSpelling aAltSpell
;
344 aAltSpell
= SvxGetAltSpelling( xHyphWord
);
345 ASSERT( aAltSpell
.bIsAltSpelling
, "no alternatve spelling" );
347 XubString aAltTxt
= aAltSpell
.aReplacement
;
348 nPorEnd
= aAltSpell
.nChangedPos
+ rGuess
.BreakStart() - rGuess
.FieldDiff();
349 xub_StrLen nTmpLen
= 0;
351 // soft hyphen at alternative spelling position?
352 if( rInf
.GetTxt().GetChar( rInf
.GetSoftHyphPos() ) == CHAR_SOFTHYPHEN
)
354 pHyphPor
= new SwSoftHyphStrPortion( aAltTxt
);
358 pHyphPor
= new SwHyphStrPortion( aAltTxt
);
361 // length of pHyphPor is adjusted
362 pHyphPor
->SetLen( aAltTxt
.Len() + 1 );
363 (SwPosSize
&)(*pHyphPor
) = pHyphPor
->GetTxtSize( rInf
);
364 pHyphPor
->SetLen( aAltSpell
.nChangedLength
+ nTmpLen
);
368 // second case: no alternative spelling
369 SwHyphPortion aHyphPor
;
370 aHyphPor
.SetLen( 1 );
372 static const void* pLastMagicNo
= 0;
373 static KSHORT aMiniCacheH
= 0, aMiniCacheW
= 0;
374 const void* pTmpMagic
;
376 rInf
.GetFont()->GetMagic( pTmpMagic
, nFntIdx
, rInf
.GetFont()->GetActual() );
377 if( !pLastMagicNo
|| pLastMagicNo
!= pTmpMagic
) {
378 pLastMagicNo
= pTmpMagic
;
379 (SwPosSize
&)aHyphPor
= aHyphPor
.GetTxtSize( rInf
);
380 aMiniCacheH
= aHyphPor
.Height(), aMiniCacheW
= aHyphPor
.Width();
382 aHyphPor
.Height( aMiniCacheH
), aHyphPor
.Width( aMiniCacheW
);
384 aHyphPor
.SetLen( 0 );
385 pHyphPor
= new SwHyphPortion( aHyphPor
);
387 pHyphPor
->SetWhichPor( POR_HYPH
);
389 // values required for this
390 nPorEnd
= xHyphWord
->getHyphenPos() + 1 + rGuess
.BreakStart()
391 - rGuess
.FieldDiff();
394 // portion end must be in front of us
395 // we do not put hyphens at start of line
396 if ( nPorEnd
> rInf
.GetIdx() ||
397 ( nPorEnd
== rInf
.GetIdx() && rInf
.GetLineStart() != rInf
.GetIdx() ) )
399 aInf
.SetLen( nPorEnd
- rInf
.GetIdx() );
400 pHyphPor
->SetAscent( GetAscent() );
401 SetLen( aInf
.GetLen() );
406 short nKern
= rInf
.GetFont()->CheckKerning();
408 new SwKernPortion( *this, nKern
);
413 // last exit for the lost
415 BreakCut( rInf
, rGuess
);
420 /*************************************************************************
421 * virtual SwHyphPortion::GetExpTxt()
422 *************************************************************************/
424 sal_Bool
SwHyphPortion::GetExpTxt( const SwTxtSizeInfo
&rInf
, XubString
&rTxt
) const
426 // --> FME 2004-06-24 #i16816# tagged pdf support
427 const sal_Unicode cChar
= rInf
.GetVsh() &&
428 rInf
.GetVsh()->GetViewOptions()->IsPDFExport() &&
429 SwTaggedPDFHelper::IsExportTaggedPDF( *rInf
.GetOut() ) ?
438 /*************************************************************************
439 * virtual SwHyphPortion::HandlePortion()
440 *************************************************************************/
442 void SwHyphPortion::HandlePortion( SwPortionHandler
& rPH
) const
444 String
aString( '-' );
445 rPH
.Special( GetLen(), aString
, GetWhichPor() );
448 /*************************************************************************
449 * virtual SwHyphPortion::Format()
450 *************************************************************************/
452 sal_Bool
SwHyphPortion::Format( SwTxtFormatInfo
&rInf
)
454 const SwLinePortion
*pLast
= rInf
.GetLast();
455 Height( pLast
->Height() );
456 SetAscent( pLast
->GetAscent() );
459 if( !GetExpTxt( rInf
, aTxt
) )
462 PrtWidth( rInf
.GetTxtSize( aTxt
).Width() );
463 const sal_Bool bFull
= rInf
.Width() <= rInf
.X() + PrtWidth();
464 if( bFull
&& !rInf
.IsUnderFlow() ) {
466 rInf
.SetUnderFlow( this );
472 /*************************************************************************
473 * virtual SwHyphStrPortion::GetExpTxt()
474 *************************************************************************/
476 sal_Bool
SwHyphStrPortion::GetExpTxt( const SwTxtSizeInfo
&, XubString
&rTxt
) const
482 /*************************************************************************
483 * virtual SwHyphStrPortion::HandlePortion()
484 *************************************************************************/
486 void SwHyphStrPortion::HandlePortion( SwPortionHandler
& rPH
) const
488 rPH
.Special( GetLen(), aExpand
, GetWhichPor() );
491 /*************************************************************************
492 * class SwSoftHyphPortion
493 *************************************************************************/
495 SwLinePortion
*SwSoftHyphPortion::Compress() { return this; }
497 SwSoftHyphPortion::SwSoftHyphPortion() :
498 bExpand(sal_False
), nViewWidth(0), nHyphWidth(0)
501 SetWhichPor( POR_SOFTHYPH
);
504 KSHORT
SwSoftHyphPortion::GetViewWidth( const SwTxtSizeInfo
&rInf
) const
506 // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
507 // Moment errechnet werden:
508 if( !Width() && rInf
.OnWin() && rInf
.GetOpt().IsSoftHyph() && !IsExpand() )
511 ((SwSoftHyphPortion
*)this)->nViewWidth
512 = rInf
.GetTxtSize( '-' ).Width();
515 ((SwSoftHyphPortion
*)this)->nViewWidth
= 0;
520 * 1) SoftHyph steht in der Zeile, ViewOpt aus.
521 * -> unsichtbar, Nachbarn unveraendert
522 * 2) SoftHyph steht in der Zeile, ViewOpt an.
523 * -> sichtbar, Nachbarn veraendert
524 * 3) SoftHyph steht am Zeilenende, ViewOpt aus/an.
525 * -> immer sichtbar, Nachbarn unveraendert
528 void SwSoftHyphPortion::Paint( const SwTxtPaintInfo
&rInf
) const
532 rInf
.DrawViewOpt( *this, POR_SOFTHYPH
);
533 SwExpandPortion::Paint( rInf
);
537 /*************************************************************************
538 * virtual SwSoftHyphPortion::Format()
539 *************************************************************************/
541 /* Die endgueltige Breite erhalten wir im FormatEOL().
542 * In der Underflow-Phase stellen wir fest, ob ueberhaupt ein
543 * alternatives Spelling vorliegt. Wenn ja ...
546 * 1) {Au}{-}{to}, {to} passt nicht mehr => Underflow
547 * 2) {-} ruft Hyphenate => keine Alternative
548 * 3) FormatEOL() und bFull = sal_True
551 * 1) {Zuc}{-}{ker}, {ker} passt nicht mehr => Underflow
552 * 2) {-} ruft Hyphenate => Alternative!
553 * 3) Underflow() und bFull = sal_True
554 * 4) {Zuc} ruft Hyphenate => {Zuk}{-}{ker}
557 sal_Bool
SwSoftHyphPortion::Format( SwTxtFormatInfo
&rInf
)
559 sal_Bool bFull
= sal_True
;
561 // special case for old german spelling
562 if( rInf
.IsUnderFlow() )
564 if( rInf
.GetSoftHyphPos() )
567 const sal_Bool bHyph
= rInf
.ChgHyph( sal_True
);
568 if( rInf
.IsHyphenate() )
570 rInf
.SetSoftHyphPos( rInf
.GetIdx() );
572 // if the soft hyphend word has an alternative spelling
573 // when hyphenated (old german spelling), the soft hyphen
574 // portion has to trigger an underflow
576 bFull
= rInf
.IsInterHyph() ||
577 !aGuess
.AlternativeSpelling( rInf
, rInf
.GetIdx() - 1 );
579 rInf
.ChgHyph( bHyph
);
581 if( bFull
&& !rInf
.IsHyphForbud() )
583 rInf
.SetSoftHyphPos(0);
586 rInf
.GetRoot()->SetMidHyph( sal_True
);
588 rInf
.GetRoot()->SetEndHyph( sal_True
);
592 rInf
.SetSoftHyphPos( rInf
.GetIdx() );
594 rInf
.SetUnderFlow( this );
599 rInf
.SetSoftHyphPos(0);
600 SetExpand( sal_True
);
601 bFull
= SwHyphPortion::Format( rInf
);
602 SetExpand( sal_False
);
605 // default-maessig besitzen wir keine Breite, aber eine Hoehe
606 nHyphWidth
= Width();
612 /*************************************************************************
613 * virtual SwSoftHyphPortion::FormatEOL()
614 *************************************************************************/
615 // Format end of Line
617 void SwSoftHyphPortion::FormatEOL( SwTxtFormatInfo
&rInf
)
621 SetExpand( sal_True
);
622 if( rInf
.GetLast() == this )
623 rInf
.SetLast( FindPrevPortion( rInf
.GetRoot() ) );
625 // 5964: alte Werte muessen wieder zurueckgesetzt werden.
626 const SwTwips nOldX
= rInf
.X();
627 const xub_StrLen nOldIdx
= rInf
.GetIdx();
628 rInf
.X( rInf
.X() - PrtWidth() );
629 rInf
.SetIdx( rInf
.GetIdx() - GetLen() );
630 const sal_Bool bFull
= SwHyphPortion::Format( rInf
);
631 nHyphWidth
= Width();
633 // 6976: Eine truebe Sache: Wir werden erlaubterweise breiter,
634 // aber gleich wird noch ein Fly verarbeitet, der eine korrekte
635 // X-Position braucht.
636 if( bFull
|| !rInf
.GetFly() )
639 rInf
.X( nOldX
+ Width() );
640 rInf
.SetIdx( nOldIdx
);
644 /*************************************************************************
645 * virtual SwSoftHyphPortion::GetExpTxt()
648 * - wenn die Sonderzeichen sichtbar sein sollen
649 * - wenn wir am Ende der Zeile stehen.
650 * - wenn wir vor einem (echten/emuliertem) Zeilenumbruch stehen
651 *************************************************************************/
653 sal_Bool
SwSoftHyphPortion::GetExpTxt( const SwTxtSizeInfo
&rInf
, XubString
&rTxt
) const
655 if( IsExpand() || ( rInf
.OnWin() && rInf
.GetOpt().IsSoftHyph() ) ||
656 ( GetPortion() && ( GetPortion()->InFixGrp() ||
657 GetPortion()->IsDropPortion() || GetPortion()->IsLayPortion() ||
658 GetPortion()->IsParaPortion() || GetPortion()->IsBreakPortion() ) ) )
660 return SwHyphPortion::GetExpTxt( rInf
, rTxt
);
665 /*************************************************************************
666 * virtual SwSoftHyphPortion::HandlePortion()
667 *************************************************************************/
669 void SwSoftHyphPortion::HandlePortion( SwPortionHandler
& rPH
) const
671 const String
aString( '-' );
672 const USHORT nWhich
= ! Width() ?
675 rPH
.Special( GetLen(), aString
, nWhich
);
678 /*************************************************************************
679 * SwSoftHyphStrPortion::Paint
680 *************************************************************************/
682 void SwSoftHyphStrPortion::Paint( const SwTxtPaintInfo
&rInf
) const
684 // Bug oder feature?:
685 // {Zu}{k-}{ker}, {k-} wird grau statt {-}
686 rInf
.DrawViewOpt( *this, POR_SOFTHYPH
);
687 SwHyphStrPortion::Paint( rInf
);
690 SwSoftHyphStrPortion::SwSoftHyphStrPortion( const XubString
&rStr
)
691 : SwHyphStrPortion( rStr
)
694 SetWhichPor( POR_SOFTHYPHSTR
);