1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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 "porfld.hxx"
32 #include <paratr.hxx>
33 #include <doc.hxx>
34
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37 using namespace ::com::sun::star::i18n;
38 using namespace ::com::sun::star::beans;
39 using namespace ::com::sun::star::linguistic2;
40
41 namespace{
42
IsBlank(sal_Unicode ch)43 bool IsBlank(sal_Unicode ch) { return ch == CH_BLANK || ch == CH_FULL_BLANK || ch == CH_NB_SPACE || ch == CH_SIX_PER_EM; }
44
45 }
46
47 // provides information for line break calculation
48 // returns true if no line break has to be performed
49 // otherwise possible break or hyphenation position is determined
Guess(const SwTextPortion & rPor,SwTextFormatInfo & rInf,const sal_uInt16 nPorHeight)50 bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
51 const sal_uInt16 nPorHeight )
52 {
53 m_nCutPos = rInf.GetIdx();
54
55 // Empty strings are always 0
56 if( !rInf.GetLen() || rInf.GetText().isEmpty() )
57 return false;
58
59 OSL_ENSURE( rInf.GetIdx() < TextFrameIndex(rInf.GetText().getLength()),
60 "+SwTextGuess::Guess: invalid SwTextFormatInfo" );
61
62 OSL_ENSURE( nPorHeight, "+SwTextGuess::Guess: no height" );
63
64 sal_uInt16 nMaxSizeDiff;
65
66 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
67
68 sal_uInt16 nMaxComp = ( SwFontScript::CJK == rInf.GetFont()->GetActual() ) &&
69 rSI.CountCompChg() &&
70 ! rInf.IsMulti() &&
71 ! rPor.InFieldGrp() &&
72 ! rPor.IsDropPortion() ?
73 10000 :
74 0 ;
75
76 SwTwips nLineWidth = rInf.GetLineWidth();
77 TextFrameIndex nMaxLen = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
78
79 const SvxAdjust& rAdjust = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
80
81 // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
82 // for LTR mode only
83 if ( !rInf.GetTextFrame()->IsRightToLeft() )
84 {
85 if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
86 DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
87 {
88 if ( rAdjust == SvxAdjust::Right || rAdjust == SvxAdjust::Center )
89 {
90 TextFrameIndex nSpaceCnt(0);
91 for (sal_Int32 i = rInf.GetText().getLength() - 1;
92 sal_Int32(rInf.GetIdx()) <= i; --i)
93 {
94 sal_Unicode cChar = rInf.GetText()[i];
95 if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != CH_SIX_PER_EM )
96 break;
97 ++nSpaceCnt;
98 }
99 TextFrameIndex nCharsCnt = nMaxLen - nSpaceCnt;
100 if ( nSpaceCnt && nCharsCnt < rPor.GetLen() )
101 {
102 nMaxLen = nCharsCnt;
103 if ( !nMaxLen )
104 return true;
105 }
106 }
107 }
108 }
109
110 if ( rInf.GetLen() < nMaxLen )
111 nMaxLen = rInf.GetLen();
112
113 if( !nMaxLen )
114 return false;
115
116 sal_uInt16 nItalic = 0;
117 if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
118 {
119 bool bAddItalic = true;
120
121 // do not add extra italic value if we have an active character grid
122 if ( rInf.SnapToGrid() )
123 {
124 SwTextGridItem const*const pGrid(
125 GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
126 bAddItalic = !pGrid || GRID_LINES_CHARS != pGrid->GetGridType();
127 }
128
129 // do not add extra italic value for an isolated blank:
130 if (TextFrameIndex(1) == rInf.GetLen() &&
131 CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx())])
132 {
133 bAddItalic = false;
134 }
135
136 if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
137 DocumentSettingId::TAB_OVER_MARGIN))
138 {
139 // Content is allowed over the margin: in this case over-margin content caused by italic
140 // formatting is OK.
141 bAddItalic = false;
142 }
143
144 nItalic = bAddItalic ? nPorHeight / 12 : 0;
145
146 nLineWidth -= nItalic;
147
148 // #i46524# LineBreak bug with italics
149 if ( nLineWidth < 0 ) nLineWidth = 0;
150 }
151
152 const sal_Int32 nLeftRightBorderSpace =
153 (!rPor.GetJoinBorderWithNext() ? rInf.GetFont()->GetRightBorderSpace() : 0) +
154 (!rPor.GetJoinBorderWithPrev() ? rInf.GetFont()->GetLeftBorderSpace() : 0);
155
156 nLineWidth -= nLeftRightBorderSpace;
157
158 const bool bUnbreakableNumberings = rInf.GetTextFrame()->GetDoc()
159 .getIDocumentSettingAccess().get(DocumentSettingId::UNBREAKABLE_NUMBERINGS);
160
161 // first check if everything fits to line
162 if ( ( nLineWidth * 2 > SwTwips(sal_Int32(nMaxLen)) * nPorHeight ) ||
163 ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
164 {
165 // call GetTextSize with maximum compression (for kanas)
166 rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen,
167 nMaxComp, m_nBreakWidth, nMaxSizeDiff );
168
169 if ( ( m_nBreakWidth <= nLineWidth ) || ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
170 {
171 // portion fits to line
172 m_nCutPos = rInf.GetIdx() + nMaxLen;
173 if( nItalic &&
174 (m_nCutPos >= TextFrameIndex(rInf.GetText().getLength()) ||
175 // #i48035# Needed for CalcFitToContent
176 // if first line ends with a manual line break
177 rInf.GetText()[sal_Int32(m_nCutPos)] == CH_BREAK))
178 m_nBreakWidth = m_nBreakWidth + nItalic;
179
180 // save maximum width for later use
181 if ( nMaxSizeDiff )
182 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
183
184 m_nBreakWidth += nLeftRightBorderSpace;
185
186 return true;
187 }
188 }
189
190 bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud();
191 TextFrameIndex nHyphPos(0);
192
193 // nCutPos is the first character not fitting to the current line
194 // nHyphPos is the first character not fitting to the current line,
195 // considering an additional "-" for hyphenation
196 if( bHyph )
197 {
198 m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() );
199
200 if ( !nHyphPos && rInf.GetIdx() )
201 nHyphPos = rInf.GetIdx() - TextFrameIndex(1);
202 }
203 else
204 {
205 m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() );
206
207 #if OSL_DEBUG_LEVEL > 1
208 if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
209 {
210 sal_uInt16 nMinSize;
211 rInf.GetTextSize( &rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
212 nMaxComp, nMinSize, nMaxSizeDiff );
213 OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" );
214 }
215 #endif
216 }
217
218 if( m_nCutPos > rInf.GetIdx() + nMaxLen )
219 {
220 // second check if everything fits to line
221 m_nCutPos = m_nBreakPos = rInf.GetIdx() + nMaxLen - TextFrameIndex(1);
222 rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp,
223 m_nBreakWidth, nMaxSizeDiff );
224
225 // The following comparison should always give true, otherwise
226 // there likely has been a pixel rounding error in GetTextBreak
227 if ( m_nBreakWidth <= nLineWidth )
228 {
229 if (nItalic && (m_nBreakPos + TextFrameIndex(1)) >= TextFrameIndex(rInf.GetText().getLength()))
230 m_nBreakWidth = m_nBreakWidth + nItalic;
231
232 // save maximum width for later use
233 if ( nMaxSizeDiff )
234 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
235
236 m_nBreakWidth += nLeftRightBorderSpace;
237
238 return true;
239 }
240 }
241
242 // we have to trigger an underflow for a footnote portion
243 // which does not fit to the current line
244 if ( rPor.IsFootnotePortion() )
245 {
246 m_nBreakPos = rInf.GetIdx();
247 m_nCutPos = TextFrameIndex(-1);
248 return false;
249 }
250
251 TextFrameIndex nPorLen(0);
252 // do not call the break iterator nCutPos is a blank
253 sal_Unicode cCutChar = m_nCutPos < TextFrameIndex(rInf.GetText().getLength())
254 ? rInf.GetText()[sal_Int32(m_nCutPos)]
255 : 0;
256 if (IsBlank(cCutChar))
257 {
258 m_nBreakPos = m_nCutPos;
259 TextFrameIndex nX = m_nBreakPos;
260
261 if ( rAdjust == SvxAdjust::Left )
262 {
263 // we step back until a non blank character has been found
264 // or there is only one more character left
265 while (nX && TextFrameIndex(rInf.GetText().getLength()) < m_nBreakPos &&
266 IsBlank(rInf.GetChar(--nX)))
267 --m_nBreakPos;
268 }
269 else // #i20878#
270 {
271 while (nX && m_nBreakPos > rInf.GetLineStart() + TextFrameIndex(1) &&
272 IsBlank(rInf.GetChar(--nX)))
273 --m_nBreakPos;
274 }
275
276 if( m_nBreakPos > rInf.GetIdx() )
277 nPorLen = m_nBreakPos - rInf.GetIdx();
278 while (++m_nCutPos < TextFrameIndex(rInf.GetText().getLength()) &&
279 IsBlank(rInf.GetChar(m_nCutPos)))
280 ; // nothing
281
282 m_nBreakStart = m_nCutPos;
283 }
284 else
285 {
286 // New: We should have a look into the last portion, if it was a
287 // field portion. For this, we expand the text of the field portion
288 // into our string. If the line break position is inside of before
289 // the field portion, we trigger an underflow.
290
291 TextFrameIndex nOldIdx = rInf.GetIdx();
292 sal_Unicode cFieldChr = 0;
293
294 #if OSL_DEBUG_LEVEL > 0
295 OUString aDebugString;
296 #endif
297
298 // be careful: a field portion can be both: 0x01 (common field)
299 // or 0x02 (the follow of a footnode)
300 if ( rInf.GetLast() && rInf.GetLast()->InFieldGrp() &&
301 ! rInf.GetLast()->IsFootnotePortion() &&
302 rInf.GetIdx() > rInf.GetLineStart() &&
303 CH_TXTATR_BREAKWORD ==
304 (cFieldChr = rInf.GetText()[sal_Int32(rInf.GetIdx()) - 1]))
305 {
306 SwFieldPortion* pField = static_cast<SwFieldPortion*>(rInf.GetLast());
307 OUString aText;
308 pField->GetExpText( rInf, aText );
309
310 if ( !aText.isEmpty() )
311 {
312 m_nFieldDiff = TextFrameIndex(aText.getLength() - 1);
313 m_nCutPos = m_nCutPos + m_nFieldDiff;
314 nHyphPos = nHyphPos + m_nFieldDiff;
315
316 #if OSL_DEBUG_LEVEL > 0
317 aDebugString = rInf.GetText();
318 #endif
319
320 // this is pretty nutso... reverted at the end...
321 OUString& rOldText = const_cast<OUString&> (rInf.GetText());
322 rOldText = rOldText.replaceAt(sal_Int32(rInf.GetIdx()) - 1, 1, aText);
323 rInf.SetIdx( rInf.GetIdx() + m_nFieldDiff );
324 }
325 else
326 cFieldChr = 0;
327 }
328
329 LineBreakHyphenationOptions aHyphOpt;
330 Reference< XHyphenator > xHyph;
331 if( bHyph )
332 {
333 xHyph = ::GetHyphenator();
334 aHyphOpt = LineBreakHyphenationOptions( xHyph,
335 rInf.GetHyphValues(), sal_Int32(nHyphPos));
336 }
337
338 // Get Language for break iterator.
339 // We have to switch the current language if we have a script
340 // change at nCutPos. Otherwise LATIN punctuation would never
341 // be allowed to be hanging punctuation.
342 // NEVER call GetLang if the string has been modified!!!
343 LanguageType aLang = rInf.GetFont()->GetLanguage();
344
345 // If we are inside a field portion, we use a temporary string which
346 // differs from the string at the textnode. Therefore we are not allowed
347 // to call the GetLang function.
348 if ( m_nCutPos && ! rPor.InFieldGrp() )
349 {
350 const CharClass& rCC = GetAppCharClass();
351
352 // step back until a non-punctuation character is reached
353 TextFrameIndex nLangIndex = m_nCutPos;
354
355 // If a field has been expanded right in front of us we do not
356 // step further than the beginning of the expanded field
357 // (which is the position of the field placeholder in our
358 // original string).
359 const TextFrameIndex nDoNotStepOver = CH_TXTATR_BREAKWORD == cFieldChr
360 ? rInf.GetIdx() - m_nFieldDiff - TextFrameIndex(1)
361 : TextFrameIndex(0);
362
363 if ( nLangIndex > nDoNotStepOver &&
364 TextFrameIndex(rInf.GetText().getLength()) == nLangIndex)
365 --nLangIndex;
366
367 while ( nLangIndex > nDoNotStepOver &&
368 !rCC.isLetterNumeric(rInf.GetText(), sal_Int32(nLangIndex)))
369 --nLangIndex;
370
371 // last "real" character is not inside our current portion
372 // we have to check the script type of the last "real" character
373 if ( nLangIndex < rInf.GetIdx() )
374 {
375 sal_uInt16 nScript = g_pBreakIt->GetRealScriptOfText( rInf.GetText(),
376 sal_Int32(nLangIndex));
377 OSL_ENSURE( nScript, "Script is not between 1 and 4" );
378
379 // compare current script with script from last "real" character
380 if ( SwFontScript(nScript - 1) != rInf.GetFont()->GetActual() )
381 {
382 aLang = rInf.GetTextFrame()->GetLangOfChar(
383 CH_TXTATR_BREAKWORD == cFieldChr
384 ? nDoNotStepOver
385 : nLangIndex,
386 nScript, true);
387 }
388 }
389 }
390
391 const ForbiddenCharacters aForbidden(
392 *rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().getForbiddenCharacters(aLang, true));
393
394 const bool bAllowHanging = rInf.IsHanging() && ! rInf.IsMulti() &&
395 ! rInf.GetTextFrame()->IsInTab() &&
396 ! rPor.InFieldGrp();
397
398 LineBreakUserOptions aUserOpt(
399 aForbidden.beginLine, aForbidden.endLine,
400 rInf.HasForbiddenChars(), bAllowHanging, false );
401
402 // !!! We must have a local copy of the locale, because inside
403 // getLineBreak the LinguEventListener can trigger a new formatting,
404 // which can corrupt the locale pointer inside pBreakIt.
405 const lang::Locale aLocale = g_pBreakIt->GetLocale( aLang );
406
407 // determines first possible line break from nCutPos to
408 // start index of current line
409 LineBreakResults aResult = g_pBreakIt->GetBreakIter()->getLineBreak(
410 rInf.GetText(), sal_Int32(m_nCutPos), aLocale,
411 sal_Int32(rInf.GetLineStart()), aHyphOpt, aUserOpt );
412
413 m_nBreakPos = TextFrameIndex(aResult.breakIndex);
414
415 // if we are formatting multi portions we want to allow line breaks
416 // at the border between single line and multi line portion
417 // we have to be careful with footnote portions, they always come in
418 // with an index 0
419 if ( m_nBreakPos < rInf.GetLineStart() && rInf.IsFirstMulti() &&
420 ! rInf.IsFootnoteInside() )
421 m_nBreakPos = rInf.GetLineStart();
422
423 m_nBreakStart = m_nBreakPos;
424
425 bHyph = BreakType::HYPHENATION == aResult.breakType;
426
427 if (bHyph && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
428 {
429 // found hyphenation position within line
430 // nBreakPos is set to the hyphenation position
431 m_xHyphWord = aResult.rHyphenatedWord;
432 m_nBreakPos += TextFrameIndex(m_xHyphWord->getHyphenationPos() + 1);
433
434 // if not in interactive mode, we have to break behind a soft hyphen
435 if ( ! rInf.IsInterHyph() && rInf.GetIdx() )
436 {
437 sal_Int32 const nSoftHyphPos =
438 m_xHyphWord->getWord().indexOf( CHAR_SOFTHYPHEN );
439
440 if ( nSoftHyphPos >= 0 &&
441 m_nBreakStart + TextFrameIndex(nSoftHyphPos) <= m_nBreakPos &&
442 m_nBreakPos > rInf.GetLineStart() )
443 m_nBreakPos = rInf.GetIdx() - TextFrameIndex(1);
444 }
445
446 if( m_nBreakPos >= rInf.GetIdx() )
447 {
448 nPorLen = m_nBreakPos - rInf.GetIdx();
449 if ('-' == rInf.GetText()[ sal_Int32(m_nBreakPos) - 1 ])
450 m_xHyphWord = nullptr;
451 }
452 }
453 else if ( !bHyph && m_nBreakPos >= rInf.GetLineStart() )
454 {
455 OSL_ENSURE(sal_Int32(m_nBreakPos) != COMPLETE_STRING, "we should have found a break pos");
456
457 // found break position within line
458 m_xHyphWord = nullptr;
459
460 // check, if break position is soft hyphen and an underflow
461 // has to be triggered
462 if( m_nBreakPos > rInf.GetLineStart() && rInf.GetIdx() &&
463 CHAR_SOFTHYPHEN == rInf.GetText()[ sal_Int32(m_nBreakPos) - 1 ])
464 {
465 m_nBreakPos = rInf.GetIdx() - TextFrameIndex(1);
466 }
467
468 if( rAdjust != SvxAdjust::Left )
469 {
470 // Delete any blanks at the end of a line, but be careful:
471 // If a field has been expanded, we do not want to delete any
472 // blanks inside the field portion. This would cause an unwanted
473 // underflow
474 TextFrameIndex nX = m_nBreakPos;
475 while( nX > rInf.GetLineStart() &&
476 ( CH_TXTATR_BREAKWORD != cFieldChr || nX > rInf.GetIdx() ) &&
477 ( CH_BLANK == rInf.GetChar( --nX ) ||
478 CH_SIX_PER_EM == rInf.GetChar( nX ) ||
479 CH_FULL_BLANK == rInf.GetChar( nX ) ) )
480 m_nBreakPos = nX;
481 }
482 if( m_nBreakPos > rInf.GetIdx() )
483 nPorLen = m_nBreakPos - rInf.GetIdx();
484 }
485 else
486 {
487 // no line break found, setting nBreakPos to COMPLETE_STRING
488 // causes a break cut
489 m_nBreakPos = TextFrameIndex(COMPLETE_STRING);
490 OSL_ENSURE( m_nCutPos >= rInf.GetIdx(), "Deep cut" );
491 nPorLen = m_nCutPos - rInf.GetIdx();
492 }
493
494 if (m_nBreakPos > m_nCutPos && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
495 {
496 const TextFrameIndex nHangingLen = m_nBreakPos - m_nCutPos;
497 SwPosSize aTmpSize = rInf.GetTextSize( &rSI, m_nCutPos, nHangingLen );
498 aTmpSize.Width(aTmpSize.Width() + nLeftRightBorderSpace);
499 OSL_ENSURE( !m_pHanging, "A hanging portion is hanging around" );
500 m_pHanging.reset( new SwHangingPortion( aTmpSize ) );
501 m_pHanging->SetLen( nHangingLen );
502 nPorLen = m_nCutPos - rInf.GetIdx();
503 }
504
505 // If we expanded a field, we must repair the original string.
506 // In case we do not trigger an underflow, we correct the nBreakPos
507 // value, but we cannot correct the nBreakStart value:
508 // If we have found a hyphenation position, nBreakStart can lie before
509 // the field.
510 if ( CH_TXTATR_BREAKWORD == cFieldChr )
511 {
512 if ( m_nBreakPos < rInf.GetIdx() )
513 m_nBreakPos = nOldIdx - TextFrameIndex(1);
514 else if (TextFrameIndex(COMPLETE_STRING) != m_nBreakPos)
515 {
516 OSL_ENSURE( m_nBreakPos >= m_nFieldDiff, "I've got field trouble!" );
517 m_nBreakPos = m_nBreakPos - m_nFieldDiff;
518 }
519
520 OSL_ENSURE( m_nCutPos >= rInf.GetIdx() && m_nCutPos >= m_nFieldDiff,
521 "I've got field trouble, part2!" );
522 m_nCutPos = m_nCutPos - m_nFieldDiff;
523
524 OUString& rOldText = const_cast<OUString&> (rInf.GetText());
525 OUString aReplacement( cFieldChr );
526 rOldText = rOldText.replaceAt(sal_Int32(nOldIdx) - 1, sal_Int32(m_nFieldDiff) + 1, aReplacement);
527 rInf.SetIdx( nOldIdx );
528
529 #if OSL_DEBUG_LEVEL > 0
530 OSL_ENSURE( aDebugString == rInf.GetText(),
531 "Somebody, somebody, somebody put something in my string" );
532 #endif
533 }
534 }
535
536 if( nPorLen )
537 {
538 rInf.GetTextSize( &rSI, rInf.GetIdx(), nPorLen,
539 nMaxComp, m_nBreakWidth, nMaxSizeDiff,
540 rInf.GetCachedVclData().get() );
541
542 // save maximum width for later use
543 if ( nMaxSizeDiff )
544 rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
545
546 m_nBreakWidth += nItalic + nLeftRightBorderSpace;
547 }
548 else
549 m_nBreakWidth = 0;
550
551 if( m_pHanging )
552 {
553 m_nBreakPos = m_nCutPos;
554 // Keep following SwBreakPortion in the same line.
555 if ( CH_BREAK == rInf.GetChar( m_nBreakPos + m_pHanging->GetLen() ) )
556 return true;
557 }
558
559 return false;
560 }
561
562 // returns true if word at position nPos has a different spelling
563 // if hyphenated at this position (old german spelling)
AlternativeSpelling(const SwTextFormatInfo & rInf,const TextFrameIndex nPos)564 bool SwTextGuess::AlternativeSpelling( const SwTextFormatInfo &rInf,
565 const TextFrameIndex nPos)
566 {
567 // get word boundaries
568 Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
569 rInf.GetText(), sal_Int32(nPos),
570 g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ),
571 WordType::DICTIONARY_WORD, true );
572 m_nBreakStart = TextFrameIndex(aBound.startPos);
573 sal_Int32 nWordLen = aBound.endPos - sal_Int32(m_nBreakStart);
574
575 // if everything else fails, we want to cut at nPos
576 m_nCutPos = nPos;
577
578 OUString const aText( rInf.GetText().copy(sal_Int32(m_nBreakStart), nWordLen) );
579
580 // check, if word has alternative spelling
581 Reference< XHyphenator > xHyph( ::GetHyphenator() );
582 OSL_ENSURE( xHyph.is(), "Hyphenator is missing");
583 //! subtract 1 since the UNO-interface is 0 based
584 m_xHyphWord = xHyph->queryAlternativeSpelling( aText,
585 g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ),
586 sal::static_int_cast<sal_Int16>(sal_Int32(nPos - m_nBreakStart)),
587 rInf.GetHyphValues() );
588 return m_xHyphWord.is() && m_xHyphWord->isAlternativeSpelling();
589 }
590
591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
592