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