android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / guess.cxx
blob7902e5a8e2c91ff1a718600a8dbf5cbe38371775
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
23 #include "guess.hxx"
24 #include "inftxt.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>
32 #include "porfld.hxx"
33 #include <paratr.hxx>
34 #include <doc.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;
43 namespace{
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() )
59 return false;
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() ) &&
71 rSI.CountCompChg() &&
72 ! rInf.IsMulti() &&
73 ! rPor.InFieldGrp() &&
74 ! rPor.IsDropPortion() ?
75 10000 :
76 0 ;
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
84 // for LTR mode only
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 )
98 break;
99 ++nSpaceCnt;
101 TextFrameIndex nCharsCnt = nMaxLen - nSpaceCnt;
102 if ( nSpaceCnt && nCharsCnt < rPor.GetLen() )
104 if (nSpaceCnt)
105 rInf.GetTextSize( &rSI, rInf.GetIdx() + nCharsCnt, nSpaceCnt,
106 nMaxComp, m_nExtraBlankWidth, nMaxSizeDiff );
107 nMaxLen = nCharsCnt;
108 if ( !nMaxLen )
109 return true;
115 if ( rInf.GetLen() < nMaxLen )
116 nMaxLen = rInf.GetLen();
118 if( !nMaxLen )
119 return false;
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())])
138 bAddItalic = false;
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
145 // formatting is OK.
146 bAddItalic = false;
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;
178 if( nItalic &&
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
186 if ( nMaxSizeDiff )
187 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
189 m_nBreakWidth += nLeftRightBorderSpace;
191 return true;
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
201 if( bHyph )
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 )
233 bHyph = false;
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)
250 break;
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)
258 break;
261 if (!URIHelper::FindFirstURLInText(rInf.GetText(), begin, end, rCC).isEmpty())
263 bHyph = false;
267 // search start of the last word, if needed
268 if ( bHyph )
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 )
282 bCutBlank = true;
283 else if ( bCutBlank )
284 break;
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);
304 else
306 m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() );
308 #if OSL_DEBUG_LEVEL > 1
309 if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
311 sal_uInt16 nMinSize;
312 rInf.GetTextSize( &rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
313 nMaxComp, nMinSize, nMaxSizeDiff );
314 OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" );
316 #endif
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
334 if ( nMaxSizeDiff )
335 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
337 m_nBreakWidth += nLeftRightBorderSpace;
339 return true;
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);
349 return false;
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)]
356 : 0;
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)))
368 --m_nBreakPos;
370 else // #i20878#
372 while (nX && m_nBreakPos > rInf.GetLineStart() + TextFrameIndex(1) &&
373 IsBlank(rInf.GetChar(--nX)))
374 --m_nBreakPos;
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)))
381 ; // nothing
383 m_nBreakStart = m_nCutPos;
385 else
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;
397 #endif
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());
408 OUString aText;
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();
419 #endif
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 );
426 else
427 cFieldChr = 0;
430 LineBreakHyphenationOptions aHyphOpt;
431 Reference< XHyphenator > xHyph;
432 if( bHyph )
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
459 // original string).
460 const TextFrameIndex nDoNotStepOver = CH_TXTATR_BREAKWORD == cFieldChr
461 ? rInf.GetIdx() - m_nFieldDiff - TextFrameIndex(1)
462 : TextFrameIndex(0);
464 if ( nLangIndex > nDoNotStepOver &&
465 TextFrameIndex(rInf.GetText().getLength()) == nLangIndex)
466 --nLangIndex;
468 while ( nLangIndex > nDoNotStepOver &&
469 !rCC.isLetterNumeric(rInf.GetText(), sal_Int32(nLangIndex)))
470 --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
485 ? nDoNotStepOver
486 : nLangIndex,
487 nScript, true);
492 const ForbiddenCharacters aForbidden(
493 *rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().getForbiddenCharacters(aLang, true));
495 const bool bAllowHanging = rInf.IsHanging() && ! rInf.IsMulti() &&
496 ! rInf.GetTextFrame()->IsInTab() &&
497 ! rPor.InFieldGrp();
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
519 // with an index 0
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
574 // underflow
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 ) ) )
581 m_nBreakPos = nX;
583 if( m_nBreakPos > rInf.GetIdx() )
584 nPorLen = m_nBreakPos - rInf.GetIdx();
586 else
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
610 // the field.
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" );
633 #endif
637 if( nPorLen )
639 rInf.GetTextSize( &rSI, rInf.GetIdx(), nPorLen,
640 nMaxComp, m_nBreakWidth, nMaxSizeDiff,
641 rInf.GetCachedVclData().get() );
643 // save maximum width for later use
644 if ( nMaxSizeDiff )
645 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
647 m_nBreakWidth += nItalic + nLeftRightBorderSpace;
649 else
650 m_nBreakWidth = 0;
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());
659 if( m_pHanging )
661 m_nBreakPos = m_nCutPos;
662 // Keep following SwBreakPortion in the same line.
663 if ( CH_BREAK == rInf.GetChar( m_nBreakPos + m_pHanging->GetLen() ) )
664 return true;
667 return false;
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
684 m_nCutPos = 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: */