Line data Source code
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 :
21 : #include <vcl/wrkwin.hxx>
22 : #include <vcl/dialog.hxx>
23 : #include <vcl/msgbox.hxx>
24 : #include <vcl/svapp.hxx>
25 : #include <vcl/metaact.hxx>
26 : #include <vcl/gdimtf.hxx>
27 : #include <vcl/settings.hxx>
28 :
29 : #include <editeng/adjustitem.hxx>
30 : #include <editeng/tstpitem.hxx>
31 : #include <editeng/lspcitem.hxx>
32 : #include <editeng/flditem.hxx>
33 : #include <impedit.hxx>
34 : #include <editeng/editeng.hxx>
35 : #include <editeng/editview.hxx>
36 : #include <editeng/txtrange.hxx>
37 : #include <editeng/charsetcoloritem.hxx>
38 : #include <editeng/colritem.hxx>
39 : #include <editeng/udlnitem.hxx>
40 : #include <editeng/fhgtitem.hxx>
41 : #include <editeng/kernitem.hxx>
42 : #include <editeng/lrspitem.hxx>
43 : #include <editeng/ulspitem.hxx>
44 : #include <editeng/fontitem.hxx>
45 : #include <editeng/wghtitem.hxx>
46 : #include <editeng/postitem.hxx>
47 : #include <editeng/langitem.hxx>
48 : #include <editeng/scriptspaceitem.hxx>
49 : #include <editeng/charscaleitem.hxx>
50 : #include <editeng/numitem.hxx>
51 : #include <editeng/justifyitem.hxx>
52 :
53 : #include <svtools/colorcfg.hxx>
54 : #include <svl/ctloptions.hxx>
55 :
56 : #include <editeng/forbiddencharacterstable.hxx>
57 :
58 : #include <unotools/localedatawrapper.hxx>
59 :
60 : #include <editeng/unolingu.hxx>
61 :
62 : #include <set>
63 : #include <math.h>
64 : #include <vcl/metric.hxx>
65 : #include <com/sun/star/i18n/BreakIterator.hpp>
66 : #include <com/sun/star/i18n/ScriptType.hpp>
67 : #include <com/sun/star/i18n/InputSequenceChecker.hpp>
68 : #include <com/sun/star/text/CharacterCompressionType.hpp>
69 : #include <vcl/pdfextoutdevdata.hxx>
70 : #include <i18nlangtag/mslangid.hxx>
71 :
72 : #include <comphelper/processfactory.hxx>
73 : #include <rtl/ustrbuf.hxx>
74 : #include <comphelper/string.hxx>
75 : #include <boost/scoped_array.hpp>
76 :
77 : using namespace ::com::sun::star;
78 : using namespace ::com::sun::star::uno;
79 : using namespace ::com::sun::star::beans;
80 : using namespace ::com::sun::star::linguistic2;
81 :
82 : #define CH_HYPH '-'
83 :
84 : #define RESDIFF 10
85 :
86 : #define WRONG_SHOW_MIN 5
87 :
88 : struct TabInfo
89 : {
90 : bool bValid;
91 :
92 : SvxTabStop aTabStop;
93 : sal_Int32 nCharPos;
94 : sal_Int32 nTabPortion;
95 : long nStartPosX;
96 : long nTabPos;
97 :
98 119976 : TabInfo()
99 : : bValid(false)
100 : , nCharPos(0)
101 : , nTabPortion(0)
102 : , nStartPosX(0)
103 119976 : , nTabPos(0)
104 119976 : { }
105 :
106 : };
107 :
108 0 : Point Rotate( const Point& rPoint, short nOrientation, const Point& rOrigin )
109 : {
110 0 : double nRealOrientation = nOrientation*F_PI1800;
111 0 : double nCos = cos( nRealOrientation );
112 0 : double nSin = sin( nRealOrientation );
113 :
114 0 : Point aRotatedPos;
115 0 : Point aTranslatedPos( rPoint );
116 :
117 : // Translation
118 0 : aTranslatedPos -= rOrigin;
119 :
120 : // Rotation...
121 0 : aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
122 0 : aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
123 0 : aTranslatedPos = aRotatedPos;
124 :
125 : // Translation...
126 0 : aTranslatedPos += rOrigin;
127 0 : return aTranslatedPos;
128 : }
129 :
130 0 : sal_uInt8 GetCharTypeForCompression( sal_Unicode cChar )
131 : {
132 0 : switch ( cChar )
133 : {
134 : case 0x3008: case 0x300A: case 0x300C: case 0x300E:
135 : case 0x3010: case 0x3014: case 0x3016: case 0x3018:
136 : case 0x301A: case 0x301D:
137 : {
138 0 : return CHAR_PUNCTUATIONRIGHT;
139 : }
140 : case 0x3001: case 0x3002: case 0x3009: case 0x300B:
141 : case 0x300D: case 0x300F: case 0x3011: case 0x3015:
142 : case 0x3017: case 0x3019: case 0x301B: case 0x301E:
143 : case 0x301F:
144 : {
145 0 : return CHAR_PUNCTUATIONLEFT;
146 : }
147 : default:
148 : {
149 0 : return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? CHAR_KANA : CHAR_NORMAL;
150 : }
151 : }
152 : }
153 :
154 107 : static void lcl_DrawRedLines(
155 : OutputDevice* pOutDev,
156 : long nFontHeight,
157 : const Point& rPnt,
158 : size_t nIndex,
159 : size_t nMaxEnd,
160 : const sal_Int32* pDXArray,
161 : WrongList* pWrongs,
162 : short nOrientation,
163 : const Point& rOrigin,
164 : sal_Bool bVertical,
165 : sal_Bool bIsRightToLeft )
166 : {
167 : // But only if font is not too small ...
168 107 : long nHght = pOutDev->LogicToPixel( Size( 0, nFontHeight ) ).Height();
169 107 : if( WRONG_SHOW_MIN < nHght )
170 : {
171 107 : size_t nEnd, nStart = nIndex;
172 107 : bool bWrong = pWrongs->NextWrong( nStart, nEnd );
173 321 : while ( bWrong )
174 : {
175 107 : if ( nStart >= nMaxEnd )
176 0 : break;
177 :
178 107 : if ( nStart < nIndex ) // Corrected
179 0 : nStart = nIndex;
180 107 : if ( nEnd > nMaxEnd )
181 101 : nEnd = nMaxEnd;
182 107 : Point aPnt1( rPnt );
183 107 : if ( bVertical )
184 : {
185 : // VCL doesn't know that the text is vertical, and is manipulating
186 : // the positions a little bit in y direction...
187 0 : long nOnePixel = pOutDev->PixelToLogic( Size( 0, 1 ) ).Height();
188 0 : long nCorrect = 2*nOnePixel;
189 0 : aPnt1.Y() -= nCorrect;
190 0 : aPnt1.X() -= nCorrect;
191 : }
192 107 : if ( nStart > nIndex )
193 : {
194 0 : if ( !bVertical )
195 : {
196 : // since for RTL portions rPnt is on the visual right end of the portion
197 : // (i.e. at the start of the first RTL char) we need to subtract the offset
198 : // for RTL portions...
199 0 : aPnt1.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nStart - nIndex - 1 ];
200 : }
201 : else
202 0 : aPnt1.Y() += pDXArray[ nStart - nIndex - 1 ];
203 : }
204 107 : Point aPnt2( rPnt );
205 : DBG_ASSERT( nEnd > nIndex, "RedLine: aPnt2?" );
206 107 : if ( !bVertical )
207 : {
208 : // since for RTL portions rPnt is on the visual right end of the portion
209 : // (i.e. at the start of the first RTL char) we need to subtract the offset
210 : // for RTL portions...
211 107 : aPnt2.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nEnd - nIndex - 1 ];
212 : }
213 : else
214 0 : aPnt2.Y() += pDXArray[ nEnd - nIndex - 1 ];
215 107 : if ( nOrientation )
216 : {
217 0 : aPnt1 = Rotate( aPnt1, nOrientation, rOrigin );
218 0 : aPnt2 = Rotate( aPnt2, nOrientation, rOrigin );
219 : }
220 :
221 107 : pOutDev->DrawWaveLine( aPnt1, aPnt2 );
222 :
223 107 : nStart = nEnd+1;
224 107 : if ( nEnd < nMaxEnd )
225 1 : bWrong = pWrongs->NextWrong( nStart, nEnd );
226 : else
227 106 : bWrong = false;
228 : }
229 : }
230 107 : }
231 :
232 453 : static Point lcl_ImplCalcRotatedPos( Point rPos, Point rOrigin, double nSin, double nCos )
233 : {
234 453 : Point aRotatedPos;
235 : // Translation...
236 453 : Point aTranslatedPos( rPos);
237 453 : aTranslatedPos -= rOrigin;
238 :
239 453 : aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
240 453 : aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
241 453 : aTranslatedPos = aRotatedPos;
242 : // Translation...
243 453 : aTranslatedPos += rOrigin;
244 :
245 453 : return aTranslatedPos;
246 : }
247 :
248 0 : static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh ) // For Kashidas from sw/source/core/text/porlay.txt
249 : {
250 : // Lam + Alef
251 0 : return ( 0x644 == cCh && 0x627 == cNextCh ) ||
252 : // Beh + Reh
253 0 : ( 0x628 == cCh && 0x631 == cNextCh );
254 : }
255 :
256 0 : static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh ) // For Kashidas from sw/source/core/text/porlay.txt
257 : {
258 : // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
259 0 : bool bRet = 0x627 != cPrevCh && 0x62F != cPrevCh && 0x630 != cPrevCh &&
260 0 : 0x631 != cPrevCh && 0x632 != cPrevCh && 0x648 != cPrevCh;
261 :
262 : // check for ligatures cPrevChar + cChar
263 0 : if ( bRet )
264 0 : bRet = ! lcl_IsLigature( cPrevCh, cCh );
265 :
266 0 : return bRet;
267 : }
268 :
269 :
270 :
271 : // class ImpEditEngine
272 :
273 1334325 : void ImpEditEngine::UpdateViews( EditView* pCurView )
274 : {
275 1334325 : if ( !GetUpdateMode() || IsFormatting() || aInvalidRect.IsEmpty() )
276 2328263 : return;
277 :
278 : DBG_ASSERT( IsFormatted(), "UpdateViews: Doc not formatted!" );
279 :
280 340997 : for (size_t nView = 0; nView < aEditViews.size(); ++nView)
281 : {
282 610 : EditView* pView = aEditViews[nView];
283 610 : pView->HideCursor();
284 :
285 610 : Rectangle aClipRect( aInvalidRect );
286 610 : Rectangle aVisArea( pView->GetVisArea() );
287 610 : aClipRect.Intersection( aVisArea );
288 :
289 610 : if ( !aClipRect.IsEmpty() )
290 : {
291 : // convert to window coordinates ....
292 609 : aClipRect = pView->pImpEditView->GetWindowPos( aClipRect );
293 :
294 609 : if ( pView == pCurView )
295 362 : Paint( pView->pImpEditView, aClipRect, 0, sal_True );
296 : else
297 247 : pView->GetWindow()->Invalidate( aClipRect );
298 : }
299 : }
300 :
301 340387 : if ( pCurView )
302 : {
303 363 : sal_Bool bGotoCursor = pCurView->pImpEditView->DoAutoScroll();
304 363 : pCurView->ShowCursor( bGotoCursor );
305 : }
306 :
307 340387 : aInvalidRect = Rectangle();
308 340387 : CallStatusHdl();
309 : }
310 :
311 808 : IMPL_LINK_NOARG(ImpEditEngine, OnlineSpellHdl)
312 : {
313 404 : if ( !Application::AnyInput( VCL_INPUT_KEYBOARD ) && GetUpdateMode() && IsFormatted() )
314 5 : DoOnlineSpelling();
315 : else
316 399 : aOnlineSpellTimer.Start();
317 :
318 404 : return 0;
319 : }
320 :
321 0 : IMPL_LINK_NOARG_INLINE_START(ImpEditEngine, IdleFormatHdl)
322 : {
323 0 : aIdleFormatter.ResetRestarts();
324 :
325 : // #i97146# check if that view is still available
326 : // else probably the idle format timer fired while we're already
327 : // downing
328 0 : EditView* pView = aIdleFormatter.GetView();
329 0 : for (size_t nView = 0; nView < aEditViews.size(); ++nView)
330 : {
331 0 : if( aEditViews[nView] == pView )
332 : {
333 0 : FormatAndUpdate( pView );
334 0 : break;
335 : }
336 : }
337 0 : return 0;
338 : }
339 0 : IMPL_LINK_NOARG_INLINE_END(ImpEditEngine, IdleFormatHdl)
340 :
341 1912 : void ImpEditEngine::CheckIdleFormatter()
342 : {
343 1912 : aIdleFormatter.ForceTimeout();
344 : // If not idle, but still not formatted:
345 1912 : if ( !IsFormatted() )
346 24 : FormatDoc();
347 1912 : }
348 :
349 183215 : void ImpEditEngine::FormatFullDoc()
350 : {
351 366490 : for ( sal_Int32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
352 183275 : GetParaPortions()[nPortion]->MarkSelectionInvalid( 0, GetParaPortions()[nPortion]->GetNode()->Len() );
353 183215 : FormatDoc();
354 183215 : }
355 :
356 1338114 : void ImpEditEngine::FormatDoc()
357 : {
358 1338114 : if (!GetUpdateMode() || IsFormatting())
359 2320527 : return;
360 :
361 355701 : EnterBlockNotifications();
362 :
363 355701 : bIsFormatting = true;
364 :
365 : // Then I can also start the spell-timer ...
366 355701 : if ( GetStatus().DoOnlineSpelling() )
367 35587 : StartOnlineSpellTimer();
368 :
369 355701 : long nY = 0;
370 355701 : bool bGrow = false;
371 :
372 355701 : Font aOldFont( GetRefDevice()->GetFont() );
373 :
374 : // Here already, so that not always in CreateLines...
375 355701 : bool bMapChanged = ImpCheckRefMapMode();
376 :
377 355701 : aInvalidRect = Rectangle(); // make empty
378 719015 : for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
379 : {
380 363314 : ParaPortion* pParaPortion = GetParaPortions()[nPara];
381 363314 : if ( pParaPortion->MustRepaint() || ( pParaPortion->IsInvalid() && pParaPortion->IsVisible() ) )
382 : {
383 347120 : if ( pParaPortion->IsInvalid() )
384 : {
385 347120 : sal_Bool bChangedByDerivedClass = GetEditEnginePtr()->FormattingParagraph( nPara );
386 347120 : if ( bChangedByDerivedClass )
387 : {
388 0 : pParaPortion->GetTextPortions().Reset();
389 0 : pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
390 : }
391 : }
392 : // No formatting should be necessary for MustRepaint()!
393 694240 : if ( ( pParaPortion->MustRepaint() && !pParaPortion->IsInvalid() )
394 694240 : || CreateLines( nPara, nY ) )
395 : {
396 267345 : if ( !bGrow && GetTextRanger() )
397 : {
398 : // For a change in height all below must be reformatted ...
399 0 : for ( sal_Int32 n = nPara+1; n < GetParaPortions().Count(); n++ )
400 : {
401 0 : ParaPortion* pPP = GetParaPortions()[n];
402 0 : pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
403 0 : pPP->GetLines().Reset();
404 : }
405 : }
406 267345 : bGrow = true;
407 267345 : if ( IsCallParaInsertedOrDeleted() )
408 267345 : GetEditEnginePtr()->ParagraphHeightChanged( nPara );
409 267345 : pParaPortion->SetMustRepaint( sal_False );
410 : }
411 :
412 : // InvalidRect set only once...
413 347120 : if ( aInvalidRect.IsEmpty() )
414 : {
415 : // For Paperwidth 0 (AutoPageSize) it would otherwise be Empty()...
416 340416 : long nWidth = std::max( (long)1, ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) );
417 340416 : Range aInvRange( GetInvalidYOffsets( pParaPortion ) );
418 340416 : aInvalidRect = Rectangle( Point( 0, nY+aInvRange.Min() ),
419 680832 : Size( nWidth, aInvRange.Len() ) );
420 : }
421 : else
422 : {
423 6704 : aInvalidRect.Bottom() = nY + pParaPortion->GetHeight();
424 : }
425 : }
426 16194 : else if ( bGrow )
427 : {
428 34 : aInvalidRect.Bottom() = nY + pParaPortion->GetHeight();
429 : }
430 363314 : nY += pParaPortion->GetHeight();
431 : }
432 :
433 : // One can also get into the formatting through UpdateMode ON=>OFF=>ON...
434 : // enable optimization first after Vobis delivery ...
435 : {
436 : sal_uInt32 nNewHeightNTP;
437 355701 : sal_uInt32 nNewHeight = CalcTextHeight( &nNewHeightNTP );
438 355701 : long nDiff = nNewHeight - nCurTextHeight;
439 355701 : if ( nDiff )
440 260831 : aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTHEIGHTCHANGED : EE_STAT_TEXTWIDTHCHANGED;
441 355701 : if ( nNewHeight < nCurTextHeight )
442 : {
443 321 : aInvalidRect.Bottom() = (long)std::max( nNewHeight, nCurTextHeight );
444 321 : if ( aInvalidRect.IsEmpty() )
445 : {
446 0 : aInvalidRect.Top() = 0;
447 : // Left and Right are not evaluated, are however set due to IsEmpty.
448 0 : aInvalidRect.Left() = 0;
449 0 : aInvalidRect.Right() = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
450 : }
451 : }
452 :
453 355701 : nCurTextHeight = nNewHeight;
454 355701 : nCurTextHeightNTP = nNewHeightNTP;
455 :
456 355701 : if ( aStatus.AutoPageSize() )
457 18510 : CheckAutoPageSize();
458 337191 : else if ( nDiff )
459 : {
460 243026 : for (size_t nView = 0; nView < aEditViews.size(); ++nView)
461 : {
462 127 : EditView* pView = aEditViews[nView];
463 127 : ImpEditView* pImpView = pView->pImpEditView;
464 127 : if ( pImpView->DoAutoHeight() )
465 : {
466 0 : Size aSz( pImpView->GetOutputArea().GetWidth(), nCurTextHeight );
467 0 : if ( aSz.Height() > aMaxAutoPaperSize.Height() )
468 0 : aSz.Height() = aMaxAutoPaperSize.Height();
469 0 : else if ( aSz.Height() < aMinAutoPaperSize.Height() )
470 0 : aSz.Height() = aMinAutoPaperSize.Height();
471 : pImpView->ResetOutputArea( Rectangle(
472 0 : pImpView->GetOutputArea().TopLeft(), aSz ) );
473 : }
474 : }
475 : }
476 : }
477 :
478 355701 : if ( aStatus.DoRestoreFont() )
479 0 : GetRefDevice()->SetFont( aOldFont );
480 355701 : bIsFormatting = false;
481 355701 : bFormatted = true;
482 :
483 355701 : if ( bMapChanged )
484 29 : GetRefDevice()->Pop();
485 :
486 355701 : CallStatusHdl(); // If Modified...
487 :
488 355701 : LeaveBlockNotifications();
489 : }
490 :
491 475677 : bool ImpEditEngine::ImpCheckRefMapMode()
492 : {
493 475677 : bool bChange = false;
494 :
495 475677 : if ( aStatus.DoFormat100() )
496 : {
497 473028 : MapMode aMapMode( GetRefDevice()->GetMapMode() );
498 473028 : if ( aMapMode.GetScaleX().GetNumerator() != aMapMode.GetScaleX().GetDenominator() )
499 29 : bChange = true;
500 472999 : else if ( aMapMode.GetScaleY().GetNumerator() != aMapMode.GetScaleY().GetDenominator() )
501 0 : bChange = true;
502 :
503 473028 : if ( bChange )
504 : {
505 29 : Fraction Scale1( 1, 1 );
506 29 : aMapMode.SetScaleX( Scale1 );
507 29 : aMapMode.SetScaleY( Scale1 );
508 29 : GetRefDevice()->Push();
509 29 : GetRefDevice()->SetMapMode( aMapMode );
510 473028 : }
511 : }
512 :
513 475677 : return bChange;
514 : }
515 :
516 18510 : void ImpEditEngine::CheckAutoPageSize()
517 : {
518 18510 : Size aPrevPaperSize( GetPaperSize() );
519 18510 : if ( GetStatus().AutoPageWidth() )
520 18510 : aPaperSize.Width() = !IsVertical() ? CalcTextWidth( sal_True ) : GetTextHeight();
521 18510 : if ( GetStatus().AutoPageHeight() )
522 18510 : aPaperSize.Height() = !IsVertical() ? GetTextHeight() : CalcTextWidth( sal_True );
523 :
524 18510 : SetValidPaperSize( aPaperSize ); // consider Min, Max
525 :
526 18510 : if ( aPaperSize != aPrevPaperSize )
527 : {
528 54599 : if ( ( !IsVertical() && ( aPaperSize.Width() != aPrevPaperSize.Width() ) )
529 22983 : || ( IsVertical() && ( aPaperSize.Height() != aPrevPaperSize.Height() ) ) )
530 : {
531 : // If ahead is centered / right or tabs ...
532 13423 : aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTWIDTHCHANGED : EE_STAT_TEXTHEIGHTCHANGED;
533 26964 : for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
534 : {
535 : // Only paragraphs which are not aligned to the left need to be
536 : // reformatted, the height can not be changed here anymore.
537 13541 : ParaPortion* pParaPortion = GetParaPortions()[nPara];
538 13541 : ContentNode* pNode = pParaPortion->GetNode();
539 13541 : SvxAdjust eJustification = GetJustification( nPara );
540 13541 : if ( eJustification != SVX_ADJUST_LEFT )
541 : {
542 426 : pParaPortion->MarkSelectionInvalid( 0, pNode->Len() );
543 426 : CreateLines( nPara, 0 ); // 0: For AutoPageSize no TextRange!
544 : }
545 : }
546 : }
547 :
548 18201 : Size aInvSize = aPaperSize;
549 18201 : if ( aPaperSize.Width() < aPrevPaperSize.Width() )
550 0 : aInvSize.Width() = aPrevPaperSize.Width();
551 18201 : if ( aPaperSize.Height() < aPrevPaperSize.Height() )
552 7306 : aInvSize.Height() = aPrevPaperSize.Height();
553 :
554 18201 : Size aSz( aInvSize );
555 18201 : if ( IsVertical() )
556 : {
557 4 : aSz.Width() = aInvSize.Height();
558 4 : aSz.Height() = aInvSize.Width();
559 : }
560 18201 : aInvalidRect = Rectangle( Point(), aSz );
561 :
562 :
563 18201 : for (size_t nView = 0; nView < aEditViews.size(); ++nView)
564 : {
565 0 : EditView* pView = aEditViews[nView];
566 0 : pView->pImpEditView->RecalcOutputArea();
567 : }
568 : }
569 18510 : }
570 :
571 20919 : static sal_Int32 ImplCalculateFontIndependentLineSpacing( const sal_Int32 nFontHeight )
572 : {
573 20919 : return ( nFontHeight * 12 ) / 10; // + 20%
574 : }
575 :
576 347546 : sal_Bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
577 : {
578 347546 : ParaPortion* pParaPortion = GetParaPortions()[nPara];
579 :
580 : // sal_Bool: Changes in the height of paragraph Yes / No - sal_True/sal_False
581 : DBG_ASSERT( pParaPortion->GetNode(), "Portion without Node in CreateLines" );
582 : DBG_ASSERT( pParaPortion->IsVisible(), "Invisible paragraphs not formatted!" );
583 : DBG_ASSERT( pParaPortion->IsInvalid(), "CreateLines: Portion not invalid!" );
584 :
585 347546 : bool bProcessingEmptyLine = ( pParaPortion->GetNode()->Len() == 0 );
586 347546 : bool bEmptyNodeWithPolygon = ( pParaPortion->GetNode()->Len() == 0 ) && GetTextRanger();
587 :
588 :
589 : // Fast special treatment for empty paragraphs ...
590 :
591 347546 : if ( ( pParaPortion->GetNode()->Len() == 0 ) && !GetTextRanger() )
592 : {
593 : // fast special treatment ...
594 227570 : if ( pParaPortion->GetTextPortions().Count() )
595 78470 : pParaPortion->GetTextPortions().Reset();
596 227570 : if ( pParaPortion->GetLines().Count() )
597 78470 : pParaPortion->GetLines().Reset();
598 227570 : CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
599 227570 : return FinishCreateLines( pParaPortion );
600 : }
601 :
602 :
603 : // Initialization ......
604 :
605 :
606 : // Always format for 100%:
607 119976 : bool bMapChanged = ImpCheckRefMapMode();
608 :
609 119976 : if ( pParaPortion->GetLines().Count() == 0 )
610 : {
611 116999 : EditLine* pL = new EditLine;
612 116999 : pParaPortion->GetLines().Append(pL);
613 : }
614 :
615 :
616 : // Get Paragraph attributes ......
617 :
618 119976 : ContentNode* const pNode = pParaPortion->GetNode();
619 :
620 119976 : sal_Bool bRightToLeftPara = IsRightToLeft( nPara );
621 :
622 119976 : SvxAdjust eJustification = GetJustification( nPara );
623 119976 : sal_Bool bHyphenatePara = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HYPHENATE )).GetValue();
624 119976 : sal_Int32 nSpaceBefore = 0;
625 119976 : sal_Int32 nMinLabelWidth = 0;
626 119976 : sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pNode, &nSpaceBefore, &nMinLabelWidth );
627 119976 : const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pNode );
628 119976 : const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&) pNode->GetContentAttribs().GetItem( EE_PARA_SBL );
629 119976 : const sal_Bool bScriptSpace = ((const SvxScriptSpaceItem&) pNode->GetContentAttribs().GetItem( EE_PARA_ASIANCJKSPACING )).GetValue();
630 :
631 119976 : const short nInvalidDiff = pParaPortion->GetInvalidDiff();
632 119976 : const sal_Int32 nInvalidStart = pParaPortion->GetInvalidPosStart();
633 119976 : const sal_Int32 nInvalidEnd = nInvalidStart + std::abs( nInvalidDiff );
634 :
635 119976 : bool bQuickFormat = false;
636 119976 : if ( !bEmptyNodeWithPolygon && !HasScriptType( nPara, i18n::ScriptType::COMPLEX ) )
637 : {
638 120360 : if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff > 0 ) &&
639 384 : ( pNode->GetString().indexOf( CH_FEATURE, nInvalidStart ) > nInvalidEnd ) )
640 : {
641 0 : bQuickFormat = true;
642 : }
643 119976 : else if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
644 : {
645 : // check if delete over the portion boundaries was done ...
646 3 : sal_Int32 nStart = nInvalidStart; // DOUBLE !!!!!!!!!!!!!!!
647 3 : sal_Int32 nEnd = nStart - nInvalidDiff; // negative
648 3 : bQuickFormat = true;
649 3 : sal_Int32 nPos = 0;
650 3 : sal_Int32 nPortions = pParaPortion->GetTextPortions().Count();
651 6 : for ( sal_Int32 nTP = 0; nTP < nPortions; nTP++ )
652 : {
653 : // There must be no start / end in the deleted area.
654 3 : TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
655 3 : nPos = nPos + pTP->GetLen();
656 3 : if ( ( nPos > nStart ) && ( nPos < nEnd ) )
657 : {
658 0 : bQuickFormat = false;
659 0 : break;
660 : }
661 : }
662 : }
663 : }
664 :
665 : // SW disables TEXT_LAYOUT_COMPLEX_DISABLED, so maybe I have to enable it...
666 :
667 : // Saving both layout mode and language (since I'm potentially changing both)
668 119976 : GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
669 :
670 119976 : ImplInitLayoutMode( GetRefDevice(), nPara, -1 );
671 :
672 119976 : sal_Int32 nRealInvalidStart = nInvalidStart;
673 :
674 119976 : if ( bEmptyNodeWithPolygon )
675 : {
676 0 : TextPortion* pDummyPortion = new TextPortion( 0 );
677 0 : pParaPortion->GetTextPortions().Reset();
678 0 : pParaPortion->GetTextPortions().Append(pDummyPortion);
679 : }
680 119976 : else if ( bQuickFormat )
681 : {
682 : // faster Method:
683 3 : RecalcTextPortion( pParaPortion, nInvalidStart, nInvalidDiff );
684 : }
685 : else // nRealInvalidStart can be before InvalidStart, since Portions were deleted....
686 : {
687 119973 : CreateTextPortions( pParaPortion, nRealInvalidStart );
688 : }
689 :
690 :
691 :
692 : // Search for line with InvalidPos, start one line before
693 : // Flag the line => do not remove it !
694 :
695 :
696 119976 : sal_Int32 nLine = pParaPortion->GetLines().Count()-1;
697 238131 : for ( sal_Int32 nL = 0; nL <= nLine; nL++ )
698 : {
699 119976 : EditLine* pLine = pParaPortion->GetLines()[nL];
700 119976 : if ( pLine->GetEnd() > nRealInvalidStart ) // not nInvalidStart!
701 : {
702 1821 : nLine = nL;
703 1821 : break;
704 : }
705 118155 : pLine->SetValid();
706 : }
707 : // Begin one line before...
708 : // If it is typed at the end, the line in front cannot change.
709 119976 : if ( nLine && ( !pParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->Len() ) || ( nInvalidDiff <= 0 ) ) )
710 0 : nLine--;
711 :
712 119976 : EditLine* pLine = pParaPortion->GetLines()[nLine];
713 :
714 119976 : static Rectangle aZeroArea = Rectangle( Point(), Point() );
715 119976 : Rectangle aBulletArea( aZeroArea );
716 119976 : if ( !nLine )
717 : {
718 119976 : aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
719 119976 : if ( aBulletArea.Right() > 0 )
720 754 : pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
721 : else
722 119222 : pParaPortion->SetBulletX( 0 ); // if Bullet is set incorrectly
723 : }
724 :
725 :
726 : // Reformat all lines from here ...
727 :
728 119976 : sal_Int32 nDelFromLine = -1;
729 119976 : bool bLineBreak = false;
730 :
731 119976 : sal_Int32 nIndex = pLine->GetStart();
732 119976 : EditLine aSaveLine( *pLine );
733 239952 : SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
734 :
735 119976 : sal_Bool bCalcCharPositions = sal_True;
736 239952 : boost::scoped_array<sal_Int32> pBuf(new sal_Int32[ pNode->Len() ]);
737 :
738 119976 : sal_Bool bSameLineAgain = sal_False; // For TextRanger, if the height changes.
739 119976 : TabInfo aCurrentTab;
740 :
741 119976 : bool bForceOneRun = bEmptyNodeWithPolygon;
742 119976 : bool bCompressedChars = false;
743 :
744 414608 : while ( ( nIndex < pNode->Len() ) || bForceOneRun )
745 : {
746 174660 : bForceOneRun = false;
747 :
748 174660 : bool bEOL = false;
749 174660 : bool bEOC = false;
750 174660 : sal_Int32 nPortionStart = 0;
751 174660 : sal_Int32 nPortionEnd = 0;
752 :
753 174660 : long nStartX = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
754 174660 : if ( nIndex == 0 )
755 : {
756 119976 : long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
757 119976 : nStartX += nFI;
758 :
759 119976 : if ( !nLine && ( pParaPortion->GetBulletX() > nStartX ) )
760 : {
761 33 : nStartX = pParaPortion->GetBulletX();
762 : }
763 : }
764 :
765 : long nMaxLineWidth;
766 174660 : if ( !IsVertical() )
767 174637 : nMaxLineWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : aPaperSize.Width();
768 : else
769 23 : nMaxLineWidth = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : aPaperSize.Height();
770 :
771 174660 : nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
772 174660 : nMaxLineWidth -= nStartX;
773 :
774 : // If PaperSize == long_max, one cannot take away any negative
775 : // first line indent. (Overflow)
776 174660 : if ( ( nMaxLineWidth < 0 ) && ( nStartX < 0 ) )
777 0 : nMaxLineWidth = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) - GetXValue( rLRItem.GetRight() );
778 :
779 : // If still less than 0, it may be just the right edge.
780 174660 : if ( nMaxLineWidth <= 0 )
781 0 : nMaxLineWidth = 1;
782 :
783 : // Problem:
784 : // Since formatting starts a line _before_ the invalid position,
785 : // the positions unfortunately have to be redefined ...
786 : // Solution:
787 : // The line before can only become longer, not smaller
788 : // => ...
789 174660 : if ( bCalcCharPositions )
790 174660 : pLine->GetCharPosArray().clear();
791 :
792 174660 : sal_Int32 nTmpPos = nIndex;
793 174660 : sal_Int32 nTmpPortion = pLine->GetStartPortion();
794 174660 : long nTmpWidth = 0;
795 174660 : long nXWidth = nMaxLineWidth;
796 174660 : if ( nXWidth <= nTmpWidth ) // while has to be looped once
797 0 : nXWidth = nTmpWidth+1;
798 :
799 174660 : LongDqPtr pTextRanges = 0;
800 174660 : long nTextExtraYOffset = 0;
801 174660 : long nTextXOffset = 0;
802 174660 : long nTextLineHeight = 0;
803 174660 : if ( GetTextRanger() )
804 : {
805 0 : GetTextRanger()->SetVertical( IsVertical() );
806 :
807 0 : long nTextY = nStartPosY + GetEditCursor( pParaPortion, pLine->GetStart() ).Top();
808 0 : if ( !bSameLineAgain )
809 : {
810 0 : SeekCursor( pNode, nTmpPos+1, aTmpFont );
811 0 : aTmpFont.SetPhysFont( GetRefDevice() );
812 0 : ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
813 :
814 0 : if ( IsFixedCellHeight() )
815 0 : nTextLineHeight = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
816 : else
817 0 : nTextLineHeight = aTmpFont.GetPhysTxtSize( GetRefDevice(), OUString() ).Height();
818 : // Metrics can be greater
819 0 : FormatterFontMetric aTempFormatterMetrics;
820 0 : RecalcFormatterFontMetrics( aTempFormatterMetrics, aTmpFont );
821 0 : sal_uInt16 nLineHeight = aTempFormatterMetrics.GetHeight();
822 0 : if ( nLineHeight > nTextLineHeight )
823 0 : nTextLineHeight = nLineHeight;
824 : }
825 : else
826 0 : nTextLineHeight = pLine->GetHeight();
827 :
828 0 : nXWidth = 0;
829 0 : while ( !nXWidth )
830 : {
831 0 : long nYOff = nTextY + nTextExtraYOffset;
832 0 : long nYDiff = nTextLineHeight;
833 0 : if ( IsVertical() )
834 : {
835 0 : long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
836 0 : nYOff = nMaxPolygonX-nYOff;
837 0 : nYDiff = -nTextLineHeight;
838 : }
839 0 : pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
840 : DBG_ASSERT( pTextRanges, "GetTextRanges?!" );
841 0 : long nMaxRangeWidth = 0;
842 : // Use the widest range ...
843 : // The widest range could be a bit confusing, so normally it
844 : // is the first one. Best with gaps.
845 0 : if ( pTextRanges->size() )
846 : {
847 0 : long nA = pTextRanges->at(0);
848 0 : long nB = pTextRanges->at(0);
849 : DBG_ASSERT( nA <= nB, "TextRange distorted?" );
850 0 : long nW = nB - nA;
851 0 : if ( nW > nMaxRangeWidth )
852 : {
853 0 : nMaxRangeWidth = nW;
854 0 : nTextXOffset = nA;
855 : }
856 : }
857 0 : nXWidth = nMaxRangeWidth;
858 0 : if ( nXWidth )
859 0 : nMaxLineWidth = nXWidth - nStartX - GetXValue( rLRItem.GetRight() );
860 : else
861 : {
862 : // Try further down in the polygon.
863 : // Below the polygon use the Paper Width.
864 0 : nTextExtraYOffset += std::max( (long)(nTextLineHeight / 10), (long)1 );
865 0 : if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
866 : {
867 0 : nXWidth = !IsVertical() ? GetPaperSize().Width() : GetPaperSize().Height();
868 0 : if ( !nXWidth ) // AutoPaperSize
869 0 : nXWidth = 0x7FFFFFFF;
870 : }
871 : }
872 : }
873 : }
874 :
875 : // search for Portion that no longer fits in line ....
876 174660 : TextPortion* pPortion = 0;
877 174660 : bool bBrokenLine = false;
878 174660 : bLineBreak = false;
879 174660 : const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() );
880 527418 : while ( ( nTmpWidth < nXWidth ) && !bEOL && ( nTmpPortion < pParaPortion->GetTextPortions().Count() ) )
881 : {
882 178098 : nPortionStart = nTmpPos;
883 178098 : pPortion = pParaPortion->GetTextPortions()[nTmpPortion];
884 178098 : if ( pPortion->GetKind() == PORTIONKIND_HYPHENATOR )
885 : {
886 : // Throw away a Portion, if necessary correct the one before,
887 : // if the Hyph portion has swallowed a character ...
888 0 : sal_Int32 nTmpLen = pPortion->GetLen();
889 0 : pParaPortion->GetTextPortions().Remove( nTmpPortion );
890 0 : if (nTmpPortion && nTmpLen)
891 : {
892 0 : nTmpPortion--;
893 0 : TextPortion* pPrev = pParaPortion->GetTextPortions()[nTmpPortion];
894 : DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
895 0 : nTmpWidth -= pPrev->GetSize().Width();
896 0 : nTmpPos = nTmpPos - pPrev->GetLen();
897 0 : pPrev->SetLen(pPrev->GetLen() + nTmpLen);
898 0 : pPrev->GetSize().Width() = (-1);
899 : }
900 :
901 : DBG_ASSERT( nTmpPortion < pParaPortion->GetTextPortions().Count(), "No more Portions left!" );
902 0 : pPortion = pParaPortion->GetTextPortions()[nTmpPortion];
903 : }
904 : DBG_ASSERT( pPortion->GetKind() != PORTIONKIND_HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
905 : DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Empty Portion in CreateLines ?!" );
906 : (void)bProcessingEmptyLine;
907 178098 : if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
908 : {
909 4389 : sal_uInt16 nWhich = pNextFeature->GetItem()->Which();
910 4389 : switch ( nWhich )
911 : {
912 : case EE_FEATURE_TAB:
913 : {
914 9 : long nOldTmpWidth = nTmpWidth;
915 :
916 : // Search for Tab-Pos...
917 9 : long nCurPos = nTmpWidth+nStartX;
918 : // consider scaling
919 9 : if ( aStatus.DoStretch() && ( nStretchX != 100 ) )
920 0 : nCurPos = nCurPos*100/std::max(static_cast<sal_Int32>(nStretchX), static_cast<sal_Int32>(1));
921 :
922 9 : short nAllSpaceBeforeText = static_cast< short >(rLRItem.GetTxtLeft()/* + rLRItem.GetTxtLeft()*/ + nSpaceBeforeAndMinLabelWidth);
923 9 : aCurrentTab.aTabStop = pNode->GetContentAttribs().FindTabStop( nCurPos - nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/, aEditDoc.GetDefTab() );
924 9 : aCurrentTab.nTabPos = GetXValue( (long) ( aCurrentTab.aTabStop.GetTabPos() + nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/ ) );
925 9 : aCurrentTab.bValid = false;
926 :
927 : // Switch direction in R2L para...
928 9 : if ( bRightToLeftPara )
929 : {
930 0 : if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
931 0 : aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_LEFT;
932 0 : else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_LEFT )
933 0 : aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_RIGHT;
934 : }
935 :
936 27 : if ( ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT ) ||
937 18 : ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER ) ||
938 9 : ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL ) )
939 : {
940 : // For LEFT / DEFAULT this tab is not considered.
941 0 : aCurrentTab.bValid = true;
942 0 : aCurrentTab.nStartPosX = nTmpWidth;
943 0 : aCurrentTab.nCharPos = nTmpPos;
944 0 : aCurrentTab.nTabPortion = nTmpPortion;
945 : }
946 :
947 9 : pPortion->GetKind() = PORTIONKIND_TAB;
948 9 : pPortion->SetExtraValue( aCurrentTab.aTabStop.GetFill() );
949 9 : pPortion->GetSize().Width() = aCurrentTab.nTabPos - (nTmpWidth+nStartX);
950 :
951 : // Height needed...
952 9 : SeekCursor( pNode, nTmpPos+1, aTmpFont );
953 9 : pPortion->GetSize().Height() = aTmpFont.QuickGetTextSize( GetRefDevice(), OUString(), 0, 0, NULL ).Height();
954 :
955 : DBG_ASSERT( pPortion->GetSize().Width() >= 0, "Tab incorrectly calculated!" );
956 :
957 9 : nTmpWidth = aCurrentTab.nTabPos-nStartX;
958 :
959 : // If this is the first token on the line,
960 : // and nTmpWidth > aPaperSize.Width, => infinite loop!
961 9 : if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
962 : {
963 : // What now?
964 : // make the tab fitting
965 0 : pPortion->GetSize().Width() = nXWidth-nOldTmpWidth;
966 0 : nTmpWidth = nXWidth-1;
967 0 : bEOL = true;
968 0 : bBrokenLine = true;
969 : }
970 9 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
971 9 : size_t nPos = nTmpPos - pLine->GetStart();
972 9 : rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
973 9 : bCompressedChars = false;
974 : }
975 9 : break;
976 : case EE_FEATURE_LINEBR:
977 : {
978 : DBG_ASSERT( pPortion, "?!" );
979 47 : pPortion->GetSize().Width() = 0;
980 47 : bEOL = true;
981 47 : bLineBreak = true;
982 47 : pPortion->GetKind() = PORTIONKIND_LINEBREAK;
983 47 : bCompressedChars = false;
984 47 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
985 47 : size_t nPos = nTmpPos - pLine->GetStart();
986 47 : rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
987 : }
988 47 : break;
989 : case EE_FEATURE_FIELD:
990 : {
991 4333 : SeekCursor( pNode, nTmpPos+1, aTmpFont );
992 4333 : sal_Unicode cChar = 0; // later: NBS?
993 4333 : aTmpFont.SetPhysFont( GetRefDevice() );
994 4333 : ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
995 :
996 4333 : OUString aFieldValue = cChar ? OUString(cChar) : ((EditCharAttribField*)pNextFeature)->GetFieldValue();
997 4333 : if ( bCalcCharPositions || !pPortion->HasValidSize() )
998 : {
999 4333 : pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), aFieldValue, 0, aFieldValue.getLength(), 0 );
1000 : // So no scrolling for oversized fields
1001 4333 : if ( pPortion->GetSize().Width() > nXWidth )
1002 : {
1003 0 : sal_Int32 nWidthOrg = pPortion->GetSize().Width();
1004 0 : sal_Int32 nChars = aFieldValue.getLength();
1005 0 : sal_Int32 nApproxWC = nXWidth / ( nWidthOrg / nChars );
1006 0 : ExtraPortionInfo *pExtraInfo= pPortion->GetExtraInfos();
1007 0 : if( !nApproxWC ) nApproxWC++;
1008 0 : if( pExtraInfo == NULL )
1009 : {
1010 0 : pExtraInfo = new ExtraPortionInfo();
1011 0 : pExtraInfo->nOrgWidth = nXWidth;
1012 0 : pPortion->SetExtraInfos( pExtraInfo );
1013 : }
1014 : else
1015 : {
1016 0 : pExtraInfo->lineBreaksList.clear();
1017 : }
1018 :
1019 0 : pPortion->GetSize().Width() = nXWidth;
1020 :
1021 0 : while( nChars > 0 )
1022 : {
1023 0 : pExtraInfo->lineBreaksList.push_back( aFieldValue.getLength() - nChars );
1024 0 : nChars -= nApproxWC;
1025 : }
1026 : }
1027 : }
1028 4333 : nTmpWidth += pPortion->GetSize().Width();
1029 4333 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
1030 4333 : size_t nPos = nTmpPos - pLine->GetStart();
1031 4333 : rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
1032 4333 : pPortion->GetKind() = cChar ? PORTIONKIND_TEXT : PORTIONKIND_FIELD;
1033 : // If this is the first token on the line,
1034 : // and nTmpWidth > aPaperSize.Width, => infinite loop!
1035 4333 : if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
1036 : {
1037 0 : nTmpWidth = nXWidth-1;
1038 0 : bEOL = true;
1039 0 : bBrokenLine = true;
1040 : }
1041 : // Compression in Fields????
1042 : // I think this could be a little bit difficult and is not very useful
1043 4333 : bCompressedChars = false;
1044 : }
1045 4333 : break;
1046 : default: OSL_FAIL( "What feature?" );
1047 : }
1048 4389 : pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
1049 : }
1050 : else
1051 : {
1052 : DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
1053 : (void)bProcessingEmptyLine;
1054 173709 : SeekCursor( pNode, nTmpPos+1, aTmpFont );
1055 173709 : aTmpFont.SetPhysFont( GetRefDevice() );
1056 173709 : ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
1057 :
1058 173709 : if ( bCalcCharPositions || !pPortion->HasValidSize() )
1059 : {
1060 173709 : pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), pParaPortion->GetNode()->GetString(), nTmpPos, pPortion->GetLen(), pBuf.get() );
1061 :
1062 : // #i9050# Do Kerning also behind portions...
1063 173709 : if ( ( aTmpFont.GetFixKerning() > 0 ) && ( ( nTmpPos + pPortion->GetLen() ) < pNode->Len() ) )
1064 0 : pPortion->GetSize().Width() += aTmpFont.GetFixKerning();
1065 173709 : if ( IsFixedCellHeight() )
1066 3300 : pPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1067 : }
1068 173709 : if ( bCalcCharPositions )
1069 : {
1070 173709 : sal_Int32 nLen = pPortion->GetLen();
1071 : // The array is generally flattened at the beginning
1072 : // => Always simply quick inserts.
1073 173709 : size_t nPos = nTmpPos - pLine->GetStart();
1074 173709 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
1075 173709 : rArray.insert(rArray.begin()+nPos, pBuf.get(), pBuf.get()+nLen);
1076 : }
1077 :
1078 : // And now check for Compression:
1079 173709 : if ( pPortion->GetLen() && GetAsianCompressionMode() )
1080 : {
1081 0 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
1082 0 : sal_Int32* pDXArray = &rArray[0] + nTmpPos - pLine->GetStart();
1083 : bCompressedChars |= ImplCalcAsianCompression(
1084 0 : pNode, pPortion, nTmpPos, pDXArray, 10000, false);
1085 : }
1086 :
1087 173709 : nTmpWidth += pPortion->GetSize().Width();
1088 :
1089 173709 : pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
1090 :
1091 173709 : sal_Int32 _nPortionEnd = nTmpPos + pPortion->GetLen();
1092 173709 : if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
1093 : {
1094 254 : bool bAllow = false;
1095 254 : sal_uInt16 nScriptTypeLeft = GetI18NScriptType( EditPaM( pNode, _nPortionEnd ) );
1096 254 : sal_uInt16 nScriptTypeRight = GetI18NScriptType( EditPaM( pNode, _nPortionEnd+1 ) );
1097 254 : if ( ( nScriptTypeLeft == i18n::ScriptType::ASIAN ) || ( nScriptTypeRight == i18n::ScriptType::ASIAN ) )
1098 0 : bAllow = true;
1099 :
1100 : // No spacing within L2R/R2L nesting
1101 254 : if ( bAllow )
1102 : {
1103 0 : long nExtraSpace = pPortion->GetSize().Height()/5;
1104 0 : nExtraSpace = GetXValue( nExtraSpace );
1105 0 : pPortion->GetSize().Width() += nExtraSpace;
1106 0 : nTmpWidth += nExtraSpace;
1107 : }
1108 : }
1109 : }
1110 :
1111 178098 : if ( aCurrentTab.bValid && ( nTmpPortion != aCurrentTab.nTabPortion ) )
1112 : {
1113 0 : long nWidthAfterTab = 0;
1114 0 : for ( sal_Int32 n = aCurrentTab.nTabPortion+1; n <= nTmpPortion; n++ )
1115 : {
1116 0 : const TextPortion* pTP = pParaPortion->GetTextPortions()[n];
1117 0 : nWidthAfterTab += pTP->GetSize().Width();
1118 : }
1119 0 : long nW = nWidthAfterTab; // Length before tab position
1120 0 : if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
1121 : {
1122 : }
1123 0 : else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER )
1124 : {
1125 0 : nW = nWidthAfterTab/2;
1126 : }
1127 0 : else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL )
1128 : {
1129 : OUString aText = GetSelected( EditSelection( EditPaM( pParaPortion->GetNode(), nTmpPos ),
1130 0 : EditPaM( pParaPortion->GetNode(), nTmpPos + pPortion->GetLen() ) ) );
1131 0 : sal_Int32 nDecPos = aText.indexOf( aCurrentTab.aTabStop.GetDecimal() );
1132 0 : if ( nDecPos != -1 )
1133 : {
1134 0 : nW -= pParaPortion->GetTextPortions()[nTmpPortion]->GetSize().Width();
1135 0 : nW += aTmpFont.QuickGetTextSize( GetRefDevice(), pParaPortion->GetNode()->GetString(), nTmpPos, nDecPos, NULL ).Width();
1136 0 : aCurrentTab.bValid = false;
1137 0 : }
1138 : }
1139 : else
1140 : {
1141 : OSL_FAIL( "CreateLines: Tab not handled!" );
1142 : }
1143 0 : long nMaxW = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nStartX;
1144 0 : if ( nW >= nMaxW )
1145 : {
1146 0 : nW = nMaxW;
1147 0 : aCurrentTab.bValid = false;
1148 : }
1149 0 : TextPortion* const pTabPortion = pParaPortion->GetTextPortions()[aCurrentTab.nTabPortion];
1150 0 : pTabPortion->GetSize().Width() = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nW - nStartX;
1151 0 : nTmpWidth = aCurrentTab.nStartPosX + pTabPortion->GetSize().Width() + nWidthAfterTab;
1152 : }
1153 :
1154 178098 : nTmpPos = nTmpPos + pPortion->GetLen();
1155 178098 : nPortionEnd = nTmpPos;
1156 178098 : nTmpPortion++;
1157 178098 : if ( aStatus.OneCharPerLine() )
1158 0 : bEOL = true;
1159 : }
1160 :
1161 : DBG_ASSERT( pPortion, "no portion!?" );
1162 :
1163 174660 : aCurrentTab.bValid = false;
1164 :
1165 : // this was possibly a portion too far:
1166 174660 : bool bFixedEnd = false;
1167 174660 : if ( aStatus.OneCharPerLine() )
1168 : {
1169 : // State before Portion (apart from nTmpWidth):
1170 0 : nPortionEnd = nTmpPos;
1171 0 : nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1172 0 : nPortionStart = nTmpPos;
1173 0 : nTmpPortion--;
1174 :
1175 0 : bEOL = true;
1176 0 : bEOC = false;
1177 :
1178 : // And now just one character:
1179 0 : nTmpPos++;
1180 0 : nTmpPortion++;
1181 0 : nPortionEnd = nTmpPortion;
1182 : // one Non-Feature-Portion has to be wrapped
1183 0 : if ( pPortion && pPortion->GetLen() > 1 )
1184 : {
1185 : DBG_ASSERT( pPortion->GetKind() == PORTIONKIND_TEXT, "Len>1, but no TextPortion?" );
1186 0 : nTmpWidth -= pPortion->GetSize().Width();
1187 0 : sal_Int32 nP = SplitTextPortion( pParaPortion, nTmpPos, pLine );
1188 0 : const TextPortion* p = pParaPortion->GetTextPortions()[nP];
1189 : DBG_ASSERT( p, "Portion ?!" );
1190 0 : nTmpWidth += p->GetSize().Width();
1191 : }
1192 : }
1193 174660 : else if ( nTmpWidth >= nXWidth )
1194 : {
1195 68240 : nPortionEnd = nTmpPos;
1196 68240 : nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1197 68240 : nPortionStart = nTmpPos;
1198 68240 : nTmpPortion--;
1199 68240 : bEOL = false;
1200 68240 : bEOC = false;
1201 68240 : if( pPortion ) switch ( pPortion->GetKind() )
1202 : {
1203 : case PORTIONKIND_TEXT:
1204 : {
1205 68238 : nTmpWidth -= pPortion->GetSize().Width();
1206 : }
1207 68238 : break;
1208 : case PORTIONKIND_FIELD:
1209 : case PORTIONKIND_TAB:
1210 : {
1211 2 : nTmpWidth -= pPortion->GetSize().Width();
1212 2 : bEOL = true;
1213 2 : bFixedEnd = true;
1214 : }
1215 2 : break;
1216 : default:
1217 : {
1218 : // A feature is not wrapped:
1219 : DBG_ASSERT( ( pPortion->GetKind() == PORTIONKIND_LINEBREAK ), "What Feature ?" );
1220 0 : bEOL = true;
1221 0 : bFixedEnd = true;
1222 : }
1223 : }
1224 : }
1225 : else
1226 : {
1227 106420 : bEOL = true;
1228 106420 : bEOC = true;
1229 106420 : pLine->SetEnd( nPortionEnd );
1230 : DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "No TextPortions?" );
1231 106420 : pLine->SetEndPortion( pParaPortion->GetTextPortions().Count() - 1 );
1232 : }
1233 :
1234 174660 : if ( aStatus.OneCharPerLine() )
1235 : {
1236 0 : pLine->SetEnd( nPortionEnd );
1237 0 : pLine->SetEndPortion( nTmpPortion-1 );
1238 : }
1239 174660 : else if ( bFixedEnd )
1240 : {
1241 2 : pLine->SetEnd( nPortionStart );
1242 2 : pLine->SetEndPortion( nTmpPortion-1 );
1243 : }
1244 174658 : else if ( bLineBreak || bBrokenLine )
1245 : {
1246 47 : pLine->SetEnd( nPortionStart+1 );
1247 47 : pLine->SetEndPortion( nTmpPortion-1 );
1248 47 : bEOC = false; // was set above, maybe change the sequence of the if's?
1249 : }
1250 174611 : else if ( !bEOL )
1251 : {
1252 : DBG_ASSERT( pPortion && ((nPortionEnd-nPortionStart) == pPortion->GetLen()), "However, another portion?!" );
1253 68238 : long nRemainingWidth = nMaxLineWidth - nTmpWidth;
1254 68238 : bool bCanHyphenate = ( aTmpFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL );
1255 68238 : if ( bCompressedChars && pPortion && ( pPortion->GetLen() > 1 ) && pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
1256 : {
1257 : // I need the manipulated DXArray for determining the break postion...
1258 0 : sal_Int32* pDXArray = NULL;
1259 0 : if (!pLine->GetCharPosArray().empty())
1260 0 : pDXArray = &pLine->GetCharPosArray()[0] + (nPortionStart - pLine->GetStart());
1261 : ImplCalcAsianCompression(
1262 0 : pNode, pPortion, nPortionStart, pDXArray, 10000, true);
1263 : }
1264 68238 : if( pPortion )
1265 : ImpBreakLine( pParaPortion, pLine, pPortion, nPortionStart,
1266 68238 : nRemainingWidth, bCanHyphenate && bHyphenatePara );
1267 : }
1268 :
1269 :
1270 : // Line finished => adjust
1271 :
1272 :
1273 : // CalcTextSize should be replaced by a continuous registering!
1274 174660 : Size aTextSize = pLine->CalcTextSize( *pParaPortion );
1275 :
1276 174660 : if ( aTextSize.Height() == 0 )
1277 : {
1278 12 : SeekCursor( pNode, pLine->GetStart()+1, aTmpFont );
1279 12 : aTmpFont.SetPhysFont( pRefDev );
1280 12 : ImplInitDigitMode(pRefDev, aTmpFont.GetLanguage());
1281 :
1282 12 : if ( IsFixedCellHeight() )
1283 0 : aTextSize.Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1284 : else
1285 12 : aTextSize.Height() = aTmpFont.GetPhysTxtSize( pRefDev, OUString() ).Height();
1286 12 : pLine->SetHeight( (sal_uInt16)aTextSize.Height() );
1287 : }
1288 :
1289 : // The font metrics can not be calculated continuously, if the font is
1290 : // set anyway, because a large font only after wrapping suddenly ends
1291 : // up in the next line => Font metrics too big.
1292 174660 : FormatterFontMetric aFormatterMetrics;
1293 174660 : sal_Int32 nTPos = pLine->GetStart();
1294 352755 : for ( sal_Int32 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
1295 : {
1296 178095 : const TextPortion* pTP = pParaPortion->GetTextPortions()[nP];
1297 : // problem with hard font height attribute, when everthing but the line break has this attribute
1298 178095 : if ( pTP->GetKind() != PORTIONKIND_LINEBREAK )
1299 : {
1300 178048 : SeekCursor( pNode, nTPos+1, aTmpFont );
1301 178048 : aTmpFont.SetPhysFont( GetRefDevice() );
1302 178048 : ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
1303 178048 : RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1304 : }
1305 178095 : nTPos = nTPos + pTP->GetLen();
1306 : }
1307 174660 : sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1308 174660 : if ( nLineHeight > pLine->GetHeight() )
1309 12073 : pLine->SetHeight( nLineHeight );
1310 174660 : pLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1311 :
1312 174660 : bSameLineAgain = sal_False;
1313 174660 : if ( GetTextRanger() && ( pLine->GetHeight() > nTextLineHeight ) )
1314 : {
1315 : // put down with the other size!
1316 0 : bSameLineAgain = sal_True;
1317 : }
1318 :
1319 :
1320 174660 : if ( !bSameLineAgain && !aStatus.IsOutliner() )
1321 : {
1322 174660 : if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1323 : {
1324 1890 : sal_uInt16 nMinHeight = GetYValue( rLSItem.GetLineHeight() );
1325 1890 : sal_uInt16 nTxtHeight = pLine->GetHeight();
1326 1890 : if ( nTxtHeight < nMinHeight )
1327 : {
1328 : // The Ascent has to be adjusted for the difference:
1329 12 : long nDiff = nMinHeight - nTxtHeight;
1330 12 : pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff) );
1331 12 : pLine->SetHeight( nMinHeight, nTxtHeight );
1332 : }
1333 : }
1334 172770 : else if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1335 : {
1336 0 : sal_uInt16 nFixHeight = GetYValue( rLSItem.GetLineHeight() );
1337 0 : sal_uInt16 nTxtHeight = pLine->GetHeight();
1338 0 : pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
1339 0 : pLine->SetHeight( nFixHeight, nTxtHeight );
1340 : }
1341 172770 : else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1342 : {
1343 7897 : if ( nPara || IsFixedCellHeight() || pLine->GetStartPortion() ) // Not the very first line
1344 : {
1345 : // There are documents with PropLineSpace 0, why?
1346 : // (cmc: re above question :-) such documents can be seen by importing a .ppt
1347 4487 : if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1348 : {
1349 4170 : sal_uInt16 nTxtHeight = pLine->GetHeight();
1350 4170 : sal_Int32 nH = nTxtHeight;
1351 4170 : nH *= rLSItem.GetPropLineSpace();
1352 4170 : nH /= 100;
1353 : // The Ascent has to be adjusted for the difference:
1354 4170 : long nDiff = pLine->GetHeight() - nH;
1355 4170 : if ( nDiff > pLine->GetMaxAscent() )
1356 0 : nDiff = pLine->GetMaxAscent();
1357 4170 : pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() - nDiff) );
1358 4170 : pLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1359 : }
1360 : }
1361 : }
1362 : }
1363 :
1364 349322 : if ( ( !IsVertical() && aStatus.AutoPageWidth() ) ||
1365 161474 : ( IsVertical() && aStatus.AutoPageHeight() ) )
1366 : {
1367 : // If the row fits within the current paper width, then this width
1368 : // has to be used for the Alignment. If it does not fit or if it
1369 : // will change the paper width, it will be formatted again for
1370 : // Justification! = LEFT anyway.
1371 26422 : long nMaxLineWidthFix = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() )
1372 26422 : - GetXValue( rLRItem.GetRight() ) - nStartX;
1373 13211 : if ( aTextSize.Width() < nMaxLineWidthFix )
1374 5981 : nMaxLineWidth = nMaxLineWidthFix;
1375 : }
1376 :
1377 174660 : if ( bCompressedChars )
1378 : {
1379 0 : long nRemainingWidth = nMaxLineWidth - aTextSize.Width();
1380 0 : if ( nRemainingWidth > 0 )
1381 : {
1382 0 : ImplExpandCompressedPortions( pLine, pParaPortion, nRemainingWidth );
1383 0 : aTextSize = pLine->CalcTextSize( *pParaPortion );
1384 : }
1385 : }
1386 :
1387 174660 : if ( pLine->IsHangingPunctuation() )
1388 : {
1389 : // Width from HangingPunctuation was set to 0 in ImpBreakLine,
1390 : // check for rel width now, maybe create compression...
1391 0 : long n = nMaxLineWidth - aTextSize.Width();
1392 0 : TextPortion* const pTP = pParaPortion->GetTextPortions()[pLine->GetEndPortion()];
1393 0 : sal_Int32 nPosInArray = pLine->GetEnd()-1-pLine->GetStart();
1394 0 : long nNewValue = ( nPosInArray ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 ) + n;
1395 0 : pLine->GetCharPosArray()[ nPosInArray ] = nNewValue;
1396 0 : pTP->GetSize().Width() += n;
1397 : }
1398 :
1399 174660 : pLine->SetTextWidth( aTextSize.Width() );
1400 174660 : switch ( eJustification )
1401 : {
1402 : case SVX_ADJUST_CENTER:
1403 : {
1404 15729 : long n = ( nMaxLineWidth - aTextSize.Width() ) / 2;
1405 15729 : n += nStartX; // Indentation is kept.
1406 15729 : pLine->SetStartPosX( n );
1407 : }
1408 15729 : break;
1409 : case SVX_ADJUST_RIGHT:
1410 : {
1411 : // For automatically wrapped lines, which has a blank at the end
1412 : // the blank must not be displayed!
1413 1540 : long n = nMaxLineWidth - aTextSize.Width();
1414 1540 : n += nStartX; // Indentation is kept.
1415 1540 : pLine->SetStartPosX( n );
1416 : }
1417 1540 : break;
1418 : case SVX_ADJUST_BLOCK:
1419 : {
1420 1962 : bool bDistLastLine = (GetJustifyMethod(nPara) == SVX_JUSTIFY_METHOD_DISTRIBUTE);
1421 1962 : long nRemainingSpace = nMaxLineWidth - aTextSize.Width();
1422 1962 : pLine->SetStartPosX( nStartX );
1423 1962 : if ( nRemainingSpace > 0 && (!bEOC || bDistLastLine) )
1424 1479 : ImpAdjustBlocks( pParaPortion, pLine, nRemainingSpace );
1425 : }
1426 1962 : break;
1427 : default:
1428 : {
1429 155429 : pLine->SetStartPosX( nStartX ); // FI, LI
1430 : }
1431 155429 : break;
1432 : }
1433 :
1434 :
1435 : // Check whether the line must be re-issued ...
1436 :
1437 174660 : pLine->SetInvalid();
1438 :
1439 : // If a portion was wrapped there may be far too many positions in
1440 : // CharPosArray:
1441 174660 : if ( bCalcCharPositions )
1442 : {
1443 174660 : EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
1444 174660 : size_t nLen = pLine->GetLen();
1445 174660 : if (rArray.size() > nLen)
1446 54489 : rArray.erase(rArray.begin()+nLen, rArray.end());
1447 : }
1448 :
1449 174660 : if ( GetTextRanger() )
1450 : {
1451 0 : if ( nTextXOffset )
1452 0 : pLine->SetStartPosX( pLine->GetStartPosX() + nTextXOffset );
1453 0 : if ( nTextExtraYOffset )
1454 : {
1455 0 : pLine->SetHeight( (sal_uInt16) ( pLine->GetHeight() + nTextExtraYOffset ), 0, pLine->GetHeight() );
1456 0 : pLine->SetMaxAscent( (sal_uInt16) ( pLine->GetMaxAscent() + nTextExtraYOffset ) );
1457 : }
1458 : }
1459 :
1460 : // for <0 think over !
1461 174660 : if ( pParaPortion->IsSimpleInvalid() )
1462 : {
1463 : // Change through simple Text changes ...
1464 : // Do mot cancel formatting since Portions possibly have to be split
1465 : // again! If at some point cancelable, then validate the following
1466 : // line! But if applicable, mark as valid, so there is less output...
1467 404 : if ( pLine->GetEnd() < nInvalidStart )
1468 : {
1469 0 : if ( *pLine == aSaveLine )
1470 : {
1471 0 : pLine->SetValid();
1472 : }
1473 : }
1474 : else
1475 : {
1476 404 : sal_Int32 nStart = pLine->GetStart();
1477 404 : sal_Int32 nEnd = pLine->GetEnd();
1478 :
1479 404 : if ( nStart > nInvalidEnd )
1480 : {
1481 0 : if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
1482 0 : ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
1483 : {
1484 0 : pLine->SetValid();
1485 0 : if ( bCalcCharPositions && bQuickFormat )
1486 : {
1487 0 : bCalcCharPositions = sal_False;
1488 0 : bLineBreak = false;
1489 0 : pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1490 4 : break;
1491 : }
1492 : }
1493 : }
1494 404 : else if ( bCalcCharPositions && bQuickFormat && ( nEnd > nInvalidEnd) )
1495 : {
1496 : // If the invalid line ends so that the next begins on the
1497 : // 'same' passage as before, i.e. not wrapped differently,
1498 : // then the text width does not have to be determined anew:
1499 2 : if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
1500 : {
1501 2 : bCalcCharPositions = sal_False;
1502 2 : bLineBreak = false;
1503 2 : pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1504 2 : break;
1505 : }
1506 : }
1507 : }
1508 : }
1509 :
1510 174658 : if ( !bSameLineAgain )
1511 : {
1512 174658 : nIndex = pLine->GetEnd(); // next line start = last line end
1513 : // as nEnd points to the last character!
1514 :
1515 174658 : sal_Int32 nEndPortion = pLine->GetEndPortion();
1516 :
1517 : // Next line or maybe a new line....
1518 174658 : pLine = 0;
1519 174658 : if ( nLine < pParaPortion->GetLines().Count()-1 )
1520 52 : pLine = pParaPortion->GetLines()[++nLine];
1521 174658 : if ( pLine && ( nIndex >= pNode->Len() ) )
1522 : {
1523 2 : nDelFromLine = nLine;
1524 2 : break;
1525 : }
1526 174656 : if ( !pLine )
1527 : {
1528 174606 : if ( nIndex < pNode->Len() )
1529 : {
1530 54634 : pLine = new EditLine;
1531 54634 : pParaPortion->GetLines().Insert(++nLine, pLine);
1532 : }
1533 119972 : else if ( nIndex && bLineBreak && GetTextRanger() )
1534 : {
1535 : // normaly CreateAndInsertEmptyLine would be called, but I want to use
1536 : // CreateLines, so I need Polygon code only here...
1537 0 : TextPortion* pDummyPortion = new TextPortion( 0 );
1538 0 : pParaPortion->GetTextPortions().Append(pDummyPortion);
1539 0 : pLine = new EditLine;
1540 0 : pParaPortion->GetLines().Insert(++nLine, pLine);
1541 0 : bForceOneRun = true;
1542 0 : bProcessingEmptyLine = true;
1543 : }
1544 : }
1545 174656 : if ( pLine )
1546 : {
1547 54684 : aSaveLine = *pLine;
1548 54684 : pLine->SetStart( nIndex );
1549 54684 : pLine->SetEnd( nIndex );
1550 54684 : pLine->SetStartPortion( nEndPortion+1 );
1551 54684 : pLine->SetEndPortion( nEndPortion+1 );
1552 : }
1553 : }
1554 : } // while ( Index < Len )
1555 :
1556 119976 : if ( nDelFromLine >= 0 )
1557 2 : pParaPortion->GetLines().DeleteFromLine( nDelFromLine );
1558 :
1559 : DBG_ASSERT( pParaPortion->GetLines().Count(), "No line after CreateLines!" );
1560 :
1561 119976 : if ( bLineBreak )
1562 8 : CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
1563 :
1564 119976 : pBuf.reset();
1565 :
1566 119976 : sal_Bool bHeightChanged = FinishCreateLines( pParaPortion );
1567 :
1568 119976 : if ( bMapChanged )
1569 0 : GetRefDevice()->Pop();
1570 :
1571 119976 : GetRefDevice()->Pop();
1572 :
1573 239952 : return bHeightChanged;
1574 : }
1575 :
1576 227578 : void ImpEditEngine::CreateAndInsertEmptyLine( ParaPortion* pParaPortion, sal_uInt32 )
1577 : {
1578 : DBG_ASSERT( !GetTextRanger(), "Don't use CreateAndInsertEmptyLine with a polygon!" );
1579 :
1580 227578 : EditLine* pTmpLine = new EditLine;
1581 227578 : pTmpLine->SetStart( pParaPortion->GetNode()->Len() );
1582 227578 : pTmpLine->SetEnd( pParaPortion->GetNode()->Len() );
1583 227578 : pParaPortion->GetLines().Append(pTmpLine);
1584 :
1585 227578 : bool bLineBreak = pParaPortion->GetNode()->Len() > 0;
1586 227578 : sal_Int32 nSpaceBefore = 0;
1587 227578 : sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pParaPortion->GetNode(), &nSpaceBefore );
1588 227578 : const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pParaPortion->GetNode() );
1589 227578 : const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pParaPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
1590 227578 : short nStartX = GetXValue( (short)(rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBefore));
1591 :
1592 227578 : Rectangle aBulletArea = Rectangle( Point(), Point() );
1593 227578 : if ( bLineBreak )
1594 : {
1595 8 : nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1596 : }
1597 : else
1598 : {
1599 227570 : aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
1600 227570 : if ( aBulletArea.Right() > 0 )
1601 3 : pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
1602 : else
1603 227567 : pParaPortion->SetBulletX( 0 ); // If Bullet set incorrectly.
1604 227570 : if ( pParaPortion->GetBulletX() > nStartX )
1605 : {
1606 3 : nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1607 3 : if ( pParaPortion->GetBulletX() > nStartX )
1608 0 : nStartX = pParaPortion->GetBulletX();
1609 : }
1610 : }
1611 :
1612 227578 : SvxFont aTmpFont;
1613 227578 : SeekCursor( pParaPortion->GetNode(), bLineBreak ? pParaPortion->GetNode()->Len() : 0, aTmpFont );
1614 227578 : aTmpFont.SetPhysFont( pRefDev );
1615 :
1616 227578 : TextPortion* pDummyPortion = new TextPortion( 0 );
1617 227578 : pDummyPortion->GetSize() = aTmpFont.GetPhysTxtSize( pRefDev, OUString() );
1618 227578 : if ( IsFixedCellHeight() )
1619 7153 : pDummyPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1620 227578 : pParaPortion->GetTextPortions().Append(pDummyPortion);
1621 227578 : FormatterFontMetric aFormatterMetrics;
1622 227578 : RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1623 227578 : pTmpLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1624 227578 : pTmpLine->SetHeight( (sal_uInt16) pDummyPortion->GetSize().Height() );
1625 227578 : sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1626 227578 : if ( nLineHeight > pTmpLine->GetHeight() )
1627 6501 : pTmpLine->SetHeight( nLineHeight );
1628 :
1629 227578 : if ( !aStatus.IsOutliner() )
1630 : {
1631 227578 : sal_Int32 nPara = GetParaPortions().GetPos( pParaPortion );
1632 227578 : SvxAdjust eJustification = GetJustification( nPara );
1633 227578 : long nMaxLineWidth = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
1634 227578 : nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
1635 227578 : long nTextXOffset = 0;
1636 227578 : if ( nMaxLineWidth < 0 )
1637 0 : nMaxLineWidth = 1;
1638 227578 : if ( eJustification == SVX_ADJUST_CENTER )
1639 858 : nStartX = sal::static_int_cast< short >(nMaxLineWidth / 2);
1640 226720 : else if ( eJustification == SVX_ADJUST_RIGHT )
1641 196 : nStartX = sal::static_int_cast< short >(nMaxLineWidth);
1642 :
1643 227578 : nStartX = sal::static_int_cast< short >(nStartX + nTextXOffset);
1644 : }
1645 :
1646 227578 : pTmpLine->SetStartPosX( nStartX );
1647 :
1648 227578 : if ( !aStatus.IsOutliner() )
1649 : {
1650 227578 : if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1651 : {
1652 124 : sal_uInt16 nMinHeight = rLSItem.GetLineHeight();
1653 124 : sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1654 124 : if ( nTxtHeight < nMinHeight )
1655 : {
1656 : // The Ascent has to be adjusted for the difference:
1657 0 : long nDiff = nMinHeight - nTxtHeight;
1658 0 : pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff) );
1659 0 : pTmpLine->SetHeight( nMinHeight, nTxtHeight );
1660 : }
1661 : }
1662 227454 : else if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1663 : {
1664 0 : sal_uInt16 nFixHeight = rLSItem.GetLineHeight();
1665 0 : sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1666 :
1667 0 : pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
1668 0 : pTmpLine->SetHeight( nFixHeight, nTxtHeight );
1669 : }
1670 227454 : else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1671 : {
1672 6540 : sal_Int32 nPara = GetParaPortions().GetPos( pParaPortion );
1673 6540 : if ( nPara || IsFixedCellHeight() || pTmpLine->GetStartPortion() ) // Not the very first line
1674 : {
1675 : // There are documents with PropLineSpace 0, why?
1676 : // (cmc: re above question :-) such documents can be seen by importing a .ppt
1677 937 : if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1678 : {
1679 937 : sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1680 937 : sal_Int32 nH = nTxtHeight;
1681 937 : nH *= rLSItem.GetPropLineSpace();
1682 937 : nH /= 100;
1683 : // The Ascent has to be adjusted for the difference:
1684 937 : long nDiff = pTmpLine->GetHeight() - nH;
1685 937 : if ( nDiff > pTmpLine->GetMaxAscent() )
1686 0 : nDiff = pTmpLine->GetMaxAscent();
1687 937 : pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() - nDiff) );
1688 937 : pTmpLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1689 : }
1690 : }
1691 : }
1692 : }
1693 :
1694 227578 : if ( !bLineBreak )
1695 : {
1696 227570 : long nMinHeight = aBulletArea.GetHeight();
1697 227570 : if ( nMinHeight > (long)pTmpLine->GetHeight() )
1698 : {
1699 0 : long nDiff = nMinHeight - (long)pTmpLine->GetHeight();
1700 : // distribute nDiff upwards and downwards
1701 0 : pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff/2) );
1702 0 : pTmpLine->SetHeight( (sal_uInt16)nMinHeight );
1703 : }
1704 : }
1705 : else
1706 : {
1707 : // -2: The new one is already inserted.
1708 : #ifdef DBG_UTIL
1709 : EditLine* pLastLine = pParaPortion->GetLines()[pParaPortion->GetLines().Count()-2];
1710 : DBG_ASSERT( pLastLine, "soft wrap no line?!" );
1711 : DBG_ASSERT( pLastLine->GetEnd() == pParaPortion->GetNode()->Len(), "different anyway?" );
1712 : #endif
1713 8 : sal_Int32 nPos = pParaPortion->GetTextPortions().Count() - 1 ;
1714 8 : pTmpLine->SetStartPortion( nPos );
1715 8 : pTmpLine->SetEndPortion( nPos );
1716 227578 : }
1717 227578 : }
1718 :
1719 347546 : sal_Bool ImpEditEngine::FinishCreateLines( ParaPortion* pParaPortion )
1720 : {
1721 : // CalcCharPositions( pParaPortion );
1722 347546 : pParaPortion->SetValid();
1723 347546 : long nOldHeight = pParaPortion->GetHeight();
1724 347546 : CalcHeight( pParaPortion );
1725 :
1726 : DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "FinishCreateLines: No Text-Portion?" );
1727 347546 : sal_Bool bRet = ( pParaPortion->GetHeight() != nOldHeight );
1728 347546 : return bRet;
1729 : }
1730 :
1731 68238 : void ImpEditEngine::ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion* pPortion, sal_Int32 nPortionStart, long nRemainingWidth, sal_Bool bCanHyphenate )
1732 : {
1733 68238 : ContentNode* const pNode = pParaPortion->GetNode();
1734 :
1735 68238 : sal_Int32 nBreakInLine = nPortionStart - pLine->GetStart();
1736 68238 : sal_Int32 nMax = nBreakInLine + pPortion->GetLen();
1737 178151 : while ( ( nBreakInLine < nMax ) && ( pLine->GetCharPosArray()[nBreakInLine] < nRemainingWidth ) )
1738 41675 : nBreakInLine++;
1739 :
1740 68238 : sal_Int32 nMaxBreakPos = nBreakInLine + pLine->GetStart();
1741 68238 : sal_Int32 nBreakPos = SAL_MAX_INT32;
1742 :
1743 68238 : sal_Bool bCompressBlank = sal_False;
1744 68238 : sal_Bool bHyphenated = sal_False;
1745 68238 : sal_Bool bHangingPunctuation = sal_False;
1746 68238 : sal_Unicode cAlternateReplChar = 0;
1747 68238 : sal_Unicode cAlternateExtraChar = 0;
1748 68238 : sal_Bool bAltFullLeft = sal_False;
1749 68238 : sal_Bool bAltFullRight = sal_False;
1750 68238 : sal_uInt32 nAltDelChar = 0;
1751 :
1752 68238 : if ( ( nMaxBreakPos < ( nMax + pLine->GetStart() ) ) && ( pNode->GetChar( nMaxBreakPos ) == ' ' ) )
1753 : {
1754 : // Break behind the blank, blank will be compressed...
1755 8857 : nBreakPos = nMaxBreakPos + 1;
1756 8857 : bCompressBlank = sal_True;
1757 : }
1758 : else
1759 : {
1760 59381 : sal_Int32 nMinBreakPos = pLine->GetStart();
1761 59381 : const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
1762 183005 : for (size_t nAttr = rAttrs.size(); nAttr; )
1763 : {
1764 64246 : const EditCharAttrib& rAttr = rAttrs[--nAttr];
1765 64246 : if (rAttr.IsFeature() && rAttr.GetEnd() > nMinBreakPos && rAttr.GetEnd() <= nMaxBreakPos)
1766 : {
1767 3 : nMinBreakPos = rAttr.GetEnd();
1768 3 : break;
1769 : }
1770 : }
1771 :
1772 59381 : lang::Locale aLocale = GetLocale( EditPaM( pNode, nMaxBreakPos ) );
1773 :
1774 118762 : Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1775 118762 : Reference< XHyphenator > xHyph;
1776 59381 : if ( bCanHyphenate )
1777 55055 : xHyph = GetHyphenator();
1778 118762 : i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, Sequence< PropertyValue >(), 1 );
1779 118762 : i18n::LineBreakUserOptions aUserOptions;
1780 :
1781 59381 : const i18n::ForbiddenCharacters* pForbidden = GetForbiddenCharsTable()->GetForbiddenCharacters( LanguageTag::convertToLanguageType( aLocale ), true );
1782 59381 : aUserOptions.forbiddenBeginCharacters = pForbidden->beginLine;
1783 59381 : aUserOptions.forbiddenEndCharacters = pForbidden->endLine;
1784 59381 : aUserOptions.applyForbiddenRules = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_FORBIDDENRULES )).GetValue();
1785 59381 : aUserOptions.allowPunctuationOutsideMargin = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HANGINGPUNCTUATION )).GetValue();
1786 59381 : aUserOptions.allowHyphenateEnglish = sal_False;
1787 :
1788 59381 : i18n::LineBreakResults aLBR = _xBI->getLineBreak(
1789 118762 : pNode->GetString(), nMaxBreakPos, aLocale, nMinBreakPos, aHyphOptions, aUserOptions );
1790 59381 : nBreakPos = aLBR.breakIndex;
1791 :
1792 : // BUG in I18N - under special condition (break behind field, #87327#) breakIndex is < nMinBreakPos
1793 59381 : if ( nBreakPos < nMinBreakPos )
1794 : {
1795 35162 : nBreakPos = nMinBreakPos;
1796 : }
1797 24219 : else if ( ( nBreakPos > nMaxBreakPos ) && !aUserOptions.allowPunctuationOutsideMargin )
1798 : {
1799 : OSL_FAIL( "I18N: XBreakIterator::getLineBreak returns position > Max" );
1800 0 : nBreakPos = nMaxBreakPos;
1801 : }
1802 :
1803 : // nBreakPos can never be outside the portion, even not with hangig punctuation
1804 59381 : if ( nBreakPos > nMaxBreakPos )
1805 0 : nBreakPos = nMaxBreakPos;
1806 :
1807 : // BUG in I18N - the japanese dot is in the next line!
1808 : // !!! Test!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1809 59381 : if ( (nBreakPos + ( aUserOptions.allowPunctuationOutsideMargin ? 0 : 1 ) ) <= nMaxBreakPos )
1810 : {
1811 6440 : sal_Unicode cFirstInNextLine = ( (nBreakPos+1) < pNode->Len() ) ? pNode->GetChar( nBreakPos ) : 0;
1812 6440 : if ( cFirstInNextLine == 12290 )
1813 0 : nBreakPos++;
1814 : }
1815 :
1816 59381 : bHangingPunctuation = ( nBreakPos > nMaxBreakPos ) ? sal_True : sal_False;
1817 59381 : pLine->SetHangingPunctuation( bHangingPunctuation );
1818 :
1819 : // Whether a separator or not, push the word after the separator through
1820 : // hyphenation ... NMaxBreakPos is the last character that fits into
1821 : // the line, nBreakPos is the beginning of the word.
1822 : // There is a problem if the Doc is so narrow that a word is broken
1823 : // into more than two lines ...
1824 59381 : if ( !bHangingPunctuation && bCanHyphenate && GetHyphenator().is() )
1825 : {
1826 55055 : i18n::Boundary aBoundary = _xBI->getWordBoundary(
1827 55055 : pNode->GetString(), nBreakPos, GetLocale( EditPaM( pNode, nBreakPos ) ), ::com::sun::star::i18n::WordType::DICTIONARY_WORD, true);
1828 55055 : sal_Int32 nWordStart = nBreakPos;
1829 55055 : sal_Int32 nWordEnd = aBoundary.endPos;
1830 : DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
1831 :
1832 55055 : sal_Int32 nWordLen = nWordEnd - nWordStart;
1833 55055 : if ( ( nWordEnd >= nMaxBreakPos ) && ( nWordLen > 3 ) )
1834 : {
1835 : // May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
1836 13603 : OUString aWord = pNode->GetString().copy(nWordStart, nWordLen);
1837 13603 : sal_Int32 nMinTrail = nWordEnd-nMaxBreakPos+1; //+1: Before the dickey letter
1838 27206 : Reference< XHyphenatedWord > xHyphWord;
1839 13603 : if (xHyphenator.is())
1840 13603 : xHyphWord = xHyphenator->hyphenate( aWord, aLocale, aWord.getLength() - nMinTrail, Sequence< PropertyValue >() );
1841 13603 : if (xHyphWord.is())
1842 : {
1843 9 : sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
1844 9 : sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos();
1845 :
1846 9 : if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= (pLine->GetStart() + 2 ) ) )
1847 : {
1848 9 : if ( !bAlternate )
1849 : {
1850 9 : bHyphenated = sal_True;
1851 9 : nBreakPos = nWordStart + _nWordLen;
1852 : }
1853 : else
1854 : {
1855 : // TODO: handle all alternative hyphenations (see hyphen-1.2.8/tests/unicode.*)
1856 0 : OUString aAlt( xHyphWord->getHyphenatedWord() );
1857 0 : OUString aWord2(aWord);
1858 0 : OUString aAltLeft(aAlt.copy(0, _nWordLen));
1859 0 : OUString aAltRight(aAlt.copy(_nWordLen));
1860 0 : bAltFullLeft = aWord2.startsWith(aAltLeft);
1861 0 : bAltFullRight = aWord2.endsWith(aAltRight);
1862 0 : nAltDelChar = aWord2.getLength() - aAlt.getLength() + (1 - bAltFullLeft) + (1 - bAltFullRight);
1863 :
1864 : // NOTE: improved for other cases, see fdo#63711
1865 :
1866 : // We expect[ed] the two cases:
1867 : // 1) packen becomes pak-ken
1868 : // 2) Schiffahrt becomes Schiff-fahrt
1869 : // In case 1, a character has to be replaced
1870 : // in case 2 a character is added.
1871 : // The identification is complicated by long
1872 : // compound words because the Hyphenator separates
1873 : // all position of the word. [This is not true for libhyphen.]
1874 : // "Schiffahrtsbrennesseln" -> "Schifffahrtsbrennnesseln"
1875 : // We can thus actually not directly connect the index of the
1876 : // AlternativeWord to aWord. The whole issue will be simplified
1877 : // by a function in the Hyphenator as soon as AMA builds this in...
1878 0 : sal_Int32 nAltStart = _nWordLen - 1;
1879 0 : sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength());
1880 0 : sal_Int32 nTxtEnd = nTxtStart;
1881 0 : sal_Int32 nAltEnd = nAltStart;
1882 :
1883 : // The regions between the nStart and nEnd is the
1884 : // difference between alternative and original string.
1885 0 : while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() &&
1886 0 : aWord[nTxtEnd] != aAlt[nAltEnd] )
1887 : {
1888 0 : ++nTxtEnd;
1889 0 : ++nAltEnd;
1890 : }
1891 :
1892 : // If a character is added, then we notice it now:
1893 0 : if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
1894 0 : aWord[ nTxtEnd ] == aAlt[nAltEnd] )
1895 : {
1896 0 : ++nAltEnd;
1897 0 : ++nTxtStart;
1898 0 : ++nTxtEnd;
1899 : }
1900 :
1901 : DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Wrong assumption!" );
1902 :
1903 0 : if ( nTxtEnd > nTxtStart )
1904 0 : cAlternateReplChar = aAlt[nAltStart];
1905 : else
1906 0 : cAlternateExtraChar = aAlt[nAltStart];
1907 :
1908 0 : bHyphenated = sal_True;
1909 0 : nBreakPos = nWordStart + nTxtStart;
1910 0 : if ( cAlternateReplChar || aAlt.getLength() < aWord2.getLength() || !bAltFullRight) // also for "oma-tje", "re-eel"
1911 0 : nBreakPos++;
1912 : }
1913 : }
1914 13603 : }
1915 : }
1916 : }
1917 :
1918 59381 : if ( nBreakPos <= pLine->GetStart() )
1919 : {
1920 : // No separator in line => Chop!
1921 58364 : nBreakPos = nMaxBreakPos;
1922 : // I18N nextCharacters !
1923 58364 : if ( nBreakPos <= pLine->GetStart() )
1924 52523 : nBreakPos = pLine->GetStart() + 1; // Otherwise infinite loop!
1925 59381 : }
1926 : }
1927 :
1928 : // the dickey portion is the end portion
1929 68238 : pLine->SetEnd( nBreakPos );
1930 :
1931 68238 : sal_Int32 nEndPortion = SplitTextPortion( pParaPortion, nBreakPos, pLine );
1932 :
1933 68238 : if ( !bCompressBlank && !bHangingPunctuation )
1934 : {
1935 : // When justification is not SVX_ADJUST_LEFT, it's important to compress
1936 : // the trailing space even if there is enough room for the space...
1937 : // Don't check for SVX_ADJUST_LEFT, doesn't matter to compress in this case too...
1938 : DBG_ASSERT( nBreakPos > pLine->GetStart(), "ImpBreakLines - BreakPos not expected!" );
1939 59381 : if ( pNode->GetChar( nBreakPos-1 ) == ' ' )
1940 1003 : bCompressBlank = sal_True;
1941 : }
1942 :
1943 68238 : if ( bCompressBlank || bHangingPunctuation )
1944 : {
1945 9860 : TextPortion* const pTP = pParaPortion->GetTextPortions()[nEndPortion];
1946 : DBG_ASSERT( pTP->GetKind() == PORTIONKIND_TEXT, "BlankRubber: No TextPortion!" );
1947 : DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion at the beginning of the line?" );
1948 9860 : sal_Int32 nPosInArray = nBreakPos - 1 - pLine->GetStart();
1949 9860 : pTP->GetSize().Width() = ( nPosInArray && ( pTP->GetLen() > 1 ) ) ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0;
1950 9860 : pLine->GetCharPosArray()[ nPosInArray ] = pTP->GetSize().Width();
1951 : }
1952 58378 : else if ( bHyphenated )
1953 : {
1954 : // A portion for inserting the separator ...
1955 9 : TextPortion* pHyphPortion = new TextPortion( 0 );
1956 9 : pHyphPortion->GetKind() = PORTIONKIND_HYPHENATOR;
1957 9 : OUString aHyphText(CH_HYPH);
1958 9 : if ( (cAlternateReplChar || cAlternateExtraChar) && bAltFullRight ) // alternation after the break doesn't supported
1959 : {
1960 0 : TextPortion* pPrev = pParaPortion->GetTextPortions()[nEndPortion];
1961 : DBG_ASSERT( pPrev && pPrev->GetLen(), "Hyphenate: Prev portion?!" );
1962 0 : pPrev->SetLen( pPrev->GetLen() - nAltDelChar );
1963 0 : pHyphPortion->SetLen( nAltDelChar );
1964 0 : if (cAlternateReplChar && !bAltFullLeft) pHyphPortion->SetExtraValue( cAlternateReplChar );
1965 : // Correct width of the portion above:
1966 0 : pPrev->GetSize().Width() =
1967 0 : pLine->GetCharPosArray()[ nBreakPos-1 - pLine->GetStart() - nAltDelChar ];
1968 : }
1969 :
1970 : // Determine the width of the Hyph-Portion:
1971 18 : SvxFont aFont;
1972 9 : SeekCursor( pParaPortion->GetNode(), nBreakPos, aFont );
1973 9 : aFont.SetPhysFont( GetRefDevice() );
1974 9 : pHyphPortion->GetSize().Height() = GetRefDevice()->GetTextHeight();
1975 9 : pHyphPortion->GetSize().Width() = GetRefDevice()->GetTextWidth( aHyphText );
1976 :
1977 18 : pParaPortion->GetTextPortions().Insert(++nEndPortion, pHyphPortion);
1978 : }
1979 68238 : pLine->SetEndPortion( nEndPortion );
1980 68238 : }
1981 :
1982 1479 : void ImpEditEngine::ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, long nRemainingSpace )
1983 : {
1984 : DBG_ASSERT( nRemainingSpace > 0, "AdjustBlocks: Somewhat too little..." );
1985 : DBG_ASSERT( pLine, "AdjustBlocks: Line ?!" );
1986 1479 : if ( ( nRemainingSpace < 0 ) || pLine->IsEmpty() )
1987 1152 : return ;
1988 :
1989 1479 : const sal_Int32 nFirstChar = pLine->GetStart();
1990 1479 : const sal_Int32 nLastChar = pLine->GetEnd() -1; // Last points behind
1991 1479 : ContentNode* pNode = pParaPortion->GetNode();
1992 :
1993 : DBG_ASSERT( nLastChar < pNode->Len(), "AdjustBlocks: Out of range!" );
1994 :
1995 : // Search blanks or Kashidas...
1996 1479 : std::vector<sal_Int32> aPositions;
1997 1479 : sal_uInt16 nLastScript = i18n::ScriptType::LATIN;
1998 6840 : for ( sal_Int32 nChar = nFirstChar; nChar <= nLastChar; nChar++ )
1999 : {
2000 5361 : EditPaM aPaM( pNode, nChar+1 );
2001 5361 : LanguageType eLang = GetLanguage(aPaM);
2002 5361 : sal_uInt16 nScript = GetI18NScriptType(aPaM);
2003 5361 : if ( MsLangId::getPrimaryLanguage( eLang) == LANGUAGE_ARABIC_PRIMARY_ONLY )
2004 : // Arabic script is handled later.
2005 0 : continue;
2006 :
2007 5361 : if ( pNode->GetChar(nChar) == ' ' )
2008 : {
2009 : // Normal latin script.
2010 714 : aPositions.push_back( nChar );
2011 : }
2012 4647 : else if (nChar > nFirstChar)
2013 : {
2014 3169 : if (nLastScript == i18n::ScriptType::ASIAN)
2015 : {
2016 : // Set break position between this and the last character if
2017 : // the last character is asian script.
2018 0 : aPositions.push_back( nChar-1 );
2019 : }
2020 3169 : else if (nScript == i18n::ScriptType::ASIAN)
2021 : {
2022 : // Set break position between a latin script and asian script.
2023 0 : aPositions.push_back( nChar-1 );
2024 : }
2025 : }
2026 :
2027 5361 : nLastScript = nScript;
2028 : }
2029 :
2030 : // Kashidas ?
2031 1479 : ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
2032 :
2033 1479 : if ( aPositions.empty() )
2034 1152 : return;
2035 :
2036 : // If the last character is a blank, it is rejected!
2037 : // The width must be distributed to the blockers in front...
2038 : // But not if it is the only one.
2039 1019 : if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.size() > 1 ) &&
2040 441 : ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ) ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) )
2041 : {
2042 38 : aPositions.pop_back();
2043 : sal_Int32 nPortionStart, nPortion;
2044 38 : nPortion = pParaPortion->GetTextPortions().FindPortion( nLastChar+1, nPortionStart );
2045 38 : TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2046 38 : long nRealWidth = pLine->GetCharPosArray()[nLastChar-nFirstChar];
2047 38 : long nBlankWidth = nRealWidth;
2048 38 : if ( nLastChar > nPortionStart )
2049 38 : nBlankWidth -= pLine->GetCharPosArray()[nLastChar-nFirstChar-1];
2050 : // Possibly the blank has already been deducted in ImpBreakLine:
2051 38 : if ( nRealWidth == pLastPortion->GetSize().Width() )
2052 : {
2053 : // For the last character the portion must stop behind the blank
2054 : // => Simplify correction:
2055 : DBG_ASSERT( ( nPortionStart + pLastPortion->GetLen() ) == ( nLastChar+1 ), "Blank actually not at the end of the portion!?");
2056 38 : pLastPortion->GetSize().Width() -= nBlankWidth;
2057 38 : nRemainingSpace += nBlankWidth;
2058 : }
2059 38 : pLine->GetCharPosArray()[nLastChar-nFirstChar] -= nBlankWidth;
2060 : }
2061 :
2062 327 : size_t nGaps = aPositions.size();
2063 327 : const long nMore4Everyone = nRemainingSpace / nGaps;
2064 327 : long nSomeExtraSpace = nRemainingSpace - nMore4Everyone*nGaps;
2065 :
2066 : DBG_ASSERT( nSomeExtraSpace < (long)nGaps, "AdjustBlocks: ExtraSpace too large" );
2067 : DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
2068 :
2069 : // Correct the positions in the Array and the portion widths:
2070 : // Last character won't be considered ...
2071 1003 : for ( std::vector<sal_Int32>::const_iterator it(aPositions.begin()); it != aPositions.end(); ++it )
2072 : {
2073 676 : sal_Int32 nChar = *it;
2074 676 : if ( nChar < nLastChar )
2075 : {
2076 : sal_Int32 nPortionStart, nPortion;
2077 387 : nPortion = pParaPortion->GetTextPortions().FindPortion( nChar, nPortionStart, true );
2078 387 : TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2079 :
2080 : // The width of the portion:
2081 387 : pLastPortion->GetSize().Width() += nMore4Everyone;
2082 387 : if ( nSomeExtraSpace )
2083 170 : pLastPortion->GetSize().Width()++;
2084 :
2085 : // Correct positions in array
2086 : // Even for kashidas just change positions, VCL will then draw the kashida automaticly
2087 387 : sal_Int32 nPortionEnd = nPortionStart + pLastPortion->GetLen();
2088 13167 : for ( sal_Int32 _n = nChar; _n < nPortionEnd; _n++ )
2089 : {
2090 12780 : pLine->GetCharPosArray()[_n-nFirstChar] += nMore4Everyone;
2091 12780 : if ( nSomeExtraSpace )
2092 7792 : pLine->GetCharPosArray()[_n-nFirstChar]++;
2093 : }
2094 :
2095 387 : if ( nSomeExtraSpace )
2096 170 : nSomeExtraSpace--;
2097 : }
2098 : }
2099 :
2100 : // Now the text width contains the extra width...
2101 327 : pLine->SetTextWidth( pLine->GetTextWidth() + nRemainingSpace );
2102 : }
2103 :
2104 1479 : void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, std::vector<sal_Int32>& rArray )
2105 : {
2106 : // the search has to be performed on a per word base
2107 :
2108 1479 : EditSelection aWordSel( EditPaM( pNode, nStart ) );
2109 1479 : aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2110 1479 : if ( aWordSel.Min().GetIndex() < nStart )
2111 1152 : aWordSel.Min().SetIndex( nStart );
2112 :
2113 4814 : while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) )
2114 : {
2115 1856 : const sal_Int32 nSavPos = aWordSel.Max().GetIndex();
2116 1856 : if ( aWordSel.Max().GetIndex() > nEnd )
2117 1152 : aWordSel.Max().SetIndex( nEnd );
2118 :
2119 1856 : OUString aWord = GetSelected( aWordSel );
2120 :
2121 : // restore selection for proper iteration at the end of the function
2122 1856 : aWordSel.Max().SetIndex( nSavPos );
2123 :
2124 1856 : sal_Int32 nIdx = 0;
2125 1856 : sal_Int32 nKashidaPos = -1;
2126 : sal_Unicode cCh;
2127 1856 : sal_Unicode cPrevCh = 0;
2128 :
2129 7177 : while ( nIdx < aWord.getLength() )
2130 : {
2131 3465 : cCh = aWord[ nIdx ];
2132 :
2133 : // 1. Priority:
2134 : // after user inserted kashida
2135 3465 : if ( 0x640 == cCh )
2136 : {
2137 0 : nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2138 0 : break;
2139 : }
2140 :
2141 : // 2. Priority:
2142 : // after a Seen or Sad
2143 3465 : if ( nIdx + 1 < aWord.getLength() &&
2144 1609 : ( 0x633 == cCh || 0x635 == cCh ) )
2145 : {
2146 0 : nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2147 0 : break;
2148 : }
2149 :
2150 : // 3. Priority:
2151 : // before final form of the Marbuta, Hah, Dal
2152 : // 4. Priority:
2153 : // before final form of Alef, Lam or Kaf
2154 3465 : if ( nIdx && nIdx + 1 == aWord.getLength() &&
2155 413 : ( 0x629 == cCh || 0x62D == cCh || 0x62F == cCh ||
2156 413 : 0x627 == cCh || 0x644 == cCh || 0x643 == cCh ) )
2157 : {
2158 : DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2159 :
2160 : // check if character is connectable to previous character,
2161 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2162 : {
2163 0 : nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2164 0 : break;
2165 : }
2166 : }
2167 :
2168 : // 5. Priority:
2169 : // before media Bah
2170 3465 : if ( nIdx && nIdx + 1 < aWord.getLength() && 0x628 == cCh )
2171 : {
2172 : DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2173 :
2174 : // check if next character is Reh, Yeh or Alef Maksura
2175 0 : sal_Unicode cNextCh = aWord[ nIdx + 1 ];
2176 :
2177 0 : if ( 0x631 == cNextCh || 0x64A == cNextCh ||
2178 : 0x649 == cNextCh )
2179 : {
2180 : // check if character is connectable to previous character,
2181 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2182 0 : nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2183 : }
2184 : }
2185 :
2186 : // 6. Priority:
2187 : // other connecting possibilities
2188 5074 : if ( nIdx && nIdx + 1 == aWord.getLength() &&
2189 3465 : 0x60C <= cCh && 0x6FE >= cCh )
2190 : {
2191 : DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2192 :
2193 : // check if character is connectable to previous character,
2194 0 : if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2195 : {
2196 : // only choose this position if we did not find
2197 : // a better one:
2198 0 : if ( nKashidaPos<0 )
2199 0 : nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2200 0 : break;
2201 : }
2202 : }
2203 :
2204 : // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
2205 : // Damma, Kasra, Shadda and Sukun when checking if
2206 : // a character can be connected to previous character.
2207 3465 : if ( cCh < 0x64B || cCh > 0x652 )
2208 3465 : cPrevCh = cCh;
2209 :
2210 3465 : ++nIdx;
2211 : } // end of current word
2212 :
2213 1856 : if ( nKashidaPos>=0 )
2214 0 : rArray.push_back( nKashidaPos );
2215 :
2216 1856 : aWordSel = WordRight( aWordSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2217 1856 : aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2218 1856 : }
2219 1479 : }
2220 :
2221 68238 : sal_Int32 ImpEditEngine::SplitTextPortion( ParaPortion* pPortion, sal_Int32 nPos, EditLine* pCurLine )
2222 : {
2223 : DBG_ASSERT( pPortion, "SplitTextPortion: Which ?" );
2224 :
2225 : // The portion at nPos is split, if there is not a transition at nPos anyway
2226 68238 : if ( nPos == 0 )
2227 0 : return 0;
2228 :
2229 : sal_Int32 nSplitPortion;
2230 68238 : sal_Int32 nTmpPos = 0;
2231 68238 : TextPortion* pTextPortion = NULL;
2232 68238 : sal_Int32 nPortions = pPortion->GetTextPortions().Count();
2233 301138 : for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
2234 : {
2235 301138 : TextPortion* pTP = pPortion->GetTextPortions()[nSplitPortion];
2236 301138 : nTmpPos = nTmpPos + pTP->GetLen();
2237 301138 : if ( nTmpPos >= nPos )
2238 : {
2239 68238 : if ( nTmpPos == nPos ) // then nothing needs to be split
2240 : {
2241 13758 : return nSplitPortion;
2242 : }
2243 54480 : pTextPortion = pTP;
2244 54480 : break;
2245 : }
2246 : }
2247 :
2248 : DBG_ASSERT( pTextPortion, "Position outside the area!" );
2249 :
2250 54480 : if (!pTextPortion)
2251 0 : return 0;
2252 :
2253 : DBG_ASSERT( pTextPortion->GetKind() == PORTIONKIND_TEXT, "SplitTextPortion: No TextPortion!" );
2254 :
2255 54480 : sal_Int32 nOverlapp = nTmpPos - nPos;
2256 54480 : pTextPortion->SetLen( pTextPortion->GetLen() - nOverlapp );
2257 54480 : TextPortion* pNewPortion = new TextPortion( nOverlapp );
2258 54480 : pPortion->GetTextPortions().Insert(nSplitPortion+1, pNewPortion);
2259 : // Set sizes
2260 54480 : if ( pCurLine )
2261 : {
2262 : // No new GetTextSize, instead use values from the Array:
2263 : DBG_ASSERT( nPos > pCurLine->GetStart(), "SplitTextPortion at the beginning of the line?" );
2264 54480 : pTextPortion->GetSize().Width() = pCurLine->GetCharPosArray()[ nPos-pCurLine->GetStart()-1 ];
2265 :
2266 54480 : if ( pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed )
2267 : {
2268 : // We need the original size from the portion
2269 0 : sal_Int32 nTxtPortionStart = pPortion->GetTextPortions().GetStartPos( nSplitPortion );
2270 0 : SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
2271 0 : SeekCursor( pPortion->GetNode(), nTxtPortionStart+1, aTmpFont );
2272 0 : aTmpFont.SetPhysFont( GetRefDevice() );
2273 0 : GetRefDevice()->Push( PUSH_TEXTLANGUAGE );
2274 0 : ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
2275 0 : Size aSz = aTmpFont.QuickGetTextSize( GetRefDevice(), pPortion->GetNode()->GetString(), nTxtPortionStart, pTextPortion->GetLen(), NULL );
2276 0 : GetRefDevice()->Pop();
2277 0 : pTextPortion->GetExtraInfos()->nOrgWidth = aSz.Width();
2278 : }
2279 : }
2280 : else
2281 0 : pTextPortion->GetSize().Width() = (-1);
2282 :
2283 54480 : return nSplitPortion;
2284 : }
2285 :
2286 119973 : void ImpEditEngine::CreateTextPortions( ParaPortion* pParaPortion, sal_Int32& rStart )
2287 : {
2288 119973 : sal_Int32 nStartPos = rStart;
2289 119973 : ContentNode* pNode = pParaPortion->GetNode();
2290 : DBG_ASSERT( pNode->Len(), "CreateTextPortions should not be used for empty paragraphs!" );
2291 :
2292 119973 : ::std::set< sal_Int32 > aPositions;
2293 119973 : aPositions.insert( 0 );
2294 :
2295 119973 : sal_uInt16 nAttr = 0;
2296 119973 : EditCharAttrib* pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2297 340730 : while ( pAttrib )
2298 : {
2299 : // Insert Start and End into the Array...
2300 : // The Insert method does not allow for duplicate values....
2301 100784 : aPositions.insert( pAttrib->GetStart() );
2302 100784 : aPositions.insert( pAttrib->GetEnd() );
2303 100784 : nAttr++;
2304 100784 : pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2305 : }
2306 119973 : aPositions.insert( pNode->Len() );
2307 :
2308 119973 : if ( pParaPortion->aScriptInfos.empty() )
2309 0 : ((ImpEditEngine*)this)->InitScriptTypes( GetParaPortions().GetPos( pParaPortion ) );
2310 :
2311 119973 : const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
2312 240757 : for ( size_t nT = 0; nT < rTypes.size(); nT++ )
2313 120784 : aPositions.insert( rTypes[nT].nStartPos );
2314 :
2315 119973 : const WritingDirectionInfos& rWritingDirections = pParaPortion->aWritingDirectionInfos;
2316 239946 : for ( size_t nD = 0; nD < rWritingDirections.size(); nD++ )
2317 119973 : aPositions.insert( rWritingDirections[nD].nStartPos );
2318 :
2319 119973 : if ( mpIMEInfos && mpIMEInfos->nLen && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) )
2320 : {
2321 0 : sal_uInt16 nLastAttr = 0xFFFF;
2322 0 : for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
2323 : {
2324 0 : if ( mpIMEInfos->pAttribs[n] != nLastAttr )
2325 : {
2326 0 : aPositions.insert( mpIMEInfos->aPos.GetIndex() + n );
2327 0 : nLastAttr = mpIMEInfos->pAttribs[n];
2328 : }
2329 : }
2330 0 : aPositions.insert( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen );
2331 : }
2332 :
2333 : // From ... Delete:
2334 : // Unfortunately, the number of text portions does not have to match
2335 : // aPositions.Count(), since there might be line breaks...
2336 119973 : sal_Int32 nPortionStart = 0;
2337 119973 : sal_Int32 nInvPortion = 0;
2338 : sal_Int32 nP;
2339 119973 : for ( nP = 0; nP < (sal_Int32)pParaPortion->GetTextPortions().Count(); nP++ )
2340 : {
2341 2974 : const TextPortion* pTmpPortion = pParaPortion->GetTextPortions()[nP];
2342 2974 : nPortionStart = nPortionStart + pTmpPortion->GetLen();
2343 2974 : if ( nPortionStart >= nStartPos )
2344 : {
2345 2974 : nPortionStart = nPortionStart - pTmpPortion->GetLen();
2346 2974 : rStart = nPortionStart;
2347 2974 : nInvPortion = nP;
2348 2974 : break;
2349 : }
2350 : }
2351 : DBG_ASSERT( nP < pParaPortion->GetTextPortions().Count() || !pParaPortion->GetTextPortions().Count(), "Nothing to delete: CreateTextPortions" );
2352 119973 : if ( nInvPortion && ( nPortionStart+pParaPortion->GetTextPortions()[nInvPortion]->GetLen() > nStartPos ) )
2353 : {
2354 : // prefer one in front ...
2355 : // But only if it was in the middle of the portion of, otherwise it
2356 : // might be the only one in the row in front!
2357 0 : nInvPortion--;
2358 0 : nPortionStart = nPortionStart - pParaPortion->GetTextPortions()[nInvPortion]->GetLen();
2359 : }
2360 119973 : pParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
2361 :
2362 : // A portion may also have been formed by a line break:
2363 119973 : aPositions.insert( nPortionStart );
2364 :
2365 119973 : ::std::set< sal_Int32 >::iterator nInvPos = aPositions.find( nPortionStart );
2366 : DBG_ASSERT( (nInvPos != aPositions.end()), "InvPos ?!" );
2367 :
2368 119973 : ::std::set< sal_Int32 >::iterator i = nInvPos;
2369 119973 : ++i;
2370 363549 : while ( i != aPositions.end() )
2371 : {
2372 123603 : TextPortion* pNew = new TextPortion( (*i++) - *nInvPos++ );
2373 123603 : pParaPortion->GetTextPortions().Append(pNew);
2374 : }
2375 :
2376 119973 : DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "No Portions?!" );
2377 : #if OSL_DEBUG_LEVEL > 2
2378 : OSL_ENSURE( pParaPortion->DbgCheckTextPortions(), "Portion is broken?" );
2379 : #endif
2380 119973 : }
2381 :
2382 3 : void ImpEditEngine::RecalcTextPortion( ParaPortion* pParaPortion, sal_Int32 nStartPos, sal_Int32 nNewChars )
2383 : {
2384 : DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "No Portions!" );
2385 : DBG_ASSERT( nNewChars, "RecalcTextPortion with Diff == 0" );
2386 :
2387 3 : ContentNode* const pNode = pParaPortion->GetNode();
2388 3 : if ( nNewChars > 0 )
2389 : {
2390 : // If an Attribute begins/ends at nStartPos, then a new portion starts
2391 : // otherwise the portion is extended at nStartPos.
2392 0 : if ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) || IsScriptChange( EditPaM( pNode, nStartPos ) ) )
2393 : {
2394 0 : sal_Int32 nNewPortionPos = 0;
2395 0 : if ( nStartPos )
2396 0 : nNewPortionPos = SplitTextPortion( pParaPortion, nStartPos ) + 1;
2397 :
2398 : // A blank portion may be here, if the paragraph was empty,
2399 : // or if a line was created by a hard line break.
2400 0 : if ( ( nNewPortionPos < (sal_Int32)pParaPortion->GetTextPortions().Count() ) &&
2401 0 : !pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
2402 : {
2403 0 : TextPortion* const pTP = pParaPortion->GetTextPortions()[nNewPortionPos];
2404 : DBG_ASSERT( pTP->GetKind() == PORTIONKIND_TEXT, "the empty portion was no TextPortion!" );
2405 0 : pTP->SetLen( pTP->GetLen() + nNewChars );
2406 : }
2407 : else
2408 : {
2409 0 : TextPortion* pNewPortion = new TextPortion( nNewChars );
2410 0 : pParaPortion->GetTextPortions().Insert(nNewPortionPos, pNewPortion);
2411 : }
2412 : }
2413 : else
2414 : {
2415 : sal_Int32 nPortionStart;
2416 0 : const sal_Int32 nTP = pParaPortion->GetTextPortions().
2417 0 : FindPortion( nStartPos, nPortionStart );
2418 0 : TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
2419 : DBG_ASSERT( pTP, "RecalcTextPortion: Portion not found" );
2420 0 : pTP->SetLen( pTP->GetLen() + nNewChars );
2421 0 : pTP->GetSize().Width() = (-1);
2422 : }
2423 : }
2424 : else
2425 : {
2426 : // Shrink or remove portion if necessary.
2427 : // Before calling this method it must be ensured that no portions were
2428 : // in the deleted area!
2429 :
2430 : // There must be no portions extending into the area or portions starting in
2431 : // the area, so it must be:
2432 : // nStartPos <= nPos <= nStartPos - nNewChars(neg.)
2433 3 : sal_Int32 nPortion = 0;
2434 3 : sal_Int32 nPos = 0;
2435 3 : sal_Int32 nEnd = nStartPos-nNewChars;
2436 3 : sal_Int32 nPortions = pParaPortion->GetTextPortions().Count();
2437 3 : TextPortion* pTP = 0;
2438 3 : for ( nPortion = 0; nPortion < nPortions; nPortion++ )
2439 : {
2440 3 : pTP = pParaPortion->GetTextPortions()[ nPortion ];
2441 3 : if ( ( nPos+pTP->GetLen() ) > nStartPos )
2442 : {
2443 : DBG_ASSERT( nPos <= nStartPos, "Wrong Start!" );
2444 : DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "Wrong End!" );
2445 3 : break;
2446 : }
2447 0 : nPos = nPos + pTP->GetLen();
2448 : }
2449 : DBG_ASSERT( pTP, "RecalcTextPortion: Portion not found" );
2450 3 : if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
2451 : {
2452 : // Remove portion;
2453 0 : sal_uInt8 nType = pTP->GetKind();
2454 0 : pParaPortion->GetTextPortions().Remove( nPortion );
2455 0 : if ( nType == PORTIONKIND_LINEBREAK )
2456 : {
2457 0 : TextPortion* pNext = pParaPortion->GetTextPortions()[ nPortion ];
2458 0 : if ( pNext && !pNext->GetLen() )
2459 : {
2460 : // Remove dummy portion
2461 0 : pParaPortion->GetTextPortions().Remove( nPortion );
2462 : }
2463 : }
2464 : }
2465 : else
2466 : {
2467 : DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion too small to shrink! ");
2468 3 : pTP->SetLen( pTP->GetLen() + nNewChars );
2469 : }
2470 :
2471 3 : sal_Int32 nPortionCount = pParaPortion->GetTextPortions().Count();
2472 : assert( nPortionCount );
2473 3 : if (nPortionCount)
2474 : {
2475 : // No HYPHENATOR portion is allowed to get stuck right at the end...
2476 3 : sal_Int32 nLastPortion = nPortionCount - 1;
2477 3 : pTP = pParaPortion->GetTextPortions()[nLastPortion];
2478 3 : if ( pTP->GetKind() == PORTIONKIND_HYPHENATOR )
2479 : {
2480 : // Discard portion; if possible, correct the ones before,
2481 : // if the Hyphenator portion has swallowed one character...
2482 0 : if ( nLastPortion && pTP->GetLen() )
2483 : {
2484 0 : TextPortion* pPrev = pParaPortion->GetTextPortions()[nLastPortion - 1];
2485 : DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
2486 0 : pPrev->SetLen( pPrev->GetLen() + pTP->GetLen() );
2487 0 : pPrev->GetSize().Width() = (-1);
2488 : }
2489 0 : pParaPortion->GetTextPortions().Remove( nLastPortion );
2490 : }
2491 : }
2492 : }
2493 : #if OSL_DEBUG_LEVEL > 2
2494 : OSL_ENSURE( pParaPortion->DbgCheckTextPortions(), "Portions are broken?" );
2495 : #endif
2496 3 : }
2497 :
2498 333504 : void ImpEditEngine::SetTextRanger( TextRanger* pRanger )
2499 : {
2500 333504 : if ( pTextRanger != pRanger )
2501 : {
2502 0 : delete pTextRanger;
2503 0 : pTextRanger = pRanger;
2504 :
2505 0 : for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
2506 : {
2507 0 : ParaPortion* pParaPortion = GetParaPortions()[nPara];
2508 0 : pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
2509 0 : pParaPortion->GetLines().Reset();
2510 : }
2511 :
2512 0 : FormatFullDoc();
2513 0 : UpdateViews( GetActiveView() );
2514 0 : if ( GetUpdateMode() && GetActiveView() )
2515 0 : pActiveView->ShowCursor(false, false);
2516 : }
2517 333504 : }
2518 :
2519 172188 : void ImpEditEngine::SetVertical( sal_Bool bVertical )
2520 : {
2521 172188 : if ( IsVertical() != bVertical )
2522 : {
2523 4 : GetEditDoc().SetVertical( bVertical );
2524 4 : sal_Bool bUseCharAttribs = ( aStatus.GetControlWord() & EE_CNTRL_USECHARATTRIBS ) ? sal_True : sal_False;
2525 4 : GetEditDoc().CreateDefFont( bUseCharAttribs );
2526 4 : if ( IsFormatted() )
2527 : {
2528 0 : FormatFullDoc();
2529 0 : UpdateViews( GetActiveView() );
2530 : }
2531 : }
2532 172188 : }
2533 :
2534 105982 : void ImpEditEngine::SetFixedCellHeight( sal_Bool bUseFixedCellHeight )
2535 : {
2536 105982 : if ( IsFixedCellHeight() != bUseFixedCellHeight )
2537 : {
2538 20 : GetEditDoc().SetFixedCellHeight( bUseFixedCellHeight );
2539 20 : if ( IsFormatted() )
2540 : {
2541 17 : FormatFullDoc();
2542 17 : UpdateViews( GetActiveView() );
2543 : }
2544 : }
2545 105982 : }
2546 :
2547 601392 : void ImpEditEngine::SeekCursor( ContentNode* pNode, sal_Int32 nPos, SvxFont& rFont, OutputDevice* pOut, sal_uInt16 nIgnoreWhich )
2548 : {
2549 : // It was planned, SeekCursor( nStartPos, nEndPos, ... ), so that it would
2550 : // only be searched anew at the StartPosition.
2551 : // Problem: There would be two lists to consider/handle:
2552 : // OrderedByStart,OrderedByEnd.
2553 :
2554 601392 : if ( nPos > pNode->Len() )
2555 2774 : nPos = pNode->Len();
2556 :
2557 601392 : rFont = pNode->GetCharAttribs().GetDefFont();
2558 :
2559 601392 : short nScriptType = GetI18NScriptType( EditPaM( pNode, nPos ) );
2560 601392 : if ( ( nScriptType == i18n::ScriptType::ASIAN ) || ( nScriptType == i18n::ScriptType::COMPLEX ) )
2561 : {
2562 0 : const SvxFontItem& rFontItem = (const SvxFontItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) );
2563 0 : rFont.SetName( rFontItem.GetFamilyName() );
2564 0 : rFont.SetFamily( rFontItem.GetFamily() );
2565 0 : rFont.SetPitch( rFontItem.GetPitch() );
2566 0 : rFont.SetCharSet( rFontItem.GetCharSet() );
2567 0 : Size aSz( rFont.GetSize() );
2568 0 : aSz.Height() = ((const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) ).GetHeight();
2569 0 : rFont.SetSize( aSz );
2570 0 : rFont.SetWeight( ((const SvxWeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ))).GetWeight() );
2571 0 : rFont.SetItalic( ((const SvxPostureItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ))).GetPosture() );
2572 0 : rFont.SetLanguage( ((const SvxLanguageItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ))).GetLanguage() );
2573 : }
2574 :
2575 601392 : sal_uInt16 nRelWidth = ((const SvxCharScaleWidthItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH)).GetValue();
2576 :
2577 601392 : if ( pOut )
2578 : {
2579 15299 : const SvxUnderlineItem& rTextLineColor = (const SvxUnderlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_UNDERLINE );
2580 15299 : if ( rTextLineColor.GetColor() != COL_TRANSPARENT )
2581 1333 : pOut->SetTextLineColor( rTextLineColor.GetColor() );
2582 : else
2583 13966 : pOut->SetTextLineColor();
2584 : }
2585 :
2586 601392 : if ( pOut )
2587 : {
2588 15299 : const SvxOverlineItem& rOverlineColor = (const SvxOverlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_OVERLINE );
2589 15299 : if ( rOverlineColor.GetColor() != COL_TRANSPARENT )
2590 1240 : pOut->SetOverlineColor( rOverlineColor.GetColor() );
2591 : else
2592 14059 : pOut->SetOverlineColor();
2593 : }
2594 :
2595 601392 : const SvxLanguageItem* pCJKLanguageItem = NULL;
2596 :
2597 601392 : if ( aStatus.UseCharAttribs() )
2598 : {
2599 601392 : CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs();
2600 601392 : size_t nAttr = 0;
2601 601392 : EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
2602 1639484 : while ( pAttrib && ( pAttrib->GetStart() <= nPos ) )
2603 : {
2604 : // when seeking, ignore attributes which start there! Empty attributes
2605 : // are considered (used) as these are just set. But do not use empty
2606 : // attributes: When just set and empty => no effect on font
2607 : // In a blank paragraph, set characters take effect immediately.
2608 1300793 : if ( ( pAttrib->Which() != nIgnoreWhich ) &&
2609 839629 : ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
2610 41960 : || ( !pNode->Len() ) ) )
2611 : {
2612 : DBG_ASSERT( ( pAttrib->Which() >= EE_CHAR_START ) && ( pAttrib->Which() <= EE_FEATURE_END ), "Invalid Attribute in Seek() " );
2613 427393 : if ( IsScriptItemValid( pAttrib->Which(), nScriptType ) )
2614 : {
2615 280294 : pAttrib->SetFont( rFont, pOut );
2616 : // #i1550# hard color attrib should win over text color from field
2617 280294 : if ( pAttrib->Which() == EE_FEATURE_FIELD )
2618 : {
2619 9499 : EditCharAttrib* pColorAttr = pNode->GetCharAttribs().FindAttrib( EE_CHAR_COLOR, nPos );
2620 9499 : if ( pColorAttr )
2621 189 : pColorAttr->SetFont( rFont, pOut );
2622 : }
2623 : }
2624 427393 : if ( pAttrib->Which() == EE_CHAR_FONTWIDTH )
2625 8278 : nRelWidth = ((const SvxCharScaleWidthItem*)pAttrib->GetItem())->GetValue();
2626 427393 : if ( pAttrib->Which() == EE_CHAR_LANGUAGE_CJK )
2627 13238 : pCJKLanguageItem = (const SvxLanguageItem*) pAttrib->GetItem();
2628 : }
2629 436700 : pAttrib = GetAttrib( rAttribs, ++nAttr );
2630 : }
2631 : }
2632 :
2633 601392 : if ( !pCJKLanguageItem )
2634 588154 : pCJKLanguageItem = (const SvxLanguageItem*) &pNode->GetContentAttribs().GetItem( EE_CHAR_LANGUAGE_CJK );
2635 :
2636 601392 : rFont.SetCJKContextLanguage( pCJKLanguageItem->GetLanguage() );
2637 :
2638 601392 : if ( rFont.GetKerning() && IsKernAsianPunctuation() && ( nScriptType == i18n::ScriptType::ASIAN ) )
2639 0 : rFont.SetKerning( rFont.GetKerning() | KERNING_ASIAN );
2640 :
2641 601392 : if ( aStatus.DoNotUseColors() )
2642 : {
2643 0 : rFont.SetColor( /* rColorItem.GetValue() */ COL_BLACK );
2644 : }
2645 :
2646 601392 : if ( aStatus.DoStretch() || ( nRelWidth != 100 ) )
2647 : {
2648 : // For the current Output device, because otherwise if RefDev=Printer its looks
2649 : // ugly on the screen!
2650 11280 : OutputDevice* pDev = pOut ? pOut : GetRefDevice();
2651 11280 : rFont.SetPhysFont( pDev );
2652 11280 : FontMetric aMetric( pDev->GetFontMetric() );
2653 :
2654 : // Set the font as we want it to look like & reset the Propr attribute
2655 : // so that it is not counted twice.
2656 11280 : Size aRealSz( aMetric.GetSize() );
2657 11280 : rFont.SetPropr( 100 );
2658 :
2659 11280 : if ( aStatus.DoStretch() )
2660 : {
2661 660 : if ( nStretchY != 100 )
2662 : {
2663 0 : aRealSz.Height() *= nStretchY;
2664 0 : aRealSz.Height() /= 100;
2665 : }
2666 660 : if ( nStretchX != 100 )
2667 : {
2668 0 : if ( nStretchX == nStretchY &&
2669 : nRelWidth == 100 )
2670 : {
2671 0 : aRealSz.Width() = 0;
2672 : }
2673 : else
2674 : {
2675 0 : aRealSz.Width() *= nStretchX;
2676 0 : aRealSz.Width() /= 100;
2677 :
2678 : // Also the Kerning: (long due to handle Interim results)
2679 0 : long nKerning = rFont.GetFixKerning();
2680 : /*
2681 : The consideration was: If negative kerning, but StretchX = 200
2682 : => Do not double the kerning, thus pull the letters closer together
2683 : ---------------------------
2684 : Kern StretchX =>Kern
2685 : ---------------------------
2686 : >0 <100 < (Proportional)
2687 : <0 <100 < (Proportional)
2688 : >0 >100 > (Proportional)
2689 : <0 >100 < (The amount, thus disproportional)
2690 : */
2691 0 : if ( ( nKerning < 0 ) && ( nStretchX > 100 ) )
2692 : {
2693 : // disproportional
2694 0 : nKerning *= 100;
2695 0 : nKerning /= nStretchX;
2696 : }
2697 0 : else if ( nKerning )
2698 : {
2699 : // Proportional
2700 0 : nKerning *= nStretchX;
2701 0 : nKerning /= 100;
2702 : }
2703 0 : rFont.SetFixKerning( (short)nKerning );
2704 : }
2705 : }
2706 : }
2707 11280 : if ( nRelWidth != 100 )
2708 : {
2709 10620 : aRealSz.Width() *= nRelWidth;
2710 10620 : aRealSz.Width() /= 100;
2711 : }
2712 11280 : rFont.SetSize( aRealSz );
2713 : // Font is not restored ...
2714 : }
2715 :
2716 601392 : if ( ( ( rFont.GetColor() == COL_AUTO ) || ( IsForceAutoColor() ) ) && pOut )
2717 : {
2718 : // #i75566# Do not use AutoColor when printing OR Pdf export
2719 9563 : const bool bPrinting(OUTDEV_PRINTER == pOut->GetOutDevType());
2720 9563 : const bool bPDFExporting(0 != pOut->GetPDFWriter());
2721 :
2722 9563 : if ( IsAutoColorEnabled() && !bPrinting && !bPDFExporting)
2723 : {
2724 : // Never use WindowTextColor on the printer
2725 9563 : rFont.SetColor( GetAutoColor() );
2726 : }
2727 : else
2728 : {
2729 0 : if ( ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() )
2730 0 : rFont.SetColor( COL_WHITE );
2731 : else
2732 0 : rFont.SetColor( COL_BLACK );
2733 : }
2734 : }
2735 :
2736 601392 : if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) &&
2737 601392 : ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
2738 : {
2739 0 : sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
2740 0 : if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
2741 0 : rFont.SetUnderline( UNDERLINE_SINGLE );
2742 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
2743 0 : rFont.SetUnderline( UNDERLINE_BOLD );
2744 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
2745 0 : rFont.SetUnderline( UNDERLINE_DOTTED );
2746 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
2747 0 : rFont.SetUnderline( UNDERLINE_DOTTED );
2748 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
2749 0 : rFont.SetColor( Color( COL_RED ) );
2750 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
2751 0 : rFont.SetColor( Color( COL_LIGHTGRAY ) );
2752 0 : if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
2753 : {
2754 0 : const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2755 0 : rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
2756 0 : rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
2757 0 : rFont.SetTransparent( false );
2758 : }
2759 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
2760 : {
2761 0 : rFont.SetUnderline( UNDERLINE_WAVE );
2762 0 : if( pOut )
2763 0 : pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
2764 : }
2765 : }
2766 601392 : }
2767 :
2768 405626 : void ImpEditEngine::RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont )
2769 : {
2770 : // for line height at high / low first without Propr!
2771 405626 : sal_uInt16 nPropr = rFont.GetPropr();
2772 : DBG_ASSERT( ( nPropr == 100 ) || rFont.GetEscapement(), "Propr without Escape?!" );
2773 405626 : if ( nPropr != 100 )
2774 : {
2775 137 : rFont.SetPropr( 100 );
2776 137 : rFont.SetPhysFont( pRefDev );
2777 : }
2778 : sal_uInt16 nAscent, nDescent;
2779 :
2780 405626 : FontMetric aMetric( pRefDev->GetFontMetric() );
2781 405626 : nAscent = (sal_uInt16)aMetric.GetAscent();
2782 405626 : if ( IsAddExtLeading() )
2783 : nAscent = sal::static_int_cast< sal_uInt16 >(
2784 13365 : nAscent + aMetric.GetExtLeading() );
2785 405626 : nDescent = (sal_uInt16)aMetric.GetDescent();
2786 :
2787 405626 : if ( IsFixedCellHeight() )
2788 : {
2789 10466 : nAscent = sal::static_int_cast< sal_uInt16 >( rFont.GetHeight() );
2790 10466 : nDescent= sal::static_int_cast< sal_uInt16 >( ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() ) - nAscent );
2791 : }
2792 : else
2793 : {
2794 395160 : sal_uInt16 nIntLeading = ( aMetric.GetIntLeading() > 0 ) ? (sal_uInt16)aMetric.GetIntLeading() : 0;
2795 : // Fonts without leading cause problems
2796 395160 : if ( ( nIntLeading == 0 ) && ( pRefDev->GetOutDevType() == OUTDEV_PRINTER ) )
2797 : {
2798 : // Lets see what Leading one gets on the screen
2799 0 : VirtualDevice* pVDev = GetVirtualDevice( pRefDev->GetMapMode(), pRefDev->GetDrawMode() );
2800 0 : rFont.SetPhysFont( pVDev );
2801 0 : aMetric = pVDev->GetFontMetric();
2802 :
2803 : // This is so that the Leading does not count itself out again,
2804 : // if the whole line has the font, nTmpLeading.
2805 0 : nAscent = (sal_uInt16)aMetric.GetAscent();
2806 0 : nDescent = (sal_uInt16)aMetric.GetDescent();
2807 : }
2808 : }
2809 405626 : if ( nAscent > rCurMetrics.nMaxAscent )
2810 402273 : rCurMetrics.nMaxAscent = nAscent;
2811 405626 : if ( nDescent > rCurMetrics.nMaxDescent )
2812 402247 : rCurMetrics.nMaxDescent= nDescent;
2813 : // Special treatment of high/low:
2814 405626 : if ( rFont.GetEscapement() )
2815 : {
2816 : // Now in consideration of Escape/Propr
2817 : // possibly enlarge Ascent or Descent
2818 4995 : short nDiff = (short)(rFont.GetSize().Height()*rFont.GetEscapement()/100L);
2819 4995 : if ( rFont.GetEscapement() > 0 )
2820 : {
2821 4859 : nAscent = (sal_uInt16) (((long)nAscent)*nPropr/100 + nDiff);
2822 4859 : if ( nAscent > rCurMetrics.nMaxAscent )
2823 4858 : rCurMetrics.nMaxAscent = nAscent;
2824 : }
2825 : else // has to be < 0
2826 : {
2827 136 : nDescent = (sal_uInt16) (((long)nDescent)*nPropr/100 - nDiff);
2828 136 : if ( nDescent > rCurMetrics.nMaxDescent )
2829 133 : rCurMetrics.nMaxDescent= nDescent;
2830 : }
2831 405626 : }
2832 405626 : }
2833 :
2834 11025 : void ImpEditEngine::Paint( OutputDevice* pOutDev, Rectangle aClipRect, Point aStartPos, sal_Bool bStripOnly, short nOrientation )
2835 : {
2836 11025 : if ( !GetUpdateMode() && !bStripOnly )
2837 0 : return;
2838 :
2839 11025 : if ( !IsFormatted() )
2840 0 : FormatDoc();
2841 :
2842 11025 : long nFirstVisXPos = - pOutDev->GetMapMode().GetOrigin().X();
2843 11025 : long nFirstVisYPos = - pOutDev->GetMapMode().GetOrigin().Y();
2844 :
2845 11025 : const EditLine* pLine = NULL;
2846 11025 : Point aTmpPos;
2847 11025 : Point aRedLineTmpPos;
2848 : DBG_ASSERT( GetParaPortions().Count(), "No ParaPortion?!" );
2849 11025 : SvxFont aTmpFont( GetParaPortions()[0]->GetNode()->GetCharAttribs().GetDefFont() );
2850 22050 : Font aOldFont( pOutDev->GetFont() );
2851 11025 : vcl::PDFExtOutDevData* pPDFExtOutDevData = PTR_CAST( vcl::PDFExtOutDevData, pOutDev->GetExtOutDevData() );
2852 :
2853 : // In the case of rotated text is aStartPos considered TopLeft because
2854 : // other information is missing, and since the whole object is shown anyway
2855 : // un-scrolled.
2856 : // The rectangle is infinite.
2857 11025 : Point aOrigin( aStartPos );
2858 11025 : double nCos = 0.0, nSin = 0.0;
2859 11025 : if ( nOrientation )
2860 : {
2861 321 : double nRealOrientation = nOrientation*F_PI1800;
2862 321 : nCos = cos( nRealOrientation );
2863 321 : nSin = sin( nRealOrientation );
2864 : }
2865 :
2866 : // #110496# Added some more optional metafile comments. This
2867 : // change: factored out some duplicated code.
2868 11025 : GDIMetaFile* pMtf = pOutDev->GetConnectMetaFile();
2869 11025 : const bool bMetafileValid( pMtf != NULL );
2870 :
2871 11025 : long nVertLineSpacing = CalcVertLineSpacing(aStartPos);
2872 :
2873 :
2874 : // Over all the paragraphs ...
2875 :
2876 22786 : for ( sal_Int32 n = 0; n < GetParaPortions().Count(); n++ )
2877 : {
2878 12669 : const ParaPortion* pPortion = GetParaPortions()[n];
2879 : DBG_ASSERT( pPortion, "NULL-Pointer in TokenList in Paint" );
2880 : // if when typing idle formatting, asynchronous Paint.
2881 : // Invisible Portions may be invalid.
2882 12669 : if ( pPortion->IsVisible() && pPortion->IsInvalid() )
2883 0 : return;
2884 :
2885 12669 : if ( pPDFExtOutDevData )
2886 0 : pPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
2887 :
2888 12669 : long nParaHeight = pPortion->GetHeight();
2889 12669 : sal_Int32 nIndex = 0;
2890 38007 : if ( pPortion->IsVisible() && (
2891 25338 : ( !IsVertical() && ( ( aStartPos.Y() + nParaHeight ) > aClipRect.Top() ) ) ||
2892 4 : ( IsVertical() && ( ( aStartPos.X() - nParaHeight ) < aClipRect.Right() ) ) ) )
2893 :
2894 : {
2895 :
2896 : // Over the lines of the paragraph ...
2897 :
2898 12669 : sal_Int32 nLines = pPortion->GetLines().Count();
2899 12669 : sal_Int32 nLastLine = nLines-1;
2900 :
2901 : // #108052#
2902 12669 : bool bEndOfParagraphWritten(false);
2903 :
2904 12669 : if ( !IsVertical() )
2905 12667 : aStartPos.Y() += pPortion->GetFirstLineOffset();
2906 : else
2907 2 : aStartPos.X() -= pPortion->GetFirstLineOffset();
2908 :
2909 12669 : Point aParaStart( aStartPos );
2910 :
2911 12669 : const SvxLineSpacingItem& rLSItem = ((const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ));
2912 12669 : sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
2913 12669 : ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
2914 12669 : bool bPaintBullet (false);
2915 :
2916 26992 : for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ )
2917 : {
2918 15231 : pLine = pPortion->GetLines()[nLine];
2919 15231 : nIndex = pLine->GetStart();
2920 : DBG_ASSERT( pLine, "NULL-Pointer in the line iterator in UpdateViews" );
2921 15231 : aTmpPos = aStartPos;
2922 15231 : if ( !IsVertical() )
2923 : {
2924 15229 : aTmpPos.X() += pLine->GetStartPosX();
2925 15229 : aTmpPos.Y() += pLine->GetMaxAscent();
2926 15229 : aStartPos.Y() += pLine->GetHeight();
2927 15229 : if (nLine != nLastLine)
2928 2565 : aStartPos.Y() += nVertLineSpacing;
2929 : }
2930 : else
2931 : {
2932 2 : aTmpPos.Y() += pLine->GetStartPosX();
2933 2 : aTmpPos.X() -= pLine->GetMaxAscent();
2934 2 : aStartPos.X() -= pLine->GetHeight();
2935 2 : if (nLine != nLastLine)
2936 0 : aStartPos.X() -= nVertLineSpacing;
2937 : }
2938 :
2939 45691 : if ( ( !IsVertical() && ( aStartPos.Y() > aClipRect.Top() ) )
2940 15233 : || ( IsVertical() && aStartPos.X() < aClipRect.Right() ) )
2941 : {
2942 15231 : bPaintBullet = false;
2943 :
2944 : // Why not just also call when stripping portions? This will give the correct values
2945 : // and needs no position corrections in OutlinerEditEng::DrawingText which tries to call
2946 : // PaintBullet correctly; exactly what GetEditEnginePtr()->PaintingFirstLine
2947 : // does, too. No change for not-layouting (painting).
2948 15231 : if(0 == nLine) // && !bStripOnly)
2949 : {
2950 12669 : GetEditEnginePtr()->PaintingFirstLine( n, aParaStart, aTmpPos.Y(), aOrigin, nOrientation, pOutDev );
2951 :
2952 : // Remember whether a bullet was painted.
2953 : const SfxBoolItem& rBulletState = static_cast<const SfxBoolItem&>(
2954 12669 : pEditEngine->GetParaAttrib(n, EE_PARA_BULLETSTATE));
2955 12669 : bPaintBullet = rBulletState.GetValue() ? true : false;
2956 : }
2957 :
2958 :
2959 : // Over the Portions of the line ...
2960 :
2961 15231 : bool bParsingFields = false;
2962 15231 : ::std::vector< sal_Int32 >::iterator itSubLines;
2963 :
2964 30545 : for ( sal_Int32 nPortion = pLine->GetStartPortion(); nPortion <= pLine->GetEndPortion(); nPortion++ )
2965 : {
2966 : DBG_ASSERT( pPortion->GetTextPortions().Count(), "Line without Textportion in Paint!" );
2967 15338 : const TextPortion* pTextPortion = pPortion->GetTextPortions()[nPortion];
2968 : DBG_ASSERT( pTextPortion, "NULL-Pointer in Portion iterator in UpdateViews" );
2969 :
2970 15338 : long nPortionXOffset = GetPortionXOffset( pPortion, pLine, nPortion );
2971 15338 : if ( !IsVertical() )
2972 : {
2973 15336 : aTmpPos.X() = aStartPos.X() + nPortionXOffset;
2974 15336 : if ( aTmpPos.X() > aClipRect.Right() )
2975 24 : break; // No further output in line necessary
2976 : }
2977 : else
2978 : {
2979 2 : aTmpPos.Y() = aStartPos.Y() + nPortionXOffset;
2980 2 : if ( aTmpPos.Y() > aClipRect.Bottom() )
2981 0 : break; // No further output in line necessary
2982 : }
2983 :
2984 15314 : switch ( pTextPortion->GetKind() )
2985 : {
2986 : case PORTIONKIND_TEXT:
2987 : case PORTIONKIND_FIELD:
2988 : case PORTIONKIND_HYPHENATOR:
2989 : {
2990 15299 : SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
2991 :
2992 15299 : bool bDrawFrame = false;
2993 :
2994 62035 : if ( ( pTextPortion->GetKind() == PORTIONKIND_FIELD ) && !aTmpFont.IsTransparent() &&
2995 45909 : ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() &&
2996 2 : ( IsAutoColorEnabled() && ( pOutDev->GetOutDevType() != OUTDEV_PRINTER ) ) )
2997 : {
2998 1 : aTmpFont.SetTransparent( true );
2999 1 : pOutDev->SetFillColor();
3000 1 : pOutDev->SetLineColor( GetAutoColor() );
3001 1 : bDrawFrame = true;
3002 : }
3003 :
3004 : #if OSL_DEBUG_LEVEL > 2
3005 : if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3006 : {
3007 : aTmpFont.SetFillColor( COL_LIGHTGRAY );
3008 : aTmpFont.SetTransparent( sal_False );
3009 : }
3010 : if ( pTextPortion->GetRightToLeft() )
3011 : {
3012 : aTmpFont.SetFillColor( COL_LIGHTGRAY );
3013 : aTmpFont.SetTransparent( sal_False );
3014 : }
3015 : else if ( GetI18NScriptType( EditPaM( pPortion->GetNode(), nIndex+1 ) ) == i18n::ScriptType::COMPLEX )
3016 : {
3017 : aTmpFont.SetFillColor( COL_LIGHTCYAN );
3018 : aTmpFont.SetTransparent( sal_False );
3019 : }
3020 : #endif
3021 15299 : aTmpFont.SetPhysFont( pOutDev );
3022 :
3023 : // #114278# Saving both layout mode and language (since I'm
3024 : // potentially changing both)
3025 15299 : pOutDev->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
3026 15299 : ImplInitLayoutMode( pOutDev, n, nIndex );
3027 15299 : ImplInitDigitMode(pOutDev, aTmpFont.GetLanguage());
3028 :
3029 15299 : OUString aText;
3030 15299 : sal_Int32 nTextStart = 0;
3031 15299 : sal_Int32 nTextLen = 0;
3032 15299 : const sal_Int32* pDXArray = 0;
3033 30598 : boost::scoped_array<sal_Int32> pTmpDXArray;
3034 :
3035 15299 : if ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3036 : {
3037 14464 : aText = pPortion->GetNode()->GetString();
3038 14464 : nTextStart = nIndex;
3039 14464 : nTextLen = pTextPortion->GetLen();
3040 14464 : if (!pLine->GetCharPosArray().empty())
3041 13090 : pDXArray = &pLine->GetCharPosArray()[0]+( nIndex-pLine->GetStart() );
3042 :
3043 : // Paint control characters (#i55716#)
3044 14464 : if ( aStatus.MarkFields() )
3045 : {
3046 : sal_Int32 nTmpIdx;
3047 1615 : const sal_Int32 nTmpEnd = nTextStart + pTextPortion->GetLen();
3048 :
3049 8886 : for ( nTmpIdx = nTextStart; nTmpIdx <= nTmpEnd ; ++nTmpIdx )
3050 : {
3051 13113 : const sal_Unicode cChar = ( nTmpIdx != aText.getLength() && ( nTmpIdx != nTextStart || 0 == nTextStart ) ) ?
3052 : aText[nTmpIdx] :
3053 12931 : 0;
3054 :
3055 7271 : if ( 0x200B == cChar || 0x2060 == cChar )
3056 : {
3057 0 : const OUString aBlank( ' ' );
3058 0 : long nHalfBlankWidth = aTmpFont.QuickGetTextSize( pOutDev, aBlank, 0, 1, 0 ).Width() / 2;
3059 :
3060 : const long nAdvanceX = ( nTmpIdx == nTmpEnd ?
3061 0 : pTextPortion->GetSize().Width() :
3062 0 : pDXArray[ nTmpIdx - nTextStart ] ) - nHalfBlankWidth;
3063 0 : const long nAdvanceY = -pLine->GetMaxAscent();
3064 :
3065 0 : Point aTopLeftRectPos( aTmpPos );
3066 0 : if ( !IsVertical() )
3067 : {
3068 0 : aTopLeftRectPos.X() += nAdvanceX;
3069 0 : aTopLeftRectPos.Y() += nAdvanceY;
3070 : }
3071 : else
3072 : {
3073 0 : aTopLeftRectPos.Y() += nAdvanceX;
3074 0 : aTopLeftRectPos.X() -= nAdvanceY;
3075 : }
3076 :
3077 0 : Point aBottomRightRectPos( aTopLeftRectPos );
3078 0 : if ( !IsVertical() )
3079 : {
3080 0 : aBottomRightRectPos.X() += 2 * nHalfBlankWidth;
3081 0 : aBottomRightRectPos.Y() += pLine->GetHeight();
3082 : }
3083 : else
3084 : {
3085 0 : aBottomRightRectPos.X() -= pLine->GetHeight();
3086 0 : aBottomRightRectPos.Y() += 2 * nHalfBlankWidth;
3087 : }
3088 :
3089 0 : pOutDev->Push( PUSH_FILLCOLOR );
3090 0 : pOutDev->Push( PUSH_LINECOLOR );
3091 0 : pOutDev->SetFillColor( COL_LIGHTGRAY );
3092 0 : pOutDev->SetLineColor( COL_LIGHTGRAY );
3093 :
3094 0 : const Rectangle aBackRect( aTopLeftRectPos, aBottomRightRectPos );
3095 0 : pOutDev->DrawRect( aBackRect );
3096 :
3097 0 : pOutDev->Pop();
3098 0 : pOutDev->Pop();
3099 :
3100 0 : if ( 0x200B == cChar )
3101 : {
3102 0 : const OUString aSlash( '/' );
3103 0 : const short nOldEscapement = aTmpFont.GetEscapement();
3104 0 : const sal_uInt8 nOldPropr = aTmpFont.GetPropr();
3105 :
3106 0 : aTmpFont.SetEscapement( -20 );
3107 0 : aTmpFont.SetPropr( 25 );
3108 0 : aTmpFont.SetPhysFont( pOutDev );
3109 :
3110 0 : const Size aSlashSize = aTmpFont.QuickGetTextSize( pOutDev, aSlash, 0, 1, 0 );
3111 0 : Point aSlashPos( aTmpPos );
3112 0 : const long nAddX = nHalfBlankWidth - aSlashSize.Width() / 2;
3113 0 : if ( !IsVertical() )
3114 : {
3115 0 : aSlashPos.X() = aTopLeftRectPos.X() + nAddX;
3116 : }
3117 : else
3118 : {
3119 0 : aSlashPos.Y() = aTopLeftRectPos.Y() + nAddX;
3120 : }
3121 :
3122 0 : aTmpFont.QuickDrawText( pOutDev, aSlashPos, aSlash, 0, 1, 0 );
3123 :
3124 0 : aTmpFont.SetEscapement( nOldEscapement );
3125 0 : aTmpFont.SetPropr( nOldPropr );
3126 0 : aTmpFont.SetPhysFont( pOutDev );
3127 0 : }
3128 : }
3129 : }
3130 : }
3131 : }
3132 835 : else if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3133 : {
3134 835 : const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3135 : DBG_ASSERT( pAttr, "Field not found");
3136 : DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Field of the wrong type! ");
3137 835 : aText = ((EditCharAttribField*)pAttr)->GetFieldValue();
3138 835 : nTextStart = 0;
3139 835 : nTextLen = aText.getLength();
3140 835 : ExtraPortionInfo *pExtraInfo = pTextPortion->GetExtraInfos();
3141 : // Do not split the Fields into different lines while editing
3142 835 : if( bStripOnly && !bParsingFields && pExtraInfo && pExtraInfo->lineBreaksList.size() )
3143 : {
3144 0 : bParsingFields = true;
3145 0 : itSubLines = pExtraInfo->lineBreaksList.begin();
3146 : }
3147 835 : if( bParsingFields )
3148 : {
3149 0 : if( itSubLines != pExtraInfo->lineBreaksList.begin() )
3150 : {
3151 0 : if ( !IsVertical() )
3152 : {
3153 0 : aStartPos.Y() += pLine->GetMaxAscent();
3154 0 : aTmpPos.Y() += pLine->GetHeight();
3155 : }
3156 : else
3157 : {
3158 0 : aTmpPos.X() -= pLine->GetMaxAscent();
3159 0 : aStartPos.X() -= pLine->GetHeight();
3160 : }
3161 : }
3162 0 : ::std::vector< sal_Int32 >::iterator curIt = itSubLines;
3163 0 : ++itSubLines;
3164 0 : if( itSubLines != pExtraInfo->lineBreaksList.end() )
3165 : {
3166 0 : nTextStart = *curIt;
3167 0 : nTextLen = *itSubLines - nTextStart;
3168 : }
3169 : else
3170 : {
3171 0 : nTextStart = *curIt;
3172 0 : nTextLen = nTextLen - nTextStart;
3173 0 : bParsingFields = false;
3174 : }
3175 : }
3176 :
3177 835 : pTmpDXArray.reset(new sal_Int32[ aText.getLength() ]);
3178 835 : pDXArray = pTmpDXArray.get();
3179 835 : Font _aOldFont( GetRefDevice()->GetFont() );
3180 835 : aTmpFont.SetPhysFont( GetRefDevice() );
3181 835 : aTmpFont.QuickGetTextSize( GetRefDevice(), aText, nTextStart, nTextLen, pTmpDXArray.get() );
3182 835 : if ( aStatus.DoRestoreFont() )
3183 0 : GetRefDevice()->SetFont( _aOldFont );
3184 :
3185 : // add a meta file comment if we record to a metafile
3186 835 : if( bMetafileValid )
3187 : {
3188 0 : const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
3189 0 : if( pFieldItem )
3190 : {
3191 0 : const SvxFieldData* pFieldData = pFieldItem->GetField();
3192 0 : if( pFieldData )
3193 0 : pMtf->AddAction( pFieldData->createBeginComment() );
3194 : }
3195 835 : }
3196 :
3197 : }
3198 0 : else if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3199 : {
3200 0 : if ( pTextPortion->GetExtraValue() )
3201 0 : aText = OUString(pTextPortion->GetExtraValue());
3202 0 : aText += OUString(CH_HYPH);
3203 0 : nTextStart = 0;
3204 0 : nTextLen = aText.getLength();
3205 :
3206 : // crash when accessing 0 pointer in pDXArray
3207 0 : pTmpDXArray.reset(new sal_Int32[ aText.getLength() ]);
3208 0 : pDXArray = pTmpDXArray.get();
3209 0 : Font _aOldFont( GetRefDevice()->GetFont() );
3210 0 : aTmpFont.SetPhysFont( GetRefDevice() );
3211 0 : aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.getLength(), pTmpDXArray.get() );
3212 0 : if ( aStatus.DoRestoreFont() )
3213 0 : GetRefDevice()->SetFont( _aOldFont );
3214 : }
3215 :
3216 15299 : long nTxtWidth = pTextPortion->GetSize().Width();
3217 :
3218 15299 : Point aOutPos( aTmpPos );
3219 15299 : aRedLineTmpPos = aTmpPos;
3220 : // In RTL portions spell markup pos should be at the start of the
3221 : // first chara as well. That is on the right end of the portion
3222 15299 : if (pTextPortion->IsRightToLeft())
3223 0 : aRedLineTmpPos.X() += pTextPortion->GetSize().Width();
3224 :
3225 15299 : if ( bStripOnly )
3226 : {
3227 13325 : EEngineData::WrongSpellVector aWrongSpellVector;
3228 :
3229 13325 : if(GetStatus().DoOnlineSpelling() && pTextPortion->GetLen())
3230 : {
3231 4533 : WrongList* pWrongs = pPortion->GetNode()->GetWrongList();
3232 :
3233 4533 : if(pWrongs && !pWrongs->empty())
3234 : {
3235 0 : size_t nStart = nIndex, nEnd = 0;
3236 0 : bool bWrong = pWrongs->NextWrong(nStart, nEnd);
3237 0 : const size_t nMaxEnd(nIndex + pTextPortion->GetLen());
3238 :
3239 0 : while(bWrong)
3240 : {
3241 0 : if(nStart >= nMaxEnd)
3242 : {
3243 0 : break;
3244 : }
3245 :
3246 0 : if(nStart < (size_t)nIndex)
3247 : {
3248 0 : nStart = nIndex;
3249 : }
3250 :
3251 0 : if(nEnd > nMaxEnd)
3252 : {
3253 0 : nEnd = nMaxEnd;
3254 : }
3255 :
3256 : // add to vector
3257 0 : aWrongSpellVector.push_back(EEngineData::WrongSpellClass(nStart, nEnd));
3258 :
3259 : // goto next index
3260 0 : nStart = nEnd + 1;
3261 :
3262 0 : if(nEnd < nMaxEnd)
3263 : {
3264 0 : bWrong = pWrongs->NextWrong(nStart, nEnd);
3265 : }
3266 : else
3267 : {
3268 0 : bWrong = false;
3269 : }
3270 : }
3271 : }
3272 : }
3273 :
3274 13325 : const SvxFieldData* pFieldData = 0;
3275 :
3276 13325 : if(PORTIONKIND_FIELD == pTextPortion->GetKind())
3277 : {
3278 769 : const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3279 769 : const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
3280 :
3281 769 : if(pFieldItem)
3282 : {
3283 769 : pFieldData = pFieldItem->GetField();
3284 : }
3285 : }
3286 :
3287 : // support for EOC, EOW, EOS TEXT comments. To support that,
3288 : // the locale is needed. With the locale and a XBreakIterator it is
3289 : // possible to re-create the text marking info on primitive level
3290 26650 : const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(), nIndex + 1)));
3291 :
3292 : // create EOL and EOP bools
3293 13325 : const bool bEndOfLine(nPortion == pLine->GetEndPortion());
3294 13325 : const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
3295 :
3296 : // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
3297 : // consequence, but also already set at pOutDev)
3298 13325 : const Color aOverlineColor(pOutDev->GetOverlineColor());
3299 :
3300 : // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
3301 : // consequence, but also already set at pOutDev)
3302 13325 : const Color aTextLineColor(pOutDev->GetTextLineColor());
3303 :
3304 : // Unicode code points conversion according to ctl text numeral setting
3305 26650 : aText = convertDigits(aText, nTextStart, nTextLen,
3306 26650 : ImplCalcDigitLang(aTmpFont.GetLanguage()));
3307 :
3308 : // StripPortions() data callback
3309 13325 : GetEditEnginePtr()->DrawingText( aOutPos, aText, nTextStart, nTextLen, pDXArray,
3310 13325 : aTmpFont, n, nIndex, pTextPortion->GetRightToLeft(),
3311 13325 : aWrongSpellVector.size() ? &aWrongSpellVector : 0,
3312 : pFieldData,
3313 : bEndOfLine, bEndOfParagraph, false, // support for EOL/EOP TEXT comments
3314 : &aLocale,
3315 : aOverlineColor,
3316 39975 : aTextLineColor);
3317 :
3318 : // #108052# remember that EOP is written already for this ParaPortion
3319 13325 : if(bEndOfParagraph)
3320 : {
3321 10888 : bEndOfParagraphWritten = true;
3322 13325 : }
3323 : }
3324 : else
3325 : {
3326 1974 : short nEsc = aTmpFont.GetEscapement();
3327 1974 : if ( nOrientation )
3328 : {
3329 : // In case of high/low do it yourself:
3330 453 : if ( aTmpFont.GetEscapement() )
3331 : {
3332 0 : long nDiff = aTmpFont.GetSize().Height() * aTmpFont.GetEscapement() / 100L;
3333 0 : if ( !IsVertical() )
3334 0 : aOutPos.Y() -= nDiff;
3335 : else
3336 0 : aOutPos.X() += nDiff;
3337 0 : aRedLineTmpPos = aOutPos;
3338 0 : aTmpFont.SetEscapement( 0 );
3339 : }
3340 :
3341 453 : aOutPos = lcl_ImplCalcRotatedPos( aOutPos, aOrigin, nSin, nCos );
3342 453 : aTmpFont.SetOrientation( aTmpFont.GetOrientation()+nOrientation );
3343 453 : aTmpFont.SetPhysFont( pOutDev );
3344 :
3345 : }
3346 :
3347 : // Take only what begins in the visible range:
3348 : // Important, because of a bug in some graphic cards
3349 : // when transparent font, output when negative
3350 3495 : if ( nOrientation || ( !IsVertical() && ( ( aTmpPos.X() + nTxtWidth ) >= nFirstVisXPos ) )
3351 1974 : || ( IsVertical() && ( ( aTmpPos.Y() + nTxtWidth ) >= nFirstVisYPos ) ) )
3352 : {
3353 1974 : if ( nEsc && ( ( aTmpFont.GetUnderline() != UNDERLINE_NONE ) ) )
3354 : {
3355 : // Paint the high/low without underline,
3356 : // Display the Underline on the
3357 : // base line of the original font height ...
3358 : // But only if there was something underlined before!
3359 92 : bool bSpecialUnderline = false;
3360 92 : EditCharAttrib* pPrev = pPortion->GetNode()->GetCharAttribs().FindAttrib( EE_CHAR_ESCAPEMENT, nIndex );
3361 92 : if ( pPrev )
3362 : {
3363 92 : SvxFont aDummy;
3364 : // Underscore in front?
3365 92 : if ( pPrev->GetStart() )
3366 : {
3367 0 : SeekCursor( pPortion->GetNode(), pPrev->GetStart(), aDummy );
3368 0 : if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3369 0 : bSpecialUnderline = true;
3370 : }
3371 92 : if ( !bSpecialUnderline && ( pPrev->GetEnd() < pPortion->GetNode()->Len() ) )
3372 : {
3373 0 : SeekCursor( pPortion->GetNode(), pPrev->GetEnd()+1, aDummy );
3374 0 : if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3375 0 : bSpecialUnderline = true;
3376 92 : }
3377 : }
3378 92 : if ( bSpecialUnderline )
3379 : {
3380 0 : Size aSz = aTmpFont.GetPhysTxtSize( pOutDev, aText, nTextStart, nTextLen );
3381 0 : sal_uInt8 nProp = aTmpFont.GetPropr();
3382 0 : aTmpFont.SetEscapement( 0 );
3383 0 : aTmpFont.SetPropr( 100 );
3384 0 : aTmpFont.SetPhysFont( pOutDev );
3385 0 : OUStringBuffer aBlanks;
3386 0 : comphelper::string::padToLength( aBlanks, (sal_Int32) nTextLen, ' ' );
3387 0 : Point aUnderlinePos( aOutPos );
3388 0 : if ( nOrientation )
3389 0 : aUnderlinePos = lcl_ImplCalcRotatedPos( aTmpPos, aOrigin, nSin, nCos );
3390 0 : pOutDev->DrawStretchText( aUnderlinePos, aSz.Width(), aBlanks.makeStringAndClear(), 0, nTextLen );
3391 :
3392 0 : aTmpFont.SetUnderline( UNDERLINE_NONE );
3393 0 : if ( !nOrientation )
3394 0 : aTmpFont.SetEscapement( nEsc );
3395 0 : aTmpFont.SetPropr( nProp );
3396 0 : aTmpFont.SetPhysFont( pOutDev );
3397 : }
3398 : }
3399 1974 : Point aRealOutPos( aOutPos );
3400 3948 : if ( ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3401 1908 : && pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed
3402 1974 : && pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation )
3403 : {
3404 0 : aRealOutPos.X() += pTextPortion->GetExtraInfos()->nPortionOffsetX;
3405 : }
3406 :
3407 : // RTL portions with (#i37132#)
3408 : // compressed blank should not paint this blank:
3409 3948 : if ( pTextPortion->IsRightToLeft() && nTextLen >= 2 &&
3410 0 : pDXArray[ nTextLen - 1 ] ==
3411 1974 : pDXArray[ nTextLen - 2 ] &&
3412 0 : ' ' == aText[nTextStart + nTextLen - 1] )
3413 0 : --nTextLen;
3414 :
3415 : // output directly
3416 1974 : aTmpFont.QuickDrawText( pOutDev, aRealOutPos, aText, nTextStart, nTextLen, pDXArray );
3417 :
3418 1974 : if ( bDrawFrame )
3419 : {
3420 1 : Point aTopLeft( aTmpPos );
3421 1 : aTopLeft.Y() -= pLine->GetMaxAscent();
3422 1 : if ( nOrientation )
3423 0 : aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
3424 1 : Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3425 1 : pOutDev->DrawRect( aRect );
3426 : }
3427 :
3428 : // PDF export:
3429 1974 : if ( pPDFExtOutDevData )
3430 : {
3431 0 : if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3432 : {
3433 0 : const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3434 0 : const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
3435 0 : if( pFieldItem )
3436 : {
3437 0 : const SvxFieldData* pFieldData = pFieldItem->GetField();
3438 0 : if ( pFieldData->ISA( SvxURLField ) )
3439 : {
3440 0 : Point aTopLeft( aTmpPos );
3441 0 : aTopLeft.Y() -= pLine->GetMaxAscent();
3442 :
3443 0 : Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3444 0 : vcl::PDFExtOutDevBookmarkEntry aBookmark;
3445 0 : aBookmark.nLinkId = pPDFExtOutDevData->CreateLink( aRect );
3446 0 : aBookmark.aBookmark = ((SvxURLField*)pFieldData)->GetURL();
3447 0 : std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
3448 0 : rBookmarks.push_back( aBookmark );
3449 : }
3450 : }
3451 : }
3452 : }
3453 : }
3454 :
3455 1974 : if ( GetStatus().DoOnlineSpelling() && !pPortion->GetNode()->GetWrongList()->empty() && pTextPortion->GetLen() )
3456 : {
3457 : {//#105750# adjust LinePos for superscript or subscript text
3458 107 : short _nEsc = aTmpFont.GetEscapement();
3459 107 : if( _nEsc )
3460 : {
3461 93 : long nShift = ((_nEsc*long(aTmpFont.GetSize().Height()))/ 100L);
3462 93 : if( !IsVertical() )
3463 93 : aRedLineTmpPos.Y() -= nShift;
3464 : else
3465 0 : aRedLineTmpPos.X() += nShift;
3466 : }
3467 : }
3468 107 : Color aOldColor( pOutDev->GetLineColor() );
3469 107 : pOutDev->SetLineColor( Color( GetColorConfig().GetColorValue( svtools::SPELL ).nColor ) );
3470 107 : lcl_DrawRedLines( pOutDev, aTmpFont.GetSize().Height(), aRedLineTmpPos, (size_t)nIndex, (size_t)nIndex + pTextPortion->GetLen(), pDXArray, pPortion->GetNode()->GetWrongList(), nOrientation, aOrigin, IsVertical(), pTextPortion->IsRightToLeft() );
3471 107 : pOutDev->SetLineColor( aOldColor );
3472 : }
3473 : }
3474 :
3475 15299 : pOutDev->Pop();
3476 :
3477 15299 : pTmpDXArray.reset();
3478 :
3479 15299 : if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3480 : {
3481 835 : const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3482 : DBG_ASSERT( pAttr, "Field not found" );
3483 : DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Wrong type of field!" );
3484 :
3485 : // add a meta file comment if we record to a metafile
3486 835 : if( bMetafileValid )
3487 : {
3488 0 : const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
3489 :
3490 0 : if( pFieldItem )
3491 : {
3492 0 : const SvxFieldData* pFieldData = pFieldItem->GetField();
3493 0 : if( pFieldData )
3494 0 : pMtf->AddAction( pFieldData->createEndComment() );
3495 : }
3496 : }
3497 :
3498 15299 : }
3499 :
3500 : }
3501 15299 : break;
3502 : case PORTIONKIND_TAB:
3503 : {
3504 0 : if ( pTextPortion->GetExtraValue() && ( pTextPortion->GetExtraValue() != ' ' ) )
3505 : {
3506 0 : SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
3507 0 : aTmpFont.SetTransparent( false );
3508 0 : aTmpFont.SetEscapement( 0 );
3509 0 : aTmpFont.SetPhysFont( pOutDev );
3510 : long nCharWidth = aTmpFont.QuickGetTextSize( pOutDev,
3511 0 : OUString(pTextPortion->GetExtraValue()), 0, 1, NULL ).Width();
3512 0 : sal_Int32 nChars = 2;
3513 0 : if( nCharWidth )
3514 0 : nChars = pTextPortion->GetSize().Width() / nCharWidth;
3515 0 : if ( nChars < 2 )
3516 0 : nChars = 2; // is compressed by DrawStretchText.
3517 0 : else if ( nChars == 2 )
3518 0 : nChars = 3; // looks better
3519 :
3520 0 : OUStringBuffer aBuf;
3521 0 : comphelper::string::padToLength(aBuf, nChars, pTextPortion->GetExtraValue());
3522 0 : OUString aText(aBuf.makeStringAndClear());
3523 0 : aTmpFont.QuickDrawText( pOutDev, aTmpPos, aText, 0, aText.getLength(), NULL );
3524 0 : pOutDev->DrawStretchText( aTmpPos, pTextPortion->GetSize().Width(), aText );
3525 :
3526 0 : if ( bStripOnly )
3527 : {
3528 : // create EOL and EOP bools
3529 0 : const bool bEndOfLine(nPortion == pLine->GetEndPortion());
3530 0 : const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
3531 :
3532 0 : const Color aOverlineColor(pOutDev->GetOverlineColor());
3533 0 : const Color aTextLineColor(pOutDev->GetTextLineColor());
3534 :
3535 : // StripPortions() data callback
3536 0 : GetEditEnginePtr()->DrawingTab( aTmpPos,
3537 0 : pTextPortion->GetSize().Width(),
3538 0 : OUString(pTextPortion->GetExtraValue()),
3539 0 : aTmpFont, n, nIndex, pTextPortion->GetRightToLeft(),
3540 : bEndOfLine, bEndOfParagraph,
3541 0 : aOverlineColor, aTextLineColor);
3542 0 : }
3543 : }
3544 0 : else if ( bStripOnly )
3545 : {
3546 : // #i108052# When stripping, a callback for _empty_ paragraphs is also needed.
3547 : // This was optimized away (by not rendering the space-only tab portion), so do
3548 : // it manually here.
3549 0 : const bool bEndOfLine(nPortion == pLine->GetEndPortion());
3550 0 : const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
3551 :
3552 0 : const Color aOverlineColor(pOutDev->GetOverlineColor());
3553 0 : const Color aTextLineColor(pOutDev->GetTextLineColor());
3554 :
3555 0 : GetEditEnginePtr()->DrawingText(
3556 : aTmpPos, OUString(), 0, 0, 0,
3557 : aTmpFont, n, nIndex, 0,
3558 : 0,
3559 : 0,
3560 : bEndOfLine, bEndOfParagraph, false,
3561 : 0,
3562 : aOverlineColor,
3563 0 : aTextLineColor);
3564 : }
3565 : }
3566 0 : break;
3567 : }
3568 15314 : if( bParsingFields )
3569 0 : nPortion--;
3570 : else
3571 15314 : nIndex = nIndex + pTextPortion->GetLen();
3572 :
3573 : }
3574 : }
3575 :
3576 15231 : if ( ( nLine != nLastLine ) && !aStatus.IsOutliner() )
3577 : {
3578 2565 : if ( !IsVertical() )
3579 2565 : aStartPos.Y() += nSBL;
3580 : else
3581 0 : aStartPos.X() -= nSBL;
3582 : }
3583 :
3584 : // no more visible actions?
3585 15231 : if ( !IsVertical() && ( aStartPos.Y() >= aClipRect.Bottom() ) )
3586 908 : break;
3587 14323 : else if ( IsVertical() && ( aStartPos.X() <= aClipRect.Left() ) )
3588 0 : break;
3589 : }
3590 :
3591 12669 : if ( !aStatus.IsOutliner() )
3592 : {
3593 12669 : const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3594 12669 : long nUL = GetYValue( rULItem.GetLower() );
3595 12669 : if ( !IsVertical() )
3596 12667 : aStartPos.Y() += nUL;
3597 : else
3598 2 : aStartPos.X() -= nUL;
3599 : }
3600 :
3601 : // #108052# Safer way for #i108052# and #i118881#: If for the current ParaPortion
3602 : // EOP is not written, do it now. This will be safer than before. It has shown
3603 : // that the reason for #i108052# was fixed/removed again, so this is a try to fix
3604 : // the number of paragraphs (and counting empty ones) now independent from the
3605 : // changes in EditEngine behaviour.
3606 12669 : if(!bEndOfParagraphWritten && !bPaintBullet && bStripOnly)
3607 : {
3608 0 : const Color aOverlineColor(pOutDev->GetOverlineColor());
3609 0 : const Color aTextLineColor(pOutDev->GetTextLineColor());
3610 :
3611 0 : GetEditEnginePtr()->DrawingText(
3612 : aTmpPos, OUString(), 0, 0, 0,
3613 : aTmpFont, n, nIndex, 0,
3614 : 0,
3615 : 0,
3616 : false, true, false, // support for EOL/EOP TEXT comments
3617 : 0,
3618 : aOverlineColor,
3619 0 : aTextLineColor);
3620 : }
3621 : }
3622 : else
3623 : {
3624 0 : if ( !IsVertical() )
3625 0 : aStartPos.Y() += nParaHeight;
3626 : else
3627 0 : aStartPos.X() -= nParaHeight;
3628 : }
3629 :
3630 12669 : if ( pPDFExtOutDevData )
3631 0 : pPDFExtOutDevData->EndStructureElement();
3632 :
3633 : // no more visible actions?
3634 12669 : if ( !IsVertical() && ( aStartPos.Y() > aClipRect.Bottom() ) )
3635 908 : break;
3636 11761 : if ( IsVertical() && ( aStartPos.X() < aClipRect.Left() ) )
3637 0 : break;
3638 : }
3639 11025 : if ( aStatus.DoRestoreFont() )
3640 11025 : pOutDev->SetFont( aOldFont );
3641 : }
3642 :
3643 935 : void ImpEditEngine::Paint( ImpEditView* pView, const Rectangle& rRect, OutputDevice* pTargetDevice, sal_Bool bUseVirtDev )
3644 : {
3645 : DBG_ASSERT( pView, "No View - No Paint!" );
3646 :
3647 935 : if ( !GetUpdateMode() || IsInUndo() )
3648 0 : return;
3649 :
3650 : // Intersection of paint area and output area.
3651 935 : Rectangle aClipRect( pView->GetOutputArea() );
3652 935 : aClipRect.Intersection( rRect );
3653 :
3654 935 : OutputDevice* pTarget = pTargetDevice ? pTargetDevice : pView->GetWindow();
3655 :
3656 935 : if ( bUseVirtDev )
3657 : {
3658 362 : Rectangle aClipRecPixel( pTarget->LogicToPixel( aClipRect ) );
3659 362 : if ( !IsVertical() )
3660 : {
3661 : // etwas mehr, falls abgerundet!
3662 362 : aClipRecPixel.Right() += 1;
3663 362 : aClipRecPixel.Bottom() += 1;
3664 : }
3665 : else
3666 : {
3667 0 : aClipRecPixel.Left() -= 1;
3668 0 : aClipRecPixel.Bottom() += 1;
3669 : }
3670 :
3671 : // If aClipRecPixel > XXXX, then invalidate?!
3672 :
3673 362 : VirtualDevice* pVDev = GetVirtualDevice( pTarget->GetMapMode(), pTarget->GetDrawMode() );
3674 362 : pVDev->SetDigitLanguage( GetRefDevice()->GetDigitLanguage() );
3675 :
3676 : {
3677 362 : Color aBackgroundColor( pView->GetBackgroundColor() );
3678 : // #i47161# Check if text is visible on background
3679 362 : SvxFont aTmpFont;
3680 362 : ContentNode* pNode = GetEditDoc().GetObject( 0 );
3681 362 : SeekCursor( pNode, 1, aTmpFont );
3682 362 : Color aFontColor( aTmpFont.GetColor() );
3683 362 : if( (aFontColor == COL_AUTO) || IsForceAutoColor() )
3684 151 : aFontColor = GetAutoColor();
3685 :
3686 : // #i69346# check for reverse color of input method attribute
3687 362 : if( mpIMEInfos && (mpIMEInfos->aPos.GetNode() == pNode &&
3688 : mpIMEInfos->pAttribs))
3689 : {
3690 0 : sal_uInt16 nAttr = mpIMEInfos->pAttribs[ 0 ];
3691 0 : if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
3692 : {
3693 0 : const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
3694 0 : aFontColor = rStyleSettings.GetHighlightColor() ;
3695 : }
3696 : }
3697 :
3698 362 : sal_uInt8 nColorDiff = aFontColor.GetColorError( aBackgroundColor );
3699 362 : if( nColorDiff < 8 )
3700 0 : aBackgroundColor = aFontColor.IsDark() ? COL_WHITE : COL_BLACK;
3701 362 : pVDev->SetBackground( aBackgroundColor );
3702 : }
3703 :
3704 362 : sal_Bool bVDevValid = sal_True;
3705 362 : Size aOutSz( pVDev->GetOutputSizePixel() );
3706 466 : if ( ( aOutSz.Width() < aClipRecPixel.GetWidth() ) ||
3707 104 : ( aOutSz.Height() < aClipRecPixel.GetHeight() ) )
3708 : {
3709 258 : bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3710 : }
3711 : else
3712 : {
3713 : // The VirtDev can become very big during a Resize =>
3714 : // eventually make it smaller!
3715 208 : if ( ( aOutSz.Height() > ( aClipRecPixel.GetHeight() + RESDIFF ) ) ||
3716 104 : ( aOutSz.Width() > ( aClipRecPixel.GetWidth() + RESDIFF ) ) )
3717 : {
3718 0 : bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3719 : }
3720 : else
3721 : {
3722 104 : pVDev->Erase();
3723 : }
3724 : }
3725 : DBG_ASSERT( bVDevValid, "VDef could not be enlarged!" );
3726 362 : if ( !bVDevValid )
3727 : {
3728 0 : Paint( pView, rRect, 0, sal_False /* ohne VDev */ );
3729 0 : return;
3730 : }
3731 :
3732 : // PaintRect for VDev not with aligned size,
3733 : // Otherwise, the line below must also be printed out:
3734 362 : Rectangle aTmpRect( Point( 0, 0 ), aClipRect.GetSize() );
3735 :
3736 362 : aClipRect = pTarget->PixelToLogic( aClipRecPixel );
3737 362 : Point aStartPos;
3738 362 : if ( !IsVertical() )
3739 : {
3740 362 : aStartPos = aClipRect.TopLeft();
3741 362 : aStartPos = pView->GetDocPos( aStartPos );
3742 362 : aStartPos.X() *= (-1);
3743 362 : aStartPos.Y() *= (-1);
3744 : }
3745 : else
3746 : {
3747 0 : aStartPos = aClipRect.TopRight();
3748 0 : Point aDocPos( pView->GetDocPos( aStartPos ) );
3749 0 : aStartPos.X() = aClipRect.GetSize().Width() + aDocPos.Y();
3750 0 : aStartPos.Y() = -aDocPos.X();
3751 : }
3752 :
3753 362 : Paint( pVDev, aTmpRect, aStartPos );
3754 :
3755 362 : sal_Bool bClipRegion = sal_False;
3756 362 : Region aOldRegion;
3757 724 : MapMode aOldMapMode;
3758 362 : if ( GetTextRanger() )
3759 : {
3760 : // Some problems here with push/pop, why?!
3761 : // pTarget->Push( PUSH_CLIPREGION|PUSH_MAPMODE );
3762 0 : bClipRegion = pTarget->IsClipRegion();
3763 0 : aOldRegion = pTarget->GetClipRegion();
3764 : // How do I get the polygon to the right place??
3765 : // The polygon is based on the view, not the Window
3766 : // => reset origin...
3767 0 : aOldMapMode = pTarget->GetMapMode();
3768 0 : Point aOrigin = aOldMapMode.GetOrigin();
3769 0 : Point aViewPos = pView->GetOutputArea().TopLeft();
3770 0 : aOrigin.Move( aViewPos.X(), aViewPos.Y() );
3771 0 : aClipRect.Move( -aViewPos.X(), -aViewPos.Y() );
3772 0 : MapMode aNewMapMode( aOldMapMode );
3773 0 : aNewMapMode.SetOrigin( aOrigin );
3774 0 : pTarget->SetMapMode( aNewMapMode );
3775 0 : pTarget->SetClipRegion( Region( GetTextRanger()->GetPolyPolygon() ) );
3776 : }
3777 :
3778 : pTarget->DrawOutDev( aClipRect.TopLeft(), aClipRect.GetSize(),
3779 362 : Point(0,0), aClipRect.GetSize(), *pVDev );
3780 :
3781 362 : if ( GetTextRanger() )
3782 : {
3783 : // pTarget->Pop();
3784 0 : if ( bClipRegion )
3785 0 : pTarget->SetClipRegion( aOldRegion );
3786 : else
3787 0 : pTarget->SetClipRegion();
3788 0 : pTarget->SetMapMode( aOldMapMode );
3789 : }
3790 :
3791 :
3792 724 : pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3793 : }
3794 : else
3795 : {
3796 573 : Point aStartPos;
3797 573 : if ( !IsVertical() )
3798 : {
3799 573 : aStartPos = pView->GetOutputArea().TopLeft();
3800 573 : aStartPos.X() -= pView->GetVisDocLeft();
3801 573 : aStartPos.Y() -= pView->GetVisDocTop();
3802 : }
3803 : else
3804 : {
3805 0 : aStartPos = pView->GetOutputArea().TopRight();
3806 0 : aStartPos.X() += pView->GetVisDocTop();
3807 0 : aStartPos.Y() -= pView->GetVisDocLeft();
3808 : }
3809 :
3810 : // If Doc-width < Output Area,Width and not wrapped fields,
3811 : // the fields usually protrude if > line.
3812 : // (Not at the top, since there the Doc-width from formatting is already
3813 : // there)
3814 573 : if ( !IsVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
3815 : {
3816 0 : long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
3817 0 : if ( aClipRect.Left() > nMaxX )
3818 0 : return;
3819 0 : if ( aClipRect.Right() > nMaxX )
3820 0 : aClipRect.Right() = nMaxX;
3821 : }
3822 :
3823 573 : sal_Bool bClipRegion = pTarget->IsClipRegion();
3824 573 : Region aOldRegion = pTarget->GetClipRegion();
3825 573 : pTarget->IntersectClipRegion( aClipRect );
3826 :
3827 573 : Paint( pTarget, aClipRect, aStartPos );
3828 :
3829 573 : if ( bClipRegion )
3830 0 : pTarget->SetClipRegion( aOldRegion );
3831 : else
3832 573 : pTarget->SetClipRegion();
3833 :
3834 573 : pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3835 : }
3836 :
3837 : }
3838 :
3839 0 : void ImpEditEngine::InsertContent( ContentNode* pNode, sal_Int32 nPos )
3840 : {
3841 : DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
3842 : DBG_ASSERT( IsInUndo(), "InsertContent only for Undo()!" );
3843 0 : ParaPortion* pNew = new ParaPortion( pNode );
3844 0 : GetParaPortions().Insert(nPos, pNew);
3845 0 : aEditDoc.Insert(nPos, pNode);
3846 0 : if ( IsCallParaInsertedOrDeleted() )
3847 0 : GetEditEnginePtr()->ParagraphInserted( nPos );
3848 0 : }
3849 :
3850 0 : EditPaM ImpEditEngine::SplitContent( sal_Int32 nNode, sal_Int32 nSepPos )
3851 : {
3852 0 : ContentNode* pNode = aEditDoc.GetObject( nNode );
3853 : DBG_ASSERT( pNode, "Invalid Node in SplitContent" );
3854 : DBG_ASSERT( IsInUndo(), "SplitContent only for Undo()!" );
3855 : DBG_ASSERT( nSepPos <= pNode->Len(), "Index out of range: SplitContent" );
3856 0 : EditPaM aPaM( pNode, nSepPos );
3857 0 : return ImpInsertParaBreak( aPaM );
3858 : }
3859 :
3860 0 : EditPaM ImpEditEngine::ConnectContents( sal_Int32 nLeftNode, sal_Bool bBackward )
3861 : {
3862 0 : ContentNode* pLeftNode = aEditDoc.GetObject( nLeftNode );
3863 0 : ContentNode* pRightNode = aEditDoc.GetObject( nLeftNode+1 );
3864 : DBG_ASSERT( pLeftNode, "Invalid left node in ConnectContents ");
3865 : DBG_ASSERT( pRightNode, "Invalid right node in ConnectContents ");
3866 : DBG_ASSERT( IsInUndo(), "ConnectContent only for Undo()!" );
3867 0 : return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
3868 : }
3869 :
3870 2314462 : void ImpEditEngine::SetUpdateMode( bool bUp, EditView* pCurView, sal_Bool bForceUpdate )
3871 : {
3872 2314462 : bool bChanged = ( GetUpdateMode() != bUp );
3873 :
3874 : // When switching from sal_True to sal_False, all selections were visible,
3875 : // => paint over
3876 : // the other hand, were all invisible => paint
3877 : // If !bFormatted, e.g. after SetText, then if UpdateMode=sal_True
3878 : // formatting is not needed immediately, probably because more text is coming.
3879 : // At latest it is formatted at a Paint/CalcTextWidth.
3880 2314462 : bUpdate = bUp;
3881 2314462 : if ( bUpdate && ( bChanged || bForceUpdate ) )
3882 330455 : FormatAndUpdate( pCurView );
3883 2314462 : }
3884 :
3885 0 : void ImpEditEngine::ShowParagraph( sal_Int32 nParagraph, bool bShow )
3886 : {
3887 0 : ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
3888 : DBG_ASSERT( pPPortion, "ShowParagraph: Paragraph does not exist! ");
3889 0 : if ( pPPortion && ( pPPortion->IsVisible() != bShow ) )
3890 : {
3891 0 : pPPortion->SetVisible( bShow );
3892 :
3893 0 : if ( !bShow )
3894 : {
3895 : // Mark as deleted, so that no selection will end or begin at
3896 : // this paragraph...
3897 0 : DeletedNodeInfo* pDelInfo = new DeletedNodeInfo( (sal_uIntPtr)pPPortion->GetNode(), nParagraph );
3898 0 : aDeletedNodes.push_back(pDelInfo);
3899 0 : UpdateSelections();
3900 : // The region below will not be invalidated if UpdateMode = sal_False!
3901 : // If anyway, then save as sal_False before SetVisible !
3902 : }
3903 :
3904 0 : if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
3905 : {
3906 0 : if ( !GetTextRanger() )
3907 : {
3908 0 : if ( pPPortion->IsInvalid() )
3909 : {
3910 0 : Font aOldFont( GetRefDevice()->GetFont() );
3911 0 : CreateLines( nParagraph, 0 ); // 0: No TextRanger
3912 0 : if ( aStatus.DoRestoreFont() )
3913 0 : GetRefDevice()->SetFont( aOldFont );
3914 : }
3915 : else
3916 : {
3917 0 : CalcHeight( pPPortion );
3918 : }
3919 0 : nCurTextHeight += pPPortion->GetHeight();
3920 : }
3921 : else
3922 : {
3923 0 : nCurTextHeight = 0x7fffffff;
3924 : }
3925 : }
3926 :
3927 0 : pPPortion->SetMustRepaint( sal_True );
3928 0 : if ( GetUpdateMode() && !IsInUndo() && !GetTextRanger() )
3929 : {
3930 0 : aInvalidRect = Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
3931 0 : Point( GetPaperSize().Width(), nCurTextHeight ) );
3932 0 : UpdateViews( GetActiveView() );
3933 : }
3934 : }
3935 0 : }
3936 :
3937 0 : EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_Int32 nNewPos, EditView* pCurView )
3938 : {
3939 : DBG_ASSERT( GetParaPortions().Count() != 0, "No paragraphs found: MoveParagraphs" );
3940 0 : if ( GetParaPortions().Count() == 0 )
3941 0 : return EditSelection();
3942 0 : aOldPositions.Justify();
3943 :
3944 0 : EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
3945 :
3946 0 : if ( nNewPos >= GetParaPortions().Count() )
3947 0 : nNewPos = GetParaPortions().Count() - 1;
3948 :
3949 : // Where the paragraph was inserted it has to be properly redrawn:
3950 : // Where the paragraph was removed it has to be properly redrawn:
3951 : // ( and correspondingly in between as well...)
3952 0 : if ( pCurView && GetUpdateMode() )
3953 : {
3954 : // in this case one can redraw directly without invalidating the
3955 : // Portions
3956 0 : sal_Int32 nFirstPortion = std::min( static_cast<sal_Int32>(aOldPositions.Min()), nNewPos );
3957 0 : sal_Int32 nLastPortion = std::max( static_cast<sal_Int32>(aOldPositions.Max()), nNewPos );
3958 :
3959 0 : ParaPortion* pUpperPortion = GetParaPortions().SafeGetObject( nFirstPortion );
3960 0 : ParaPortion* pLowerPortion = GetParaPortions().SafeGetObject( nLastPortion );
3961 :
3962 0 : aInvalidRect = Rectangle(); // make empty
3963 0 : aInvalidRect.Left() = 0;
3964 0 : aInvalidRect.Right() = aPaperSize.Width();
3965 0 : aInvalidRect.Top() = GetParaPortions().GetYOffset( pUpperPortion );
3966 0 : aInvalidRect.Bottom() = GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight();
3967 :
3968 0 : UpdateViews( pCurView );
3969 : }
3970 : else
3971 : {
3972 : // redraw from the upper invalid position
3973 0 : sal_Int32 nFirstInvPara = std::min( static_cast<sal_Int32>(aOldPositions.Min()), nNewPos );
3974 0 : InvalidateFromParagraph( nFirstInvPara );
3975 : }
3976 0 : return aSel;
3977 : }
3978 :
3979 0 : void ImpEditEngine::InvalidateFromParagraph( sal_Int32 nFirstInvPara )
3980 : {
3981 : // The following paragraphs are not invalidated, since ResetHeight()
3982 : // => size change => all the following are re-issued anyway.
3983 : ParaPortion* pTmpPortion;
3984 0 : if ( nFirstInvPara != 0 )
3985 : {
3986 0 : pTmpPortion = GetParaPortions()[nFirstInvPara-1];
3987 0 : pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
3988 : }
3989 : else
3990 : {
3991 0 : pTmpPortion = GetParaPortions()[0];
3992 0 : pTmpPortion->MarkSelectionInvalid( 0, pTmpPortion->GetNode()->Len() );
3993 : }
3994 0 : pTmpPortion->ResetHeight();
3995 0 : }
3996 :
3997 0 : IMPL_LINK_NOARG_INLINE_START(ImpEditEngine, StatusTimerHdl)
3998 : {
3999 0 : CallStatusHdl();
4000 0 : return 0;
4001 : }
4002 0 : IMPL_LINK_NOARG_INLINE_END(ImpEditEngine, StatusTimerHdl)
4003 :
4004 696092 : void ImpEditEngine::CallStatusHdl()
4005 : {
4006 696092 : if ( aStatusHdlLink.IsSet() && aStatus.GetStatusWord() )
4007 : {
4008 : // The Status has to be reset before the Call,
4009 : // since other Flags might be set in the handler...
4010 823 : EditStatus aTmpStatus( aStatus );
4011 823 : aStatus.Clear();
4012 823 : aStatusHdlLink.Call( &aTmpStatus );
4013 823 : aStatusTimer.Stop(); // If called by hand ...
4014 : }
4015 696092 : }
4016 :
4017 0 : ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode* pCurNode )
4018 : {
4019 0 : const ParaPortion* pPortion = FindParaPortion( pCurNode );
4020 : DBG_ASSERT( pPortion, "GetPrevVisibleNode: No matching portion!" );
4021 0 : pPortion = GetPrevVisPortion( pPortion );
4022 0 : if ( pPortion )
4023 0 : return pPortion->GetNode();
4024 0 : return 0;
4025 : }
4026 :
4027 0 : ContentNode* ImpEditEngine::GetNextVisNode( ContentNode* pCurNode )
4028 : {
4029 0 : const ParaPortion* pPortion = FindParaPortion( pCurNode );
4030 : DBG_ASSERT( pPortion, "GetNextVisibleNode: No matching portion!" );
4031 0 : pPortion = GetNextVisPortion( pPortion );
4032 0 : if ( pPortion )
4033 0 : return pPortion->GetNode();
4034 0 : return 0;
4035 : }
4036 :
4037 0 : const ParaPortion* ImpEditEngine::GetPrevVisPortion( const ParaPortion* pCurPortion ) const
4038 : {
4039 0 : sal_Int32 nPara = GetParaPortions().GetPos( pCurPortion );
4040 : DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion not found: GetPrevVisPortion" );
4041 0 : const ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4042 0 : while ( pPortion && !pPortion->IsVisible() )
4043 0 : pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4044 :
4045 0 : return pPortion;
4046 : }
4047 :
4048 0 : const ParaPortion* ImpEditEngine::GetNextVisPortion( const ParaPortion* pCurPortion ) const
4049 : {
4050 0 : sal_Int32 nPara = GetParaPortions().GetPos( pCurPortion );
4051 : DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion not found: GetPrevVisNode" );
4052 0 : const ParaPortion* pPortion = GetParaPortions().SafeGetObject( ++nPara );
4053 0 : while ( pPortion && !pPortion->IsVisible() )
4054 0 : pPortion = GetParaPortions().SafeGetObject( ++nPara );
4055 :
4056 0 : return pPortion;
4057 : }
4058 :
4059 11025 : long ImpEditEngine::CalcVertLineSpacing(Point& rStartPos) const
4060 : {
4061 11025 : long nTotalOccupiedHeight = 0;
4062 11025 : sal_Int32 nTotalLineCount = 0;
4063 11025 : const ParaPortionList& rParaPortions = GetParaPortions();
4064 11025 : sal_Int32 nParaCount = rParaPortions.Count();
4065 :
4066 11037 : for (sal_Int32 i = 0; i < nParaCount; ++i)
4067 : {
4068 11025 : if (GetVerJustification(i) != SVX_VER_JUSTIFY_BLOCK)
4069 : // All paragraphs must have the block justification set.
4070 11013 : return 0;
4071 :
4072 12 : const ParaPortion* pPortion = rParaPortions[i];
4073 12 : nTotalOccupiedHeight += pPortion->GetFirstLineOffset();
4074 :
4075 12 : const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_SBL);
4076 12 : sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
4077 12 : ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
4078 :
4079 12 : const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_ULSPACE);
4080 12 : long nUL = GetYValue( rULItem.GetLower() );
4081 :
4082 12 : const EditLineList& rLines = pPortion->GetLines();
4083 12 : sal_Int32 nLineCount = rLines.Count();
4084 12 : nTotalLineCount += nLineCount;
4085 58 : for (sal_Int32 j = 0; j < nLineCount; ++j)
4086 : {
4087 46 : const EditLine* pLine = rLines[j];
4088 46 : nTotalOccupiedHeight += pLine->GetHeight();
4089 46 : if (j < nLineCount-1)
4090 34 : nTotalOccupiedHeight += nSBL;
4091 46 : nTotalOccupiedHeight += nUL;
4092 : }
4093 : }
4094 :
4095 12 : long nTotalSpace = IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
4096 12 : nTotalSpace -= nTotalOccupiedHeight;
4097 12 : if (nTotalSpace <= 0 || nTotalLineCount <= 1)
4098 12 : return 0;
4099 :
4100 0 : if (IsVertical())
4101 : // Shift the text to the right for the asian layout mode.
4102 0 : rStartPos.X() += nTotalSpace;
4103 :
4104 0 : return nTotalSpace / (nTotalLineCount-1);
4105 : }
4106 :
4107 873 : EditPaM ImpEditEngine::InsertParagraph( sal_Int32 nPara )
4108 : {
4109 873 : EditPaM aPaM;
4110 873 : if ( nPara != 0 )
4111 : {
4112 873 : ContentNode* pNode = GetEditDoc().GetObject( nPara-1 );
4113 873 : if ( !pNode )
4114 0 : pNode = GetEditDoc().GetObject( GetEditDoc().Count() - 1 );
4115 : DBG_ASSERT( pNode, "Not a single paragraph in InsertParagraph ?" );
4116 873 : aPaM = EditPaM( pNode, pNode->Len() );
4117 : }
4118 : else
4119 : {
4120 0 : ContentNode* pNode = GetEditDoc().GetObject( 0 );
4121 0 : aPaM = EditPaM( pNode, 0 );
4122 : }
4123 :
4124 873 : return ImpInsertParaBreak( aPaM );
4125 : }
4126 :
4127 17677 : EditSelection* ImpEditEngine::SelectParagraph( sal_Int32 nPara )
4128 : {
4129 17677 : EditSelection* pSel = 0;
4130 17677 : ContentNode* pNode = GetEditDoc().GetObject( nPara );
4131 : DBG_ASSERTWARNING( pNode, "Paragraph does not exist: SelectParagraph" );
4132 17677 : if ( pNode )
4133 17677 : pSel = new EditSelection( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
4134 :
4135 17677 : return pSel;
4136 : }
4137 :
4138 1151101 : void ImpEditEngine::FormatAndUpdate( EditView* pCurView )
4139 : {
4140 1151101 : if ( bDowning )
4141 1151101 : return ;
4142 :
4143 1151101 : if ( IsInUndo() )
4144 0 : IdleFormatAndUpdate( pCurView );
4145 : else
4146 : {
4147 1151101 : FormatDoc();
4148 1151101 : UpdateViews( pCurView );
4149 : }
4150 : }
4151 :
4152 0 : void ImpEditEngine::SetFlatMode( sal_Bool bFlat )
4153 : {
4154 0 : if ( bFlat != aStatus.UseCharAttribs() )
4155 0 : return;
4156 :
4157 0 : if ( !bFlat )
4158 0 : aStatus.TurnOnFlags( EE_CNTRL_USECHARATTRIBS );
4159 : else
4160 0 : aStatus.TurnOffFlags( EE_CNTRL_USECHARATTRIBS );
4161 :
4162 0 : aEditDoc.CreateDefFont( !bFlat );
4163 :
4164 0 : FormatFullDoc();
4165 0 : UpdateViews( (EditView*) 0);
4166 0 : if ( pActiveView )
4167 0 : pActiveView->ShowCursor();
4168 : }
4169 :
4170 333534 : void ImpEditEngine::SetCharStretching( sal_uInt16 nX, sal_uInt16 nY )
4171 : {
4172 333534 : bool bChanged(false);
4173 333534 : if ( !IsVertical() )
4174 : {
4175 333500 : bChanged = nStretchX!=nX || nStretchY!=nY;
4176 333500 : nStretchX = nX;
4177 333500 : nStretchY = nY;
4178 : }
4179 : else
4180 : {
4181 34 : bChanged = nStretchX!=nY || nStretchY!=nX;
4182 34 : nStretchX = nY;
4183 34 : nStretchY = nX;
4184 : }
4185 :
4186 333534 : if (bChanged && aStatus.DoStretch())
4187 : {
4188 0 : FormatFullDoc();
4189 : // (potentially) need everything redrawn
4190 0 : aInvalidRect=Rectangle(0,0,1000000,1000000);
4191 0 : UpdateViews( GetActiveView() );
4192 : }
4193 333534 : }
4194 :
4195 443956 : const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
4196 : {
4197 443956 : const SvxNumberFormat *pRes = 0;
4198 :
4199 443956 : if (pNode)
4200 : {
4201 : // get index of paragraph
4202 443956 : sal_Int32 nPara = GetEditDoc().GetPos( const_cast< ContentNode * >(pNode) );
4203 : DBG_ASSERT( nPara < EE_PARA_NOT_FOUND, "node not found in array" );
4204 443956 : if (nPara < EE_PARA_NOT_FOUND)
4205 : {
4206 : // the called function may be overloaded by an OutlinerEditEng object to provide
4207 : // access to the SvxNumberFormat of the Outliner.
4208 : // The EditEngine implementation will just return 0.
4209 443956 : pRes = pEditEngine->GetNumberFormat( nPara );
4210 : }
4211 : }
4212 :
4213 443956 : return pRes;
4214 : }
4215 :
4216 443956 : sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
4217 : const ContentNode *pNode,
4218 : sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
4219 : {
4220 : // nSpaceBefore matches the ODF attribut text:space-before
4221 : // nMinLabelWidth matches the ODF attribut text:min-label-width
4222 :
4223 443956 : const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
4224 :
4225 : // if no number format was found we have no Outliner or the numbering level
4226 : // within the Outliner is -1 which means no number format should be applied.
4227 : // Thus the default values to be returned are 0.
4228 443956 : sal_Int32 nSpaceBefore = 0;
4229 443956 : sal_Int32 nMinLabelWidth = 0;
4230 :
4231 443956 : if (pNumFmt)
4232 : {
4233 4785 : nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
4234 4785 : nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
4235 : DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
4236 : }
4237 443956 : if (pnSpaceBefore)
4238 347554 : *pnSpaceBefore = nSpaceBefore;
4239 443956 : if (pnMinLabelWidth)
4240 119976 : *pnMinLabelWidth = nMinLabelWidth;
4241 :
4242 443956 : return nSpaceBefore + nMinLabelWidth;
4243 : }
4244 :
4245 443956 : const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
4246 : {
4247 443956 : return (const SvxLRSpaceItem&)pNode->GetContentAttribs().GetItem( aStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
4248 : }
4249 :
4250 : // select a representative text language for the digit type according to the
4251 : // text numeral setting:
4252 620101 : LanguageType ImpEditEngine::ImplCalcDigitLang(LanguageType eCurLang) const
4253 : {
4254 : // #114278# Also setting up digit language from Svt options
4255 : // (cannot reliably inherit the outdev's setting)
4256 620101 : if( !pCTLOptions )
4257 3436 : pCTLOptions = new SvtCTLOptions;
4258 :
4259 620101 : LanguageType eLang = eCurLang;
4260 620101 : const SvtCTLOptions::TextNumerals nCTLTextNumerals = pCTLOptions->GetCTLTextNumerals();
4261 :
4262 620101 : if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
4263 0 : eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4264 620101 : else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
4265 620101 : eLang = LANGUAGE_ENGLISH;
4266 0 : else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
4267 0 : eLang = (LanguageType) Application::GetSettings().GetLanguageTag().getLanguageType();
4268 :
4269 620101 : return eLang;
4270 : }
4271 :
4272 13325 : OUString ImpEditEngine::convertDigits(const OUString &rString, sal_Int32 nStt, sal_Int32 nLen, LanguageType eDigitLang) const
4273 : {
4274 13325 : OUStringBuffer aBuf(rString);
4275 95371 : for (sal_Int32 nIdx = nStt, nEnd = nStt + nLen; nIdx < nEnd; ++nIdx)
4276 : {
4277 82046 : sal_Unicode cChar = aBuf[nIdx];
4278 82046 : if (cChar >= '0' && cChar <= '9')
4279 12137 : aBuf[nIdx] = GetLocalizedChar(cChar, eDigitLang);
4280 : }
4281 13325 : return aBuf.makeStringAndClear();
4282 : }
4283 :
4284 : // Either sets the digit mode at the output device
4285 606776 : void ImpEditEngine::ImplInitDigitMode(OutputDevice* pOutDev, LanguageType eCurLang)
4286 : {
4287 : assert(pOutDev); //persumably there isn't any case where pOutDev should be NULL ?
4288 606776 : if (pOutDev)
4289 606776 : pOutDev->SetDigitLanguage(ImplCalcDigitLang(eCurLang));
4290 606776 : }
4291 :
4292 234759 : void ImpEditEngine::ImplInitLayoutMode( OutputDevice* pOutDev, sal_Int32 nPara, sal_Int32 nIndex )
4293 : {
4294 234759 : sal_Bool bCTL = sal_False;
4295 234759 : sal_Bool bR2L = sal_False;
4296 234759 : if ( nIndex == -1 )
4297 : {
4298 219460 : bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
4299 219460 : bR2L = IsRightToLeft( nPara );
4300 : }
4301 : else
4302 : {
4303 15299 : ContentNode* pNode = GetEditDoc().GetObject( nPara );
4304 15299 : short nScriptType = GetI18NScriptType( EditPaM( pNode, nIndex+1 ) );
4305 15299 : bCTL = nScriptType == i18n::ScriptType::COMPLEX;
4306 : // this change was discussed in issue 37190
4307 15299 : bR2L = (GetRightToLeft( nPara, nIndex + 1) % 2) ? sal_True : sal_False;
4308 : // it also works for issue 55927
4309 : }
4310 :
4311 234759 : sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
4312 :
4313 : // We always use the left postion for DrawText()
4314 234759 : nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL);
4315 :
4316 234759 : if ( !bCTL && !bR2L)
4317 : {
4318 : // No CTL/Bidi checking necessary
4319 233594 : nLayoutMode |= ( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4320 : }
4321 : else
4322 : {
4323 : // CTL/Bidi checking necessary
4324 : // Don't use BIDI_STRONG, VCL must do some checks.
4325 1165 : nLayoutMode &= ~( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4326 :
4327 1165 : if ( bR2L )
4328 1165 : nLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
4329 : }
4330 :
4331 234759 : pOutDev->SetLayoutMode( nLayoutMode );
4332 :
4333 : // #114278# Also setting up digit language from Svt options
4334 : // (cannot reliably inherit the outdev's setting)
4335 234759 : LanguageType eLang = (LanguageType) Application::GetSettings().GetLanguageTag().getLanguageType();
4336 234759 : ImplInitDigitMode( pOutDev, eLang );
4337 234759 : }
4338 :
4339 256842 : Reference < i18n::XBreakIterator > ImpEditEngine::ImplGetBreakIterator() const
4340 : {
4341 256842 : if ( !xBI.is() )
4342 : {
4343 14130 : Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
4344 14130 : xBI = i18n::BreakIterator::create( xContext );
4345 : }
4346 256842 : return xBI;
4347 : }
4348 :
4349 0 : Reference < i18n::XExtendedInputSequenceChecker > ImpEditEngine::ImplGetInputSequenceChecker() const
4350 : {
4351 0 : if ( !xISC.is() )
4352 : {
4353 0 : Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
4354 0 : xISC = i18n::InputSequenceChecker::create( xContext );
4355 : }
4356 0 : return xISC;
4357 : }
4358 :
4359 11022 : Color ImpEditEngine::GetAutoColor() const
4360 : {
4361 11022 : Color aColor = const_cast<ImpEditEngine*>(this)->GetColorConfig().GetColorValue( svtools::FONTCOLOR ).nColor;
4362 :
4363 11022 : if ( GetBackgroundColor() != COL_AUTO )
4364 : {
4365 3767 : if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
4366 7 : aColor = COL_WHITE;
4367 3760 : else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
4368 0 : aColor = COL_BLACK;
4369 : }
4370 :
4371 11022 : return aColor;
4372 : }
4373 :
4374 :
4375 0 : bool ImpEditEngine::ImplCalcAsianCompression(ContentNode* pNode,
4376 : TextPortion* pTextPortion, sal_Int32 nStartPos, sal_Int32* pDXArray,
4377 : sal_uInt16 n100thPercentFromMax, sal_Bool bManipulateDXArray)
4378 : {
4379 : DBG_ASSERT( GetAsianCompressionMode(), "ImplCalcAsianCompression - Why?" );
4380 : DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
4381 :
4382 : // Percent is 1/100 Percent...
4383 0 : if ( n100thPercentFromMax == 10000 )
4384 0 : pTextPortion->SetExtraInfos( NULL );
4385 :
4386 0 : bool bCompressed = false;
4387 :
4388 0 : if ( GetI18NScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
4389 : {
4390 0 : long nNewPortionWidth = pTextPortion->GetSize().Width();
4391 0 : sal_Int32 nPortionLen = pTextPortion->GetLen();
4392 0 : for ( sal_Int32 n = 0; n < nPortionLen; n++ )
4393 : {
4394 0 : sal_uInt8 nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
4395 :
4396 0 : sal_Bool bCompressPunctuation = ( nType == CHAR_PUNCTUATIONLEFT ) || ( nType == CHAR_PUNCTUATIONRIGHT );
4397 0 : sal_Bool bCompressKana = ( nType == CHAR_KANA ) && ( GetAsianCompressionMode() == text::CharacterCompressionType::PUNCTUATION_AND_KANA );
4398 :
4399 : // create Extra infos only if needed...
4400 0 : if ( bCompressPunctuation || bCompressKana )
4401 : {
4402 0 : if ( !pTextPortion->GetExtraInfos() )
4403 : {
4404 0 : ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
4405 0 : pTextPortion->SetExtraInfos( pExtraInfos );
4406 0 : pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
4407 0 : pExtraInfos->nAsianCompressionTypes = CHAR_NORMAL;
4408 : }
4409 0 : pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
4410 0 : pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
4411 :
4412 : long nOldCharWidth;
4413 0 : if ( (n+1) < nPortionLen )
4414 : {
4415 0 : nOldCharWidth = pDXArray[n];
4416 : }
4417 : else
4418 : {
4419 0 : if ( bManipulateDXArray )
4420 0 : nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
4421 : else
4422 0 : nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
4423 : }
4424 0 : nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
4425 :
4426 0 : long nCompress = 0;
4427 :
4428 0 : if ( bCompressPunctuation )
4429 : {
4430 0 : nCompress = nOldCharWidth / 2;
4431 : }
4432 : else // Kana
4433 : {
4434 0 : nCompress = nOldCharWidth / 10;
4435 : }
4436 :
4437 0 : if ( n100thPercentFromMax != 10000 )
4438 : {
4439 0 : nCompress *= n100thPercentFromMax;
4440 0 : nCompress /= 10000;
4441 : }
4442 :
4443 0 : if ( nCompress )
4444 : {
4445 0 : bCompressed = true;
4446 0 : nNewPortionWidth -= nCompress;
4447 0 : pTextPortion->GetExtraInfos()->bCompressed = sal_True;
4448 :
4449 :
4450 : // Special handling for rightpunctuation: For the 'compression' we must
4451 : // start the output before the normal char position....
4452 0 : if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
4453 : {
4454 0 : if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
4455 0 : pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
4456 :
4457 0 : if ( nType == CHAR_PUNCTUATIONRIGHT )
4458 : {
4459 : // If it's the first char, I must handle it in Paint()...
4460 0 : if ( n )
4461 : {
4462 : // -1: No entry for the last character
4463 0 : for ( sal_Int32 i = n-1; i < (nPortionLen-1); i++ )
4464 0 : pDXArray[i] -= nCompress;
4465 : }
4466 : else
4467 : {
4468 0 : pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = sal_True;
4469 0 : pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
4470 : }
4471 : }
4472 : else
4473 : {
4474 : // -1: No entry for the last character
4475 0 : for ( sal_Int32 i = n; i < (nPortionLen-1); i++ )
4476 0 : pDXArray[i] -= nCompress;
4477 : }
4478 : }
4479 : }
4480 : }
4481 : }
4482 :
4483 0 : if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
4484 0 : pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
4485 :
4486 0 : pTextPortion->GetSize().Width() = nNewPortionWidth;
4487 :
4488 0 : if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
4489 : {
4490 : // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
4491 0 : long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
4492 0 : nShrink *= n100thPercentFromMax;
4493 0 : nShrink /= 10000;
4494 0 : long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
4495 0 : if ( nNewWidth < pTextPortion->GetSize().Width() )
4496 0 : pTextPortion->GetSize().Width() = nNewWidth;
4497 : }
4498 : }
4499 0 : return bCompressed;
4500 : }
4501 :
4502 :
4503 0 : void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, long nRemainingWidth )
4504 : {
4505 0 : sal_Bool bFoundCompressedPortion = sal_False;
4506 0 : long nCompressed = 0;
4507 0 : std::vector<TextPortion*> aCompressedPortions;
4508 :
4509 0 : sal_Int32 nPortion = pLine->GetEndPortion();
4510 0 : TextPortion* pTP = pParaPortion->GetTextPortions()[ nPortion ];
4511 0 : while ( pTP && ( pTP->GetKind() == PORTIONKIND_TEXT ) )
4512 : {
4513 0 : if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
4514 : {
4515 0 : bFoundCompressedPortion = sal_True;
4516 0 : nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
4517 0 : aCompressedPortions.push_back(pTP);
4518 : }
4519 0 : pTP = ( nPortion > pLine->GetStartPortion() ) ? pParaPortion->GetTextPortions()[ --nPortion ] : NULL;
4520 : }
4521 :
4522 0 : if ( bFoundCompressedPortion )
4523 : {
4524 0 : long nCompressPercent = 0;
4525 0 : if ( nCompressed > nRemainingWidth )
4526 : {
4527 0 : nCompressPercent = nCompressed - nRemainingWidth;
4528 : DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
4529 0 : nCompressPercent *= 10000;
4530 0 : nCompressPercent /= nCompressed;
4531 : }
4532 :
4533 0 : for (size_t i = 0, n = aCompressedPortions.size(); i < n; ++i)
4534 : {
4535 0 : pTP = aCompressedPortions[i];
4536 0 : pTP->GetExtraInfos()->bCompressed = sal_False;
4537 0 : pTP->GetSize().Width() = pTP->GetExtraInfos()->nOrgWidth;
4538 0 : if ( nCompressPercent )
4539 : {
4540 0 : sal_Int32 nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
4541 0 : sal_Int32 nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
4542 : DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
4543 0 : sal_Int32* pDXArray = NULL;
4544 0 : if (!pLine->GetCharPosArray().empty())
4545 0 : pDXArray = &pLine->GetCharPosArray()[0]+( nTxtPortionStart-pLine->GetStart() );
4546 0 : if ( pTP->GetExtraInfos()->pOrgDXArray )
4547 0 : memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray, (pTP->GetLen()-1)*sizeof(sal_Int32) );
4548 0 : ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, (sal_uInt16)nCompressPercent, sal_True );
4549 : }
4550 : }
4551 0 : }
4552 0 : }
4553 :
4554 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|