1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <editeng/unolingu.hxx>
21 #include <breakit.hxx>
22 #include <IDocumentSettingAccess.hxx>
25 #include <pagefrm.hxx>
26 #include <tgrditem.hxx>
27 #include <com/sun/star/i18n/BreakType.hpp>
28 #include <com/sun/star/i18n/WordType.hpp>
29 #include <com/sun/star/i18n/XBreakIterator.hpp>
30 #include <unotools/charclass.hxx>
31 #include <svl/urihelper.hxx>
35 #include <unotools/linguprops.hxx>
37 using namespace ::com::sun::star
;
38 using namespace ::com::sun::star::uno
;
39 using namespace ::com::sun::star::i18n
;
40 using namespace ::com::sun::star::beans
;
41 using namespace ::com::sun::star::linguistic2
;
45 bool IsBlank(sal_Unicode ch
) { return ch
== CH_BLANK
|| ch
== CH_FULL_BLANK
|| ch
== CH_NB_SPACE
|| ch
== CH_SIX_PER_EM
; }
49 // provides information for line break calculation
50 // returns true if no line break has to be performed
51 // otherwise possible break or hyphenation position is determined
52 bool SwTextGuess::Guess( const SwTextPortion
& rPor
, SwTextFormatInfo
&rInf
,
53 const sal_uInt16 nPorHeight
)
55 m_nCutPos
= rInf
.GetIdx();
57 // Empty strings are always 0
58 if( !rInf
.GetLen() || rInf
.GetText().isEmpty() )
61 OSL_ENSURE( rInf
.GetIdx() < TextFrameIndex(rInf
.GetText().getLength()),
62 "+SwTextGuess::Guess: invalid SwTextFormatInfo" );
64 OSL_ENSURE( nPorHeight
, "+SwTextGuess::Guess: no height" );
66 sal_uInt16 nMaxSizeDiff
;
68 const SwScriptInfo
& rSI
= rInf
.GetParaPortion()->GetScriptInfo();
70 sal_uInt16 nMaxComp
= ( SwFontScript::CJK
== rInf
.GetFont()->GetActual() ) &&
73 ! rPor
.InFieldGrp() &&
74 ! rPor
.IsDropPortion() ?
78 SwTwips nLineWidth
= rInf
.GetLineWidth();
79 TextFrameIndex nMaxLen
= TextFrameIndex(rInf
.GetText().getLength()) - rInf
.GetIdx();
81 const SvxAdjust
& rAdjust
= rInf
.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
83 // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
85 if ( !rInf
.GetTextFrame()->IsRightToLeft() )
87 if (rInf
.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
88 DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS
))
90 if ( rAdjust
== SvxAdjust::Right
|| rAdjust
== SvxAdjust::Center
)
92 TextFrameIndex
nSpaceCnt(0);
93 for (sal_Int32 i
= rInf
.GetText().getLength() - 1;
94 sal_Int32(rInf
.GetIdx()) <= i
; --i
)
96 sal_Unicode cChar
= rInf
.GetText()[i
];
97 if ( cChar
!= CH_BLANK
&& cChar
!= CH_FULL_BLANK
&& cChar
!= CH_SIX_PER_EM
)
101 TextFrameIndex nCharsCnt
= nMaxLen
- nSpaceCnt
;
102 if ( nSpaceCnt
&& nCharsCnt
< rPor
.GetLen() )
105 rInf
.GetTextSize( &rSI
, rInf
.GetIdx() + nCharsCnt
, nSpaceCnt
,
106 nMaxComp
, m_nExtraBlankWidth
, nMaxSizeDiff
);
115 if ( rInf
.GetLen() < nMaxLen
)
116 nMaxLen
= rInf
.GetLen();
121 sal_uInt16 nItalic
= 0;
122 if( ITALIC_NONE
!= rInf
.GetFont()->GetItalic() && !rInf
.NotEOL() )
124 bool bAddItalic
= true;
126 // do not add extra italic value if we have an active character grid
127 if ( rInf
.SnapToGrid() )
129 SwTextGridItem
const*const pGrid(
130 GetGridItem(rInf
.GetTextFrame()->FindPageFrame()));
131 bAddItalic
= !pGrid
|| GRID_LINES_CHARS
!= pGrid
->GetGridType();
134 // do not add extra italic value for an isolated blank:
135 if (TextFrameIndex(1) == rInf
.GetLen() &&
136 CH_BLANK
== rInf
.GetText()[sal_Int32(rInf
.GetIdx())])
141 if (rInf
.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
142 DocumentSettingId::TAB_OVER_MARGIN
))
144 // Content is allowed over the margin: in this case over-margin content caused by italic
149 nItalic
= bAddItalic
? nPorHeight
/ 12 : 0;
151 nLineWidth
-= nItalic
;
153 // #i46524# LineBreak bug with italics
154 if ( nLineWidth
< 0 ) nLineWidth
= 0;
157 const sal_Int32 nLeftRightBorderSpace
=
158 (!rPor
.GetJoinBorderWithNext() ? rInf
.GetFont()->GetRightBorderSpace() : 0) +
159 (!rPor
.GetJoinBorderWithPrev() ? rInf
.GetFont()->GetLeftBorderSpace() : 0);
161 nLineWidth
-= nLeftRightBorderSpace
;
163 const bool bUnbreakableNumberings
= rInf
.GetTextFrame()->GetDoc()
164 .getIDocumentSettingAccess().get(DocumentSettingId::UNBREAKABLE_NUMBERINGS
);
166 // first check if everything fits to line
167 if ( ( nLineWidth
* 2 > SwTwips(sal_Int32(nMaxLen
)) * nPorHeight
) ||
168 ( bUnbreakableNumberings
&& rPor
.IsNumberPortion() ) )
170 // call GetTextSize with maximum compression (for kanas)
171 rInf
.GetTextSize( &rSI
, rInf
.GetIdx(), nMaxLen
,
172 nMaxComp
, m_nBreakWidth
, nMaxSizeDiff
);
174 if ( ( m_nBreakWidth
<= nLineWidth
) || ( bUnbreakableNumberings
&& rPor
.IsNumberPortion() ) )
176 // portion fits to line
177 m_nCutPos
= rInf
.GetIdx() + nMaxLen
;
179 (m_nCutPos
>= TextFrameIndex(rInf
.GetText().getLength()) ||
180 // #i48035# Needed for CalcFitToContent
181 // if first line ends with a manual line break
182 rInf
.GetText()[sal_Int32(m_nCutPos
)] == CH_BREAK
))
183 m_nBreakWidth
= m_nBreakWidth
+ nItalic
;
185 // save maximum width for later use
187 rInf
.SetMaxWidthDiff( &rPor
, nMaxSizeDiff
);
189 m_nBreakWidth
+= nLeftRightBorderSpace
;
195 bool bHyph
= rInf
.IsHyphenate() && !rInf
.IsHyphForbud();
196 TextFrameIndex
nHyphPos(0);
198 // nCutPos is the first character not fitting to the current line
199 // nHyphPos is the first character not fitting to the current line,
200 // considering an additional "-" for hyphenation
203 // nHyphZone is the first character not fitting in the hyphenation zone,
204 // or 0, if the whole line in the hyphenation zone,
205 // or -1, if no hyphenation zone defined (i.e. it is 0)
206 sal_Int32 nHyphZone
= -1;
207 const css::beans::PropertyValues
& rHyphValues
= rInf
.GetHyphValues();
208 assert( rHyphValues
.getLength() > 5 && rHyphValues
[5].Name
== UPN_HYPH_ZONE
);
209 // hyphenation zone (distance from the line end in twips)
210 sal_uInt16 nTextHyphenZone
;
211 if ( rHyphValues
[5].Value
>>= nTextHyphenZone
)
212 nHyphZone
= nTextHyphenZone
>= nLineWidth
214 : sal_Int32(rInf
.GetTextBreak( nLineWidth
- nTextHyphenZone
,
215 nMaxLen
, nMaxComp
, rInf
.GetCachedVclData().get() ));
217 m_nCutPos
= rInf
.GetTextBreak( nLineWidth
, nMaxLen
, nMaxComp
, nHyphPos
, rInf
.GetCachedVclData().get() );
219 // don't try to hyphenate in the hyphenation zone
220 if ( nHyphZone
!= -1 && TextFrameIndex(COMPLETE_STRING
) != m_nCutPos
)
222 sal_Int32 nZonePos
= sal_Int32(m_nCutPos
);
223 // disable hyphenation, if there is a space within the hyphenation zone
224 // Note: for better interoperability, not fitting space character at
225 // rInf.GetIdx()[nHyphZone] always disables the hyphenation, don't need to calculate
226 // with its fitting part. Moreover, do not check double or more spaces there, they
227 // are accepted outside of the hyphenation zone, too.
228 for (; sal_Int32(rInf
.GetIdx()) <= nZonePos
&& nHyphZone
<= nZonePos
; --nZonePos
)
230 sal_Unicode cChar
= rInf
.GetText()[nZonePos
];
231 if ( cChar
== CH_BLANK
|| cChar
== CH_FULL_BLANK
|| cChar
== CH_SIX_PER_EM
)
238 if (!rInf
.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
239 DocumentSettingId::HYPHENATE_URLS
))
241 // check URL from preceding space - similar to what AutoFormat does
242 const CharClass
& rCC
= GetAppCharClass();
243 sal_Int32
begin(m_nCutPos
== TextFrameIndex(COMPLETE_STRING
) ? rInf
.GetText().getLength() : sal_Int32(m_nCutPos
));
244 sal_Int32
end(begin
);
245 for (; 0 < begin
; --begin
)
247 sal_Unicode cChar
= rInf
.GetText()[begin
- 1];
248 if (cChar
== CH_BLANK
|| cChar
== CH_FULL_BLANK
|| cChar
== CH_SIX_PER_EM
)
253 for (; end
< rInf
.GetText().getLength(); ++end
)
255 sal_Unicode cChar
= rInf
.GetText()[end
];
256 if (cChar
== CH_BLANK
|| cChar
== CH_FULL_BLANK
|| cChar
== CH_SIX_PER_EM
)
261 if (!URIHelper::FindFirstURLInText(rInf
.GetText(), begin
, end
, rCC
).isEmpty())
267 // search start of the last word, if needed
270 // nLastWord is the space character before the last word
271 sal_Int32 nLastWord
= rInf
.GetText().getLength() - 1;
272 bool bHyphenationNoLastWord
= false;
273 assert( rHyphValues
.getLength() > 3 && rHyphValues
[3].Name
== UPN_HYPH_NO_LAST_WORD
);
274 if ( rHyphValues
[3].Value
>>= bHyphenationNoLastWord
)
276 // skip spaces after the last word
277 bool bCutBlank
= false;
278 for (; sal_Int32(rInf
.GetIdx()) <= nLastWord
; --nLastWord
)
280 sal_Unicode cChar
= rInf
.GetText()[nLastWord
];
281 if ( cChar
!= CH_BLANK
&& cChar
!= CH_FULL_BLANK
&& cChar
!= CH_SIX_PER_EM
)
283 else if ( bCutBlank
)
288 // don't hyphenate the last word of the paragraph
289 if ( bHyphenationNoLastWord
&& sal_Int32(m_nCutPos
) > nLastWord
&&
290 TextFrameIndex(COMPLETE_STRING
) != m_nCutPos
&&
291 // if the last word is multiple line long, e.g. an URL,
292 // apply this only if the space before the word is there
293 // in the actual line, i.e. start the long word in a new
294 // line, but still allows to break its last parts
295 sal_Int32(rInf
.GetIdx()) < nLastWord
)
297 m_nCutPos
= TextFrameIndex(nLastWord
);
301 if ( !nHyphPos
&& rInf
.GetIdx() )
302 nHyphPos
= rInf
.GetIdx() - TextFrameIndex(1);
306 m_nCutPos
= rInf
.GetTextBreak( nLineWidth
, nMaxLen
, nMaxComp
, rInf
.GetCachedVclData().get() );
308 #if OSL_DEBUG_LEVEL > 1
309 if ( TextFrameIndex(COMPLETE_STRING
) != m_nCutPos
)
312 rInf
.GetTextSize( &rSI
, rInf
.GetIdx(), m_nCutPos
- rInf
.GetIdx(),
313 nMaxComp
, nMinSize
, nMaxSizeDiff
);
314 OSL_ENSURE( nMinSize
<= nLineWidth
, "What a Guess!!!" );
319 if( m_nCutPos
> rInf
.GetIdx() + nMaxLen
)
321 // second check if everything fits to line
322 m_nCutPos
= m_nBreakPos
= rInf
.GetIdx() + nMaxLen
- TextFrameIndex(1);
323 rInf
.GetTextSize( &rSI
, rInf
.GetIdx(), nMaxLen
, nMaxComp
,
324 m_nBreakWidth
, nMaxSizeDiff
);
326 // The following comparison should always give true, otherwise
327 // there likely has been a pixel rounding error in GetTextBreak
328 if ( m_nBreakWidth
<= nLineWidth
)
330 if (nItalic
&& (m_nBreakPos
+ TextFrameIndex(1)) >= TextFrameIndex(rInf
.GetText().getLength()))
331 m_nBreakWidth
= m_nBreakWidth
+ nItalic
;
333 // save maximum width for later use
335 rInf
.SetMaxWidthDiff( &rPor
, nMaxSizeDiff
);
337 m_nBreakWidth
+= nLeftRightBorderSpace
;
343 // we have to trigger an underflow for a footnote portion
344 // which does not fit to the current line
345 if ( rPor
.IsFootnotePortion() )
347 m_nBreakPos
= rInf
.GetIdx();
348 m_nCutPos
= TextFrameIndex(-1);
352 TextFrameIndex
nPorLen(0);
353 // do not call the break iterator nCutPos is a blank
354 sal_Unicode cCutChar
= m_nCutPos
< TextFrameIndex(rInf
.GetText().getLength())
355 ? rInf
.GetText()[sal_Int32(m_nCutPos
)]
357 if (IsBlank(cCutChar
))
359 m_nBreakPos
= m_nCutPos
;
360 TextFrameIndex nX
= m_nBreakPos
;
362 if ( rAdjust
== SvxAdjust::Left
)
364 // we step back until a non blank character has been found
365 // or there is only one more character left
366 while (nX
&& TextFrameIndex(rInf
.GetText().getLength()) < m_nBreakPos
&&
367 IsBlank(rInf
.GetChar(--nX
)))
372 while (nX
&& m_nBreakPos
> rInf
.GetLineStart() + TextFrameIndex(1) &&
373 IsBlank(rInf
.GetChar(--nX
)))
377 if( m_nBreakPos
> rInf
.GetIdx() )
378 nPorLen
= m_nBreakPos
- rInf
.GetIdx();
379 while (++m_nCutPos
< TextFrameIndex(rInf
.GetText().getLength()) &&
380 IsBlank(rInf
.GetChar(m_nCutPos
)))
383 m_nBreakStart
= m_nCutPos
;
387 // New: We should have a look into the last portion, if it was a
388 // field portion. For this, we expand the text of the field portion
389 // into our string. If the line break position is inside of before
390 // the field portion, we trigger an underflow.
392 TextFrameIndex nOldIdx
= rInf
.GetIdx();
393 sal_Unicode cFieldChr
= 0;
395 #if OSL_DEBUG_LEVEL > 0
396 OUString aDebugString
;
399 // be careful: a field portion can be both: 0x01 (common field)
400 // or 0x02 (the follow of a footnode)
401 if ( rInf
.GetLast() && rInf
.GetLast()->InFieldGrp() &&
402 ! rInf
.GetLast()->IsFootnotePortion() &&
403 rInf
.GetIdx() > rInf
.GetLineStart() &&
404 CH_TXTATR_BREAKWORD
==
405 (cFieldChr
= rInf
.GetText()[sal_Int32(rInf
.GetIdx()) - 1]))
407 SwFieldPortion
* pField
= static_cast<SwFieldPortion
*>(rInf
.GetLast());
409 pField
->GetExpText( rInf
, aText
);
411 if ( !aText
.isEmpty() )
413 m_nFieldDiff
= TextFrameIndex(aText
.getLength() - 1);
414 m_nCutPos
= m_nCutPos
+ m_nFieldDiff
;
415 nHyphPos
= nHyphPos
+ m_nFieldDiff
;
417 #if OSL_DEBUG_LEVEL > 0
418 aDebugString
= rInf
.GetText();
421 // this is pretty nutso... reverted at the end...
422 OUString
& rOldText
= const_cast<OUString
&> (rInf
.GetText());
423 rOldText
= rOldText
.replaceAt(sal_Int32(rInf
.GetIdx()) - 1, 1, aText
);
424 rInf
.SetIdx( rInf
.GetIdx() + m_nFieldDiff
);
430 LineBreakHyphenationOptions aHyphOpt
;
431 Reference
< XHyphenator
> xHyph
;
434 xHyph
= ::GetHyphenator();
435 aHyphOpt
= LineBreakHyphenationOptions( xHyph
,
436 rInf
.GetHyphValues(), sal_Int32(nHyphPos
));
439 // Get Language for break iterator.
440 // We have to switch the current language if we have a script
441 // change at nCutPos. Otherwise LATIN punctuation would never
442 // be allowed to be hanging punctuation.
443 // NEVER call GetLang if the string has been modified!!!
444 LanguageType aLang
= rInf
.GetFont()->GetLanguage();
446 // If we are inside a field portion, we use a temporary string which
447 // differs from the string at the textnode. Therefore we are not allowed
448 // to call the GetLang function.
449 if ( m_nCutPos
&& ! rPor
.InFieldGrp() )
451 const CharClass
& rCC
= GetAppCharClass();
453 // step back until a non-punctuation character is reached
454 TextFrameIndex nLangIndex
= m_nCutPos
;
456 // If a field has been expanded right in front of us we do not
457 // step further than the beginning of the expanded field
458 // (which is the position of the field placeholder in our
460 const TextFrameIndex nDoNotStepOver
= CH_TXTATR_BREAKWORD
== cFieldChr
461 ? rInf
.GetIdx() - m_nFieldDiff
- TextFrameIndex(1)
464 if ( nLangIndex
> nDoNotStepOver
&&
465 TextFrameIndex(rInf
.GetText().getLength()) == nLangIndex
)
468 while ( nLangIndex
> nDoNotStepOver
&&
469 !rCC
.isLetterNumeric(rInf
.GetText(), sal_Int32(nLangIndex
)))
472 // last "real" character is not inside our current portion
473 // we have to check the script type of the last "real" character
474 if ( nLangIndex
< rInf
.GetIdx() )
476 sal_uInt16 nScript
= g_pBreakIt
->GetRealScriptOfText( rInf
.GetText(),
477 sal_Int32(nLangIndex
));
478 OSL_ENSURE( nScript
, "Script is not between 1 and 4" );
480 // compare current script with script from last "real" character
481 if ( SwFontScript(nScript
- 1) != rInf
.GetFont()->GetActual() )
483 aLang
= rInf
.GetTextFrame()->GetLangOfChar(
484 CH_TXTATR_BREAKWORD
== cFieldChr
492 const ForbiddenCharacters
aForbidden(
493 *rInf
.GetTextFrame()->GetDoc().getIDocumentSettingAccess().getForbiddenCharacters(aLang
, true));
495 const bool bAllowHanging
= rInf
.IsHanging() && ! rInf
.IsMulti() &&
496 ! rInf
.GetTextFrame()->IsInTab() &&
499 LineBreakUserOptions
aUserOpt(
500 aForbidden
.beginLine
, aForbidden
.endLine
,
501 rInf
.HasForbiddenChars(), bAllowHanging
, false );
503 // !!! We must have a local copy of the locale, because inside
504 // getLineBreak the LinguEventListener can trigger a new formatting,
505 // which can corrupt the locale pointer inside pBreakIt.
506 const lang::Locale aLocale
= g_pBreakIt
->GetLocale( aLang
);
508 // determines first possible line break from nCutPos to
509 // start index of current line
510 LineBreakResults aResult
= g_pBreakIt
->GetBreakIter()->getLineBreak(
511 rInf
.GetText(), sal_Int32(m_nCutPos
), aLocale
,
512 sal_Int32(rInf
.GetLineStart()), aHyphOpt
, aUserOpt
);
514 m_nBreakPos
= TextFrameIndex(aResult
.breakIndex
);
516 // if we are formatting multi portions we want to allow line breaks
517 // at the border between single line and multi line portion
518 // we have to be careful with footnote portions, they always come in
520 if ( m_nBreakPos
< rInf
.GetLineStart() && rInf
.IsFirstMulti() &&
521 ! rInf
.IsFootnoteInside() )
522 m_nBreakPos
= rInf
.GetLineStart();
524 m_nBreakStart
= m_nBreakPos
;
526 bHyph
= BreakType::HYPHENATION
== aResult
.breakType
;
528 if (bHyph
&& m_nBreakPos
!= TextFrameIndex(COMPLETE_STRING
))
530 // found hyphenation position within line
531 // nBreakPos is set to the hyphenation position
532 m_xHyphWord
= aResult
.rHyphenatedWord
;
533 m_nBreakPos
+= TextFrameIndex(m_xHyphWord
->getHyphenationPos() + 1);
535 // if not in interactive mode, we have to break behind a soft hyphen
536 if ( ! rInf
.IsInterHyph() && rInf
.GetIdx() )
538 sal_Int32
const nSoftHyphPos
=
539 m_xHyphWord
->getWord().indexOf( CHAR_SOFTHYPHEN
);
541 if ( nSoftHyphPos
>= 0 &&
542 m_nBreakStart
+ TextFrameIndex(nSoftHyphPos
) <= m_nBreakPos
&&
543 m_nBreakPos
> rInf
.GetLineStart() )
544 m_nBreakPos
= rInf
.GetIdx() - TextFrameIndex(1);
547 if( m_nBreakPos
>= rInf
.GetIdx() )
549 nPorLen
= m_nBreakPos
- rInf
.GetIdx();
550 if ('-' == rInf
.GetText()[ sal_Int32(m_nBreakPos
) - 1 ])
551 m_xHyphWord
= nullptr;
554 else if ( !bHyph
&& m_nBreakPos
>= rInf
.GetLineStart() )
556 OSL_ENSURE(sal_Int32(m_nBreakPos
) != COMPLETE_STRING
, "we should have found a break pos");
558 // found break position within line
559 m_xHyphWord
= nullptr;
561 // check, if break position is soft hyphen and an underflow
562 // has to be triggered
563 if( m_nBreakPos
> rInf
.GetLineStart() && rInf
.GetIdx() &&
564 CHAR_SOFTHYPHEN
== rInf
.GetText()[ sal_Int32(m_nBreakPos
) - 1 ])
566 m_nBreakPos
= rInf
.GetIdx() - TextFrameIndex(1);
569 if( rAdjust
!= SvxAdjust::Left
)
571 // Delete any blanks at the end of a line, but be careful:
572 // If a field has been expanded, we do not want to delete any
573 // blanks inside the field portion. This would cause an unwanted
575 TextFrameIndex nX
= m_nBreakPos
;
576 while( nX
> rInf
.GetLineStart() &&
577 ( CH_TXTATR_BREAKWORD
!= cFieldChr
|| nX
> rInf
.GetIdx() ) &&
578 ( CH_BLANK
== rInf
.GetChar( --nX
) ||
579 CH_SIX_PER_EM
== rInf
.GetChar( nX
) ||
580 CH_FULL_BLANK
== rInf
.GetChar( nX
) ) )
583 if( m_nBreakPos
> rInf
.GetIdx() )
584 nPorLen
= m_nBreakPos
- rInf
.GetIdx();
588 // no line break found, setting nBreakPos to COMPLETE_STRING
589 // causes a break cut
590 m_nBreakPos
= TextFrameIndex(COMPLETE_STRING
);
591 OSL_ENSURE( m_nCutPos
>= rInf
.GetIdx(), "Deep cut" );
592 nPorLen
= m_nCutPos
- rInf
.GetIdx();
595 if (m_nBreakPos
> m_nCutPos
&& m_nBreakPos
!= TextFrameIndex(COMPLETE_STRING
))
597 const TextFrameIndex nHangingLen
= m_nBreakPos
- m_nCutPos
;
598 SwPosSize aTmpSize
= rInf
.GetTextSize( &rSI
, m_nCutPos
, nHangingLen
);
599 aTmpSize
.Width(aTmpSize
.Width() + nLeftRightBorderSpace
);
600 OSL_ENSURE( !m_pHanging
, "A hanging portion is hanging around" );
601 m_pHanging
.reset( new SwHangingPortion( std::move(aTmpSize
) ) );
602 m_pHanging
->SetLen( nHangingLen
);
603 nPorLen
= m_nCutPos
- rInf
.GetIdx();
606 // If we expanded a field, we must repair the original string.
607 // In case we do not trigger an underflow, we correct the nBreakPos
608 // value, but we cannot correct the nBreakStart value:
609 // If we have found a hyphenation position, nBreakStart can lie before
611 if ( CH_TXTATR_BREAKWORD
== cFieldChr
)
613 if ( m_nBreakPos
< rInf
.GetIdx() )
614 m_nBreakPos
= nOldIdx
- TextFrameIndex(1);
615 else if (TextFrameIndex(COMPLETE_STRING
) != m_nBreakPos
)
617 OSL_ENSURE( m_nBreakPos
>= m_nFieldDiff
, "I've got field trouble!" );
618 m_nBreakPos
= m_nBreakPos
- m_nFieldDiff
;
621 OSL_ENSURE( m_nCutPos
>= rInf
.GetIdx() && m_nCutPos
>= m_nFieldDiff
,
622 "I've got field trouble, part2!" );
623 m_nCutPos
= m_nCutPos
- m_nFieldDiff
;
625 OUString
& rOldText
= const_cast<OUString
&> (rInf
.GetText());
626 OUString
aReplacement( cFieldChr
);
627 rOldText
= rOldText
.replaceAt(sal_Int32(nOldIdx
) - 1, sal_Int32(m_nFieldDiff
) + 1, aReplacement
);
628 rInf
.SetIdx( nOldIdx
);
630 #if OSL_DEBUG_LEVEL > 0
631 OSL_ENSURE( aDebugString
== rInf
.GetText(),
632 "Somebody, somebody, somebody put something in my string" );
639 rInf
.GetTextSize( &rSI
, rInf
.GetIdx(), nPorLen
,
640 nMaxComp
, m_nBreakWidth
, nMaxSizeDiff
,
641 rInf
.GetCachedVclData().get() );
643 // save maximum width for later use
645 rInf
.SetMaxWidthDiff( &rPor
, nMaxSizeDiff
);
647 m_nBreakWidth
+= nItalic
+ nLeftRightBorderSpace
;
652 if (m_nBreakStart
> rInf
.GetIdx() + nPorLen
+ m_nFieldDiff
)
654 rInf
.GetTextSize(&rSI
, rInf
.GetIdx() + nPorLen
,
655 m_nBreakStart
- rInf
.GetIdx() - nPorLen
- m_nFieldDiff
, nMaxComp
,
656 m_nExtraBlankWidth
, nMaxSizeDiff
, rInf
.GetCachedVclData().get());
661 m_nBreakPos
= m_nCutPos
;
662 // Keep following SwBreakPortion in the same line.
663 if ( CH_BREAK
== rInf
.GetChar( m_nBreakPos
+ m_pHanging
->GetLen() ) )
670 // returns true if word at position nPos has a different spelling
671 // if hyphenated at this position (old german spelling)
672 bool SwTextGuess::AlternativeSpelling( const SwTextFormatInfo
&rInf
,
673 const TextFrameIndex nPos
)
675 // get word boundaries
676 Boundary aBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
677 rInf
.GetText(), sal_Int32(nPos
),
678 g_pBreakIt
->GetLocale( rInf
.GetFont()->GetLanguage() ),
679 WordType::DICTIONARY_WORD
, true );
680 m_nBreakStart
= TextFrameIndex(aBound
.startPos
);
681 sal_Int32 nWordLen
= aBound
.endPos
- sal_Int32(m_nBreakStart
);
683 // if everything else fails, we want to cut at nPos
686 OUString
const aText( rInf
.GetText().copy(sal_Int32(m_nBreakStart
), nWordLen
) );
688 // check, if word has alternative spelling
689 Reference
< XHyphenator
> xHyph( ::GetHyphenator() );
690 OSL_ENSURE( xHyph
.is(), "Hyphenator is missing");
691 //! subtract 1 since the UNO-interface is 0 based
692 m_xHyphWord
= xHyph
->queryAlternativeSpelling( aText
,
693 g_pBreakIt
->GetLocale( rInf
.GetFont()->GetLanguage() ),
694 sal::static_int_cast
<sal_Int16
>(sal_Int32(nPos
- m_nBreakStart
)),
695 rInf
.GetHyphValues() );
696 return m_xHyphWord
.is() && m_xHyphWord
->isAlternativeSpelling();
699 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */