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