summaryrefslogtreecommitdiff
path: root/editeng/source/editeng/impedit3.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'editeng/source/editeng/impedit3.cxx')
-rw-r--r--editeng/source/editeng/impedit3.cxx4677
1 files changed, 4677 insertions, 0 deletions
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
new file mode 100644
index 000000000000..dbc6767d1f91
--- /dev/null
+++ b/editeng/source/editeng/impedit3.cxx
@@ -0,0 +1,4677 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_editeng.hxx"
+
+#include <vcl/wrkwin.hxx>
+#include <vcl/dialog.hxx>
+#include <vcl/msgbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+
+#define _SVSTDARR_USHORTS
+#include <svl/svstdarr.hxx>
+
+#include <vcl/wrkwin.hxx>
+#include <editeng/adjitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/flditem.hxx>
+#include <impedit.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/txtrange.hxx>
+#include <editeng/cscoitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/numitem.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <svl/ctloptions.hxx>
+
+#include <editeng/forbiddencharacterstable.hxx>
+
+#include <unotools/localedatawrapper.hxx>
+
+#include <editeng/unolingu.hxx>
+
+#include <math.h>
+#include <vcl/svapp.hxx>
+#include <vcl/metric.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/text/CharacterCompressionType.hpp>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <i18npool/mslangid.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+using ::rtl::OUString;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+SV_DECL_VARARR_SORT( SortedPositions, sal_uInt32, 16, 8 )
+SV_IMPL_VARARR_SORT( SortedPositions, sal_uInt32 );
+
+#define CH_HYPH '-'
+
+#define RESDIFF 10
+
+#define WRONG_SHOW_MIN 5
+#define WRONG_SHOW_SMALL 11
+#define WRONG_SHOW_MEDIUM 15
+
+struct TabInfo
+{
+ BOOL bValid;
+
+ SvxTabStop aTabStop;
+ xub_StrLen nCharPos;
+ USHORT nTabPortion;
+ long nStartPosX;
+ long nTabPos;
+
+ TabInfo() { bValid = FALSE; }
+};
+
+Point Rotate( const Point& rPoint, short nOrientation, const Point& rOrigin )
+{
+ double nRealOrientation = nOrientation*F_PI1800;
+ double nCos = cos( nRealOrientation );
+ double nSin = sin( nRealOrientation );
+
+ Point aRotatedPos;
+ Point aTranslatedPos( rPoint );
+
+ // Translation
+ aTranslatedPos -= rOrigin;
+
+ // Rotation...
+ aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
+ aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
+ aTranslatedPos = aRotatedPos;
+
+ // Translation...
+ aTranslatedPos += rOrigin;
+ return aTranslatedPos;
+}
+
+BYTE GetCharTypeForCompression( xub_Unicode cChar )
+{
+ switch ( cChar )
+ {
+ case 0x3008: case 0x300A: case 0x300C: case 0x300E:
+ case 0x3010: case 0x3014: case 0x3016: case 0x3018:
+ case 0x301A: case 0x301D:
+ {
+ return CHAR_PUNCTUATIONRIGHT;
+ }
+ case 0x3001: case 0x3002: case 0x3009: case 0x300B:
+ case 0x300D: case 0x300F: case 0x3011: case 0x3015:
+ case 0x3017: case 0x3019: case 0x301B: case 0x301E:
+ case 0x301F:
+ {
+ return CHAR_PUNCTUATIONLEFT;
+ }
+ default:
+ {
+ return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? CHAR_KANA : CHAR_NORMAL;
+ }
+ }
+}
+
+void lcl_DrawRedLines(
+ OutputDevice* pOutDev,
+ long nFontHeight,
+ const Point& rPnt,
+ sal_uInt16 nIndex,
+ sal_uInt16 nMaxEnd,
+ const sal_Int32* pDXArray,
+ WrongList* pWrongs,
+ short nOrientation,
+ const Point& rOrigin,
+ BOOL bVertical,
+ BOOL bIsRightToLeft )
+{
+#ifndef SVX_LIGHT
+ // Aber nur, wenn Font nicht zu klein...
+ long nHght = pOutDev->LogicToPixel( Size( 0, nFontHeight ) ).Height();
+ if( WRONG_SHOW_MIN < nHght )
+ {
+ sal_uInt16 nStyle;
+ if( WRONG_SHOW_MEDIUM < nHght )
+ nStyle = WAVE_NORMAL;
+ else if( WRONG_SHOW_SMALL < nHght )
+ nStyle = WAVE_SMALL;
+ else
+ nStyle = WAVE_FLAT;
+
+ sal_uInt16 nEnd, nStart = nIndex;
+ sal_Bool bWrong = pWrongs->NextWrong( nStart, nEnd );
+ while ( bWrong )
+ {
+ if ( nStart >= nMaxEnd )
+ break;
+
+ if ( nStart < nIndex ) // Wurde korrigiert
+ nStart = nIndex;
+ if ( nEnd > nMaxEnd )
+ nEnd = nMaxEnd;
+ Point aPnt1( rPnt );
+ if ( bVertical && ( nStyle != WAVE_FLAT ) )
+ {
+ // VCL doesn't know that the text is vertical, and is manipulating
+ // the positions a little bit in y direction...
+ long nOnePixel = pOutDev->PixelToLogic( Size( 0, 1 ) ).Height();
+ long nCorrect = ( nStyle == WAVE_NORMAL ) ? 2*nOnePixel : nOnePixel;
+ aPnt1.Y() -= nCorrect;
+ aPnt1.X() -= nCorrect;
+ }
+ if ( nStart > nIndex )
+ {
+ if ( !bVertical )
+ {
+ // since for RTL portions rPnt is on the visual right end of the portion
+ // (i.e. at the start of the first RTL char) we need to subtract the offset
+ // for RTL portions...
+ aPnt1.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nStart - nIndex - 1 ];
+ }
+ else
+ aPnt1.Y() += pDXArray[ nStart - nIndex - 1 ];
+ }
+ Point aPnt2( rPnt );
+ DBG_ASSERT( nEnd > nIndex, "RedLine: aPnt2?" );
+ if ( !bVertical )
+ {
+ // since for RTL portions rPnt is on the visual right end of the portion
+ // (i.e. at the start of the first RTL char) we need to subtract the offset
+ // for RTL portions...
+ aPnt2.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nEnd - nIndex - 1 ];
+ }
+ else
+ aPnt2.Y() += pDXArray[ nEnd - nIndex - 1 ];
+ if ( nOrientation )
+ {
+ aPnt1 = Rotate( aPnt1, nOrientation, rOrigin );
+ aPnt2 = Rotate( aPnt2, nOrientation, rOrigin );
+ }
+
+ pOutDev->DrawWaveLine( aPnt1, aPnt2, nStyle );
+
+ nStart = nEnd+1;
+ if ( nEnd < nMaxEnd )
+ bWrong = pWrongs->NextWrong( nStart, nEnd );
+ else
+ bWrong = sal_False;
+ }
+ }
+#endif // !SVX_LIGHT
+}
+
+Point lcl_ImplCalcRotatedPos( Point rPos, Point rOrigin, double nSin, double nCos )
+{
+ Point aRotatedPos;
+ // Translation...
+ Point aTranslatedPos( rPos);
+ aTranslatedPos -= rOrigin;
+
+ aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
+ aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
+ aTranslatedPos = aRotatedPos;
+ // Translation...
+ aTranslatedPos += rOrigin;
+
+ return aTranslatedPos;
+}
+
+sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh ) // For Kashidas from sw/source/core/text/porlay.txt
+{
+ // Lam + Alef
+ return ( 0x644 == cCh && 0x627 == cNextCh ) ||
+ // Beh + Reh
+ ( 0x628 == cCh && 0x631 == cNextCh );
+}
+
+sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh ) // For Kashidas from sw/source/core/text/porlay.txt
+{
+ // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
+ sal_Bool bRet = 0x627 != cPrevCh && 0x62F != cPrevCh && 0x630 != cPrevCh &&
+ 0x631 != cPrevCh && 0x632 != cPrevCh && 0x648 != cPrevCh;
+
+ // check for ligatures cPrevChar + cChar
+ if ( bRet )
+ bRet = ! lcl_IsLigature( cPrevCh, cCh );
+
+ return bRet;
+}
+
+
+// ----------------------------------------------------------------------
+// class ImpEditEngine
+// ----------------------------------------------------------------------
+void ImpEditEngine::UpdateViews( EditView* pCurView )
+{
+ if ( !GetUpdateMode() || IsFormatting() || aInvalidRec.IsEmpty() )
+ return;
+
+ DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
+
+ for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews[nView];
+ DBG_CHKOBJ( pView, EditView, 0 );
+ pView->HideCursor();
+
+ Rectangle aClipRec( aInvalidRec );
+ Rectangle aVisArea( pView->GetVisArea() );
+ aClipRec.Intersection( aVisArea );
+
+ if ( !aClipRec.IsEmpty() )
+ {
+ // in Fensterkoordinaten umwandeln....
+ aClipRec = pView->pImpEditView->GetWindowPos( aClipRec );
+
+ if ( ( pView == pCurView ) )
+ Paint( pView->pImpEditView, aClipRec, sal_True );
+ else
+ pView->GetWindow()->Invalidate( aClipRec );
+ }
+ }
+
+ if ( pCurView )
+ {
+ sal_Bool bGotoCursor = pCurView->pImpEditView->DoAutoScroll();
+ pCurView->ShowCursor( bGotoCursor );
+ }
+
+ aInvalidRec = Rectangle();
+ CallStatusHdl();
+}
+
+IMPL_LINK( ImpEditEngine, OnlineSpellHdl, Timer *, EMPTYARG )
+{
+ if ( !Application::AnyInput( INPUT_KEYBOARD ) && GetUpdateMode() && IsFormatted() )
+ DoOnlineSpelling();
+ else
+ aOnlineSpellTimer.Start();
+
+ return 0;
+}
+
+IMPL_LINK_INLINE_START( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
+{
+ aIdleFormatter.ResetRestarts();
+
+ // #i97146# check if that view is still available
+ // else probably the idle format timer fired while we're already
+ // downing
+ EditView* pView = aIdleFormatter.GetView();
+ for( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ if( aEditViews[nView] == pView )
+ {
+ FormatAndUpdate( pView );
+ break;
+ }
+ }
+ return 0;
+}
+IMPL_LINK_INLINE_END( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
+
+void ImpEditEngine::CheckIdleFormatter()
+{
+ aIdleFormatter.ForceTimeout();
+ // Falls kein Idle, aber trotzdem nicht formatiert:
+ if ( !IsFormatted() )
+ FormatDoc();
+}
+
+void ImpEditEngine::FormatFullDoc()
+{
+ for ( sal_uInt16 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ GetParaPortions()[nPortion]->MarkSelectionInvalid( 0, GetParaPortions()[nPortion]->GetNode()->Len() );
+ FormatDoc();
+}
+
+void ImpEditEngine::FormatDoc()
+{
+ if ( !GetUpdateMode() || IsFormatting() )
+ return;
+
+ EnterBlockNotifications();
+
+ bIsFormatting = sal_True;
+
+ // Dann kann ich auch den Spell-Timer starten...
+ if ( GetStatus().DoOnlineSpelling() )
+ StartOnlineSpellTimer();
+
+ long nY = 0;
+ sal_Bool bGrow = sal_False;
+
+ Font aOldFont( GetRefDevice()->GetFont() );
+
+ // Hier schon, damit nicht jedesmal in CreateLines...
+ sal_Bool bMapChanged = ImpCheckRefMapMode();
+
+ aInvalidRec = Rectangle(); // leermachen
+ for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
+ {
+ ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
+ if ( pParaPortion->MustRepaint() || ( pParaPortion->IsInvalid() && pParaPortion->IsVisible() ) )
+ {
+ if ( pParaPortion->IsInvalid() )
+ {
+ sal_Bool bChangedByDerivedClass = GetEditEnginePtr()->FormattingParagraph( nPara );
+ if ( bChangedByDerivedClass )
+ {
+ pParaPortion->GetTextPortions().Reset();
+ pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
+ }
+ }
+ // bei MustRepaint() sollte keine Formatierung noetig sein!
+ // 23.1.95: Evtl. ist sie durch eine andere Aktion aber doch
+ // ungueltig geworden!
+// if ( pParaPortion->MustRepaint() || CreateLines( nPara ) )
+ if ( ( pParaPortion->MustRepaint() && !pParaPortion->IsInvalid() )
+ || CreateLines( nPara, nY ) )
+ {
+ if ( !bGrow && GetTextRanger() )
+ {
+ // Bei einer Aenderung der Hoehe muss alles weiter unten
+ // neu formatiert werden...
+ for ( sal_uInt16 n = nPara+1; n < GetParaPortions().Count(); n++ )
+ {
+ ParaPortion* pPP = GetParaPortions().GetObject( n );
+ pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
+ pPP->GetLines().Reset();
+ }
+ }
+ bGrow = sal_True;
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphHeightChanged( nPara );
+ pParaPortion->SetMustRepaint( sal_False );
+ }
+
+ // InvalidRec nur einmal setzen...
+ if ( aInvalidRec.IsEmpty() )
+ {
+ // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
+ long nWidth = Max( (long)1, ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) );
+ Range aInvRange( GetInvalidYOffsets( pParaPortion ) );
+ aInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
+ Size( nWidth, aInvRange.Len() ) );
+ }
+ else
+ {
+ aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
+ }
+ }
+ else if ( bGrow )
+ {
+ aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
+ }
+ nY += pParaPortion->GetHeight();
+ }
+
+ // Man kann auch durch UpdateMode An=>AUS=>AN in die Formatierung gelangen...
+ // Optimierung erst nach Vobis-Auslieferung aktivieren...
+// if ( !aInvalidRec.IsEmpty() )
+ {
+ sal_uInt32 nNewHeight = CalcTextHeight();
+ long nDiff = nNewHeight - nCurTextHeight;
+ if ( nDiff )
+ aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTHEIGHTCHANGED : EE_STAT_TEXTWIDTHCHANGED;
+ if ( nNewHeight < nCurTextHeight )
+ {
+ aInvalidRec.Bottom() = (long)Max( nNewHeight, nCurTextHeight );
+ if ( aInvalidRec.IsEmpty() )
+ {
+ aInvalidRec.Top() = 0;
+ // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
+ aInvalidRec.Left() = 0;
+ aInvalidRec.Right() = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
+ }
+ }
+
+ nCurTextHeight = nNewHeight;
+
+ if ( aStatus.AutoPageSize() )
+ CheckAutoPageSize();
+ else if ( nDiff )
+ {
+ for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews[nView];
+ ImpEditView* pImpView = pView->pImpEditView;
+ if ( pImpView->DoAutoHeight() )
+ {
+ Size aSz( pImpView->GetOutputArea().GetWidth(), nCurTextHeight );
+ if ( aSz.Height() > aMaxAutoPaperSize.Height() )
+ aSz.Height() = aMaxAutoPaperSize.Height();
+ else if ( aSz.Height() < aMinAutoPaperSize.Height() )
+ aSz.Height() = aMinAutoPaperSize.Height();
+ pImpView->ResetOutputArea( Rectangle(
+ pImpView->GetOutputArea().TopLeft(), aSz ) );
+ }
+ }
+ }
+ }
+
+ if ( aStatus.DoRestoreFont() )
+ GetRefDevice()->SetFont( aOldFont );
+ bIsFormatting = sal_False;
+ bFormatted = sal_True;
+
+ if ( bMapChanged )
+ GetRefDevice()->Pop();
+
+ CallStatusHdl(); // Falls Modified...
+
+ LeaveBlockNotifications();
+}
+
+sal_Bool ImpEditEngine::ImpCheckRefMapMode()
+{
+ sal_Bool bChange = sal_False;
+
+ if ( aStatus.DoFormat100() )
+ {
+ MapMode aMapMode( GetRefDevice()->GetMapMode() );
+ if ( aMapMode.GetScaleX().GetNumerator() != aMapMode.GetScaleX().GetDenominator() )
+ bChange = sal_True;
+ else if ( aMapMode.GetScaleY().GetNumerator() != aMapMode.GetScaleY().GetDenominator() )
+ bChange = sal_True;
+
+ if ( bChange )
+ {
+ Fraction Scale1( 1, 1 );
+ aMapMode.SetScaleX( Scale1 );
+ aMapMode.SetScaleY( Scale1 );
+ GetRefDevice()->Push();
+ GetRefDevice()->SetMapMode( aMapMode );
+ }
+ }
+
+ return bChange;
+}
+
+void ImpEditEngine::CheckAutoPageSize()
+{
+ Size aPrevPaperSize( GetPaperSize() );
+ if ( GetStatus().AutoPageWidth() )
+ aPaperSize.Width() = (long) !IsVertical() ? CalcTextWidth( TRUE ) : GetTextHeight();
+ if ( GetStatus().AutoPageHeight() )
+ aPaperSize.Height() = (long) !IsVertical() ? GetTextHeight() : CalcTextWidth( TRUE );
+
+ SetValidPaperSize( aPaperSize ); //Min, Max beruecksichtigen
+
+ if ( aPaperSize != aPrevPaperSize )
+ {
+ if ( ( !IsVertical() && ( aPaperSize.Width() != aPrevPaperSize.Width() ) )
+ || ( IsVertical() && ( aPaperSize.Height() != aPrevPaperSize.Height() ) ) )
+ {
+ // Falls davor zentriert/rechts oder Tabs...
+ aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTWIDTHCHANGED : EE_STAT_TEXTHEIGHTCHANGED;
+ for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
+ {
+ // Es brauchen nur Absaetze neu formatiert werden,
+ // die nicht linksbuendig sind.
+ // Die Hoehe kann sich hier nicht mehr aendern.
+ ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
+ ContentNode* pNode = pParaPortion->GetNode();
+ SvxAdjust eJustification = GetJustification( nPara );
+ if ( eJustification != SVX_ADJUST_LEFT )
+ {
+ pParaPortion->MarkSelectionInvalid( 0, pNode->Len() );
+ CreateLines( nPara, 0 ); // 0: Bei AutoPageSize kein TextRange!
+ }
+ }
+ }
+
+ Size aInvSize = aPaperSize;
+ if ( aPaperSize.Width() < aPrevPaperSize.Width() )
+ aInvSize.Width() = aPrevPaperSize.Width();
+ if ( aPaperSize.Height() < aPrevPaperSize.Height() )
+ aInvSize.Height() = aPrevPaperSize.Height();
+
+ Size aSz( aInvSize );
+ if ( IsVertical() )
+ {
+ aSz.Width() = aInvSize.Height();
+ aSz.Height() = aInvSize.Width();
+ }
+ aInvalidRec = Rectangle( Point(), aSz );
+
+
+ for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews[nView];
+ pView->pImpEditView->RecalcOutputArea();
+ }
+ }
+}
+
+static sal_Int32 ImplCalculateFontIndependentLineSpacing( const sal_Int32 nFontHeight )
+{
+ return ( nFontHeight * 12 ) / 10; // + 20%
+}
+
+sal_Bool ImpEditEngine::CreateLines( USHORT nPara, sal_uInt32 nStartPosY )
+{
+ ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
+
+ // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
+ DBG_ASSERT( pParaPortion->GetNode(), "Portion ohne Node in CreateLines" );
+ DBG_ASSERT( pParaPortion->IsVisible(), "Unsichtbare Absaetze nicht formatieren!" );
+ DBG_ASSERT( pParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
+
+ BOOL bProcessingEmptyLine = ( pParaPortion->GetNode()->Len() == 0 );
+ BOOL bEmptyNodeWithPolygon = ( pParaPortion->GetNode()->Len() == 0 ) && GetTextRanger();
+
+ // ---------------------------------------------------------------
+ // Schnelle Sonderbehandlung fuer leere Absaetze...
+ // ---------------------------------------------------------------
+ if ( ( pParaPortion->GetNode()->Len() == 0 ) && !GetTextRanger() )
+ {
+ // schnelle Sonderbehandlung...
+ if ( pParaPortion->GetTextPortions().Count() )
+ pParaPortion->GetTextPortions().Reset();
+ if ( pParaPortion->GetLines().Count() )
+ pParaPortion->GetLines().Reset();
+ CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
+ return FinishCreateLines( pParaPortion );
+ }
+
+ // ---------------------------------------------------------------
+ // Initialisierung......
+ // ---------------------------------------------------------------
+
+ // Immer fuer 100% formatieren:
+ sal_Bool bMapChanged = ImpCheckRefMapMode();
+
+ if ( pParaPortion->GetLines().Count() == 0 )
+ {
+ EditLine* pL = new EditLine;
+ pParaPortion->GetLines().Insert( pL, 0 );
+ }
+
+ // ---------------------------------------------------------------
+ // Absatzattribute holen......
+ // ---------------------------------------------------------------
+ ContentNode* const pNode = pParaPortion->GetNode();
+
+ BOOL bRightToLeftPara = IsRightToLeft( nPara );
+
+ SvxAdjust eJustification = GetJustification( nPara );
+ sal_Bool bHyphenatePara = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HYPHENATE )).GetValue();
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nMinLabelWidth = 0;
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pNode, &nSpaceBefore, &nMinLabelWidth );
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pNode );
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&) pNode->GetContentAttribs().GetItem( EE_PARA_SBL );
+ const BOOL bScriptSpace = ((const SvxScriptSpaceItem&) pNode->GetContentAttribs().GetItem( EE_PARA_ASIANCJKSPACING )).GetValue();
+
+// const sal_uInt16 nInvalidEnd = ( pParaPortion->GetInvalidDiff() > 0 )
+// ? pParaPortion->GetInvalidPosStart() + pParaPortion->GetInvalidDiff()
+// : pNode->Len();
+ const short nInvalidDiff = pParaPortion->GetInvalidDiff();
+ const sal_uInt16 nInvalidStart = pParaPortion->GetInvalidPosStart();
+ const sal_uInt16 nInvalidEnd = nInvalidStart + Abs( nInvalidDiff );
+
+ sal_Bool bQuickFormat = sal_False;
+ if ( !bEmptyNodeWithPolygon && !HasScriptType( nPara, i18n::ScriptType::COMPLEX ) )
+ {
+ if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff > 0 ) &&
+ ( pNode->Search( CH_FEATURE, nInvalidStart ) > nInvalidEnd ) )
+ {
+ bQuickFormat = sal_True;
+ }
+ else if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
+ {
+ // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
+ sal_uInt16 nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!!
+ sal_uInt16 nEnd = nStart - nInvalidDiff; // neg.
+ bQuickFormat = sal_True;
+ sal_uInt16 nPos = 0;
+ sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
+ for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
+ {
+ // Es darf kein Start/Ende im geloeschten Bereich liegen.
+ TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
+ nPos = nPos + pTP->GetLen();
+ if ( ( nPos > nStart ) && ( nPos < nEnd ) )
+ {
+ bQuickFormat = sal_False;
+ break;
+ }
+ }
+ }
+ }
+
+ // SW disables TEXT_LAYOUT_COMPLEX_DISABLED, so maybe I have to enable it...
+
+ // #114278# Saving both layout mode and language (since I'm
+ // potentially changing both)
+
+ GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
+
+ ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
+
+ sal_uInt16 nRealInvalidStart = nInvalidStart;
+
+ if ( bEmptyNodeWithPolygon )
+ {
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pParaPortion->GetTextPortions().Reset();
+ pParaPortion->GetTextPortions().Insert( pDummyPortion, 0 );
+ }
+ else if ( bQuickFormat )
+ {
+ // schnellere Methode:
+ RecalcTextPortion( pParaPortion, nInvalidStart, nInvalidDiff );
+ }
+ else // nRealInvalidStart kann vor InvalidStart liegen, weil Portions geloescht....
+ {
+ CreateTextPortions( pParaPortion, nRealInvalidStart );
+ }
+
+
+ // ---------------------------------------------------------------
+ // Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
+ // Zeilen flaggen => nicht removen !
+ // ---------------------------------------------------------------
+
+ sal_uInt16 nLine = pParaPortion->GetLines().Count()-1;
+ for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
+ {
+ EditLine* pLine = pParaPortion->GetLines().GetObject( nL );
+ if ( pLine->GetEnd() > nRealInvalidStart ) // nicht nInvalidStart!
+ {
+ nLine = nL;
+ break;
+ }
+ pLine->SetValid();
+ }
+ // Eine Zeile davor beginnen...
+ // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
+ if ( nLine && ( !pParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->Len() ) || ( nInvalidDiff <= 0 ) ) )
+ nLine--;
+
+ EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
+
+ static Rectangle aZeroArea = Rectangle( Point(), Point() );
+ Rectangle aBulletArea( aZeroArea );
+ if ( !nLine )
+ {
+ aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
+ if ( aBulletArea.Right() > 0 )
+ pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
+ else
+ pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
+ }
+
+ // ---------------------------------------------------------------
+ // Ab hier alle Zeilen durchformatieren...
+ // ---------------------------------------------------------------
+ sal_uInt16 nDelFromLine = 0xFFFF;
+ sal_Bool bLineBreak = sal_False;
+
+ sal_uInt16 nIndex = pLine->GetStart();
+ EditLine aSaveLine( *pLine );
+ SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
+
+ sal_Bool bCalcCharPositions = sal_True;
+ sal_Int32* pBuf = new sal_Int32[ pNode->Len() ];
+
+ sal_Bool bSameLineAgain = sal_False; // Fuer TextRanger, wenn sich die Hoehe aendert.
+ TabInfo aCurrentTab;
+
+ BOOL bForceOneRun = bEmptyNodeWithPolygon;
+ BOOL bCompressedChars = FALSE;
+
+ while ( ( nIndex < pNode->Len() ) || bForceOneRun )
+ {
+ bForceOneRun = FALSE;
+
+ sal_Bool bEOL = sal_False;
+ sal_Bool bEOC = sal_False;
+ sal_uInt16 nPortionStart = 0;
+ sal_uInt16 nPortionEnd = 0;
+
+ long nStartX = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
+ if ( nIndex == 0 )
+ {
+ long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
+ nStartX += nFI;
+
+ if ( !nLine && ( pParaPortion->GetBulletX() > nStartX ) )
+ {
+// TL_NFLR nStartX += nFI; // Vielleicht reicht der LI?
+// TL_NFLR if ( pParaPortion->GetBulletX() > nStartX )
+ nStartX = pParaPortion->GetBulletX();
+ }
+ }
+
+ long nMaxLineWidth;
+ if ( !IsVertical() )
+ nMaxLineWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : aPaperSize.Width();
+ else
+ nMaxLineWidth = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : aPaperSize.Height();
+
+ nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
+ nMaxLineWidth -= nStartX;
+
+ // Wenn PaperSize == long_max, kann ich keinen neg. Erstzeileneinzug
+ // abziehen (Overflow)
+ if ( ( nMaxLineWidth < 0 ) && ( nStartX < 0 ) )
+ nMaxLineWidth = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) - GetXValue( rLRItem.GetRight() );
+
+ // Wenn jetzt noch kleiner 0, kann es nur der rechte Rand sein.
+ if ( nMaxLineWidth <= 0 )
+ nMaxLineWidth = 1;
+
+ // Problem: Da eine Zeile _vor_ der ungueltigen Position mit der
+ // Formatierung begonnen wird, werden hier leider auch die Positionen
+ // neu bestimmt...
+ // Loesungsansatz:
+ // Die Zeile davor kann nur groesser werden, nicht kleiner
+ // => ...
+ if ( bCalcCharPositions )
+ pLine->GetCharPosArray().Remove( 0, pLine->GetCharPosArray().Count() );
+
+ sal_uInt16 nTmpPos = nIndex;
+ sal_uInt16 nTmpPortion = pLine->GetStartPortion();
+ long nTmpWidth = 0;
+ long nXWidth = nMaxLineWidth;
+ if ( nXWidth <= nTmpWidth ) // while muss 1x durchlaufen werden
+ nXWidth = nTmpWidth+1;
+
+ SvLongsPtr pTextRanges = 0;
+ long nTextExtraYOffset = 0;
+ long nTextXOffset = 0;
+ long nTextLineHeight = 0;
+ if ( GetTextRanger() )
+ {
+ GetTextRanger()->SetVertical( IsVertical() );
+
+ long nTextY = nStartPosY + GetEditCursor( pParaPortion, pLine->GetStart() ).Top();
+ if ( !bSameLineAgain )
+ {
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+
+ if ( IsFixedCellHeight() )
+ nTextLineHeight = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
+ else
+ nTextLineHeight = aTmpFont.GetPhysTxtSize( GetRefDevice(), String() ).Height();
+ // Metriken koennen groesser sein
+ FormatterFontMetric aTempFormatterMetrics;
+ RecalcFormatterFontMetrics( aTempFormatterMetrics, aTmpFont );
+ sal_uInt16 nLineHeight = aTempFormatterMetrics.GetHeight();
+ if ( nLineHeight > nTextLineHeight )
+ nTextLineHeight = nLineHeight;
+ }
+ else
+ nTextLineHeight = pLine->GetHeight();
+
+ nXWidth = 0;
+ while ( !nXWidth )
+ {
+ long nYOff = nTextY + nTextExtraYOffset;
+ long nYDiff = nTextLineHeight;
+ if ( IsVertical() )
+ {
+ long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
+ nYOff = nMaxPolygonX-nYOff;
+ nYDiff = -nTextLineHeight;
+ }
+ pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
+ DBG_ASSERT( pTextRanges, "GetTextRanges?!" );
+ long nMaxRangeWidth = 0;
+ // Den breitesten Bereich verwenden...
+ // Der breiteste Bereich koennte etwas verwirren, also
+ // generell den ersten. Am besten mal richtig mit Luecken.
+// for ( sal_uInt16 n = 0; n < pTextRanges->Count(); )
+ if ( pTextRanges->Count() )
+ {
+ sal_uInt16 n = 0;
+ long nA = pTextRanges->GetObject( n++ );
+ long nB = pTextRanges->GetObject( n++ );
+ DBG_ASSERT( nA <= nB, "TextRange verdreht?" );
+ long nW = nB - nA;
+ if ( nW > nMaxRangeWidth )
+ {
+ nMaxRangeWidth = nW;
+ nTextXOffset = nA;
+ }
+ }
+ nXWidth = nMaxRangeWidth;
+ if ( nXWidth )
+ nMaxLineWidth = nXWidth - nStartX - GetXValue( rLRItem.GetRight() );
+ else
+ {
+ // Weiter unten im Polygon versuchen.
+ // Unterhalb des Polygons die Paperbreite verwenden.
+ nTextExtraYOffset += Max( (long)(nTextLineHeight / 10), (long)1 );
+ if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
+ {
+ nXWidth = !IsVertical() ? GetPaperSize().Width() : GetPaperSize().Height();
+ if ( !nXWidth ) // AutoPaperSize
+ nXWidth = 0x7FFFFFFF;
+ }
+ }
+ }
+ }
+
+ // Portion suchen, die nicht mehr in Zeile passt....
+ TextPortion* pPortion = 0;
+ sal_Bool bBrokenLine = sal_False;
+ bLineBreak = sal_False;
+ EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() );
+ while ( ( nTmpWidth < nXWidth ) && !bEOL && ( nTmpPortion < pParaPortion->GetTextPortions().Count() ) )
+ {
+ nPortionStart = nTmpPos;
+ pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ if ( pPortion->GetKind() == PORTIONKIND_HYPHENATOR )
+ {
+ // Portion wegschmeissen, ggf. die davor korrigieren, wenn
+ // die Hyph-Portion ein Zeichen geschluckt hat...
+ pParaPortion->GetTextPortions().Remove( nTmpPortion );
+ if ( nTmpPortion && pPortion->GetLen() )
+ {
+ nTmpPortion--;
+ TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
+ nTmpWidth -= pPrev->GetSize().Width();
+ nTmpPos = nTmpPos - pPrev->GetLen();
+ pPrev->SetLen( pPrev->GetLen() + pPortion->GetLen() );
+ pPrev->GetSize().Width() = (-1);
+ }
+ delete pPortion;
+ DBG_ASSERT( nTmpPortion < pParaPortion->GetTextPortions().Count(), "Keine Portion mehr da!" );
+ pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ }
+ DBG_ASSERT( pPortion->GetKind() != PORTIONKIND_HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
+ DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Leere Portion in CreateLines ?!" );
+ if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
+ {
+ sal_uInt16 nWhich = pNextFeature->GetItem()->Which();
+ switch ( nWhich )
+ {
+ case EE_FEATURE_TAB:
+ {
+ long nOldTmpWidth = nTmpWidth;
+
+ // Tab-Pos suchen...
+ long nCurPos = nTmpWidth+nStartX;
+// nCurPos -= rLRItem.GetTxtLeft(); // Tabs relativ zu LI
+ // Skalierung rausrechnen
+ if ( aStatus.DoStretch() && ( nStretchX != 100 ) )
+ nCurPos = nCurPos*100/nStretchX;
+
+ short nAllSpaceBeforeText = static_cast< short >(rLRItem.GetTxtLeft()/* + rLRItem.GetTxtLeft()*/ + nSpaceBeforeAndMinLabelWidth);
+ aCurrentTab.aTabStop = pNode->GetContentAttribs().FindTabStop( nCurPos - nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/, aEditDoc.GetDefTab() );
+ aCurrentTab.nTabPos = GetXValue( (long) ( aCurrentTab.aTabStop.GetTabPos() + nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/ ) );
+ aCurrentTab.bValid = FALSE;
+
+ // Switch direction in R2L para...
+ if ( bRightToLeftPara )
+ {
+ if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
+ aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_LEFT;
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_LEFT )
+ aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_RIGHT;
+ }
+
+ if ( ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT ) ||
+ ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER ) ||
+ ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL ) )
+ {
+ // Bei LEFT/DEFAULT wird dieses Tab nicht mehr betrachtet.
+ aCurrentTab.bValid = TRUE;
+ aCurrentTab.nStartPosX = nTmpWidth;
+ aCurrentTab.nCharPos = nTmpPos;
+ aCurrentTab.nTabPortion = nTmpPortion;
+ }
+
+ pPortion->GetKind() = PORTIONKIND_TAB;
+ pPortion->SetExtraValue( aCurrentTab.aTabStop.GetFill() );
+ pPortion->GetSize().Width() = aCurrentTab.nTabPos - (nTmpWidth+nStartX);
+
+ // #90520# Height needed...
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ pPortion->GetSize().Height() = aTmpFont.QuickGetTextSize( GetRefDevice(), String(), 0, 0, NULL ).Height();
+
+ DBG_ASSERT( pPortion->GetSize().Width() >= 0, "Tab falsch berechnet!" );
+
+ nTmpWidth = aCurrentTab.nTabPos-nStartX;
+
+ // Wenn dies das erste Token in der Zeile ist,
+ // und nTmpWidth > aPaperSize.Width, habe ich eine
+ // Endlos-Schleife!
+ if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
+ {
+ // Aber was jetzt ?
+ // Tab passend machen
+ pPortion->GetSize().Width() = nXWidth-nOldTmpWidth;
+ nTmpWidth = nXWidth-1;
+ bEOL = sal_True;
+ bBrokenLine = sal_True;
+ }
+ pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
+ bCompressedChars = FALSE;
+ }
+ break;
+ case EE_FEATURE_LINEBR:
+ {
+ DBG_ASSERT( pPortion, "?!" );
+ pPortion->GetSize().Width() = 0;
+ bEOL = sal_True;
+ bLineBreak = sal_True;
+ pPortion->GetKind() = PORTIONKIND_LINEBREAK;
+ bCompressedChars = FALSE;
+ pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
+ }
+ break;
+ case EE_FEATURE_FIELD:
+ {
+// long nCurWidth = nTmpWidth;
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ sal_Unicode cChar = 0; // later: NBS?
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+
+ String aFieldValue = cChar ? String(cChar) : ((EditCharAttribField*)pNextFeature)->GetFieldValue();
+ if ( bCalcCharPositions || !pPortion->HasValidSize() )
+ {
+ pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), aFieldValue, 0, aFieldValue.Len(), 0 );
+ // Damit kein Scrollen bei ueberlangen Feldern
+ if ( pPortion->GetSize().Width() > nXWidth )
+ pPortion->GetSize().Width() = nXWidth;
+ }
+ nTmpWidth += pPortion->GetSize().Width();
+ pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
+ pPortion->GetKind() = cChar ? PORTIONKIND_TEXT : PORTIONKIND_FIELD;
+ // Wenn dies das erste Token in der Zeile ist,
+ // und nTmpWidth > aPaperSize.Width, habe ich eine
+ // Endlos-Schleife!
+ if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
+ {
+ nTmpWidth = nXWidth-1;
+ bEOL = sal_True;
+ bBrokenLine = sal_True;
+ }
+ // Compression in Fields????
+ // I think this could be a little bit difficult and is not very usefull
+ bCompressedChars = FALSE;
+ }
+ break;
+ default: DBG_ERROR( "Was fuer ein Feature ?" );
+ }
+ pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
+ }
+ else
+ {
+ DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+
+ if ( bCalcCharPositions || !pPortion->HasValidSize() )
+ {
+ pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, pPortion->GetLen(), pBuf );
+
+ // #i9050# Do Kerning also behind portions...
+ if ( ( aTmpFont.GetFixKerning() > 0 ) && ( ( nTmpPos + pPortion->GetLen() ) < pNode->Len() ) )
+ pPortion->GetSize().Width() += aTmpFont.GetFixKerning();
+ if ( IsFixedCellHeight() )
+ pPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
+ }
+ if ( bCalcCharPositions )
+ {
+ sal_uInt16 nLen = pPortion->GetLen();
+ // Es wird am Anfang generell das Array geplaettet
+ // => Immer einfach schnelles insert.
+ sal_uInt16 nPos = nTmpPos - pLine->GetStart();
+ pLine->GetCharPosArray().Insert( pBuf, nLen, nPos );
+ }
+
+ // And now check for Compression:
+ if ( pPortion->GetLen() && GetAsianCompressionMode() )
+ bCompressedChars |= ImplCalcAsianCompression( pNode, pPortion, nTmpPos, (sal_Int32*)pLine->GetCharPosArray().GetData() + (nTmpPos-pLine->GetStart()), 10000, FALSE );
+
+ nTmpWidth += pPortion->GetSize().Width();
+
+ pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
+
+ USHORT _nPortionEnd = nTmpPos + pPortion->GetLen();
+ if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
+ {
+ BOOL bAllow = FALSE;
+ USHORT nScriptTypeLeft = GetScriptType( EditPaM( pNode, _nPortionEnd ) );
+ USHORT nScriptTypeRight = GetScriptType( EditPaM( pNode, _nPortionEnd+1 ) );
+ if ( ( nScriptTypeLeft == i18n::ScriptType::ASIAN ) || ( nScriptTypeRight == i18n::ScriptType::ASIAN ) )
+ bAllow = TRUE;
+
+ // No spacing within L2R/R2L nesting
+ if ( bAllow )
+ {
+ long nExtraSpace = pPortion->GetSize().Height()/5;
+ nExtraSpace = GetXValue( nExtraSpace );
+ pPortion->GetSize().Width() += nExtraSpace;
+ nTmpWidth += nExtraSpace;
+ }
+ }
+ }
+
+ if ( aCurrentTab.bValid && ( nTmpPortion != aCurrentTab.nTabPortion ) )
+ {
+ long nWidthAfterTab = 0;
+ for ( USHORT n = aCurrentTab.nTabPortion+1; n <= nTmpPortion; n++ )
+ {
+ TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( n );
+ nWidthAfterTab += pTP->GetSize().Width();
+ }
+ long nW = nWidthAfterTab; // Length before tab position
+ if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
+ {
+// nW = nWidthAfterTab;
+ }
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER )
+ {
+ nW = nWidthAfterTab/2;
+ }
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL )
+ {
+// nW = nWidthAfterTab;
+ String aText = GetSelected( EditSelection( EditPaM( pParaPortion->GetNode(), nTmpPos ),
+ EditPaM( pParaPortion->GetNode(), nTmpPos + pPortion->GetLen() ) ) );
+ USHORT nDecPos = aText.Search( aCurrentTab.aTabStop.GetDecimal() );
+ if ( nDecPos != STRING_NOTFOUND )
+ {
+ nW -= pParaPortion->GetTextPortions().GetObject( nTmpPortion )->GetSize().Width();
+ nW += aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, nDecPos, NULL ).Width();
+ aCurrentTab.bValid = FALSE;
+ }
+ }
+ else
+ {
+ DBG_ERROR( "CreateLines: Tab not handled!" );
+ }
+ long nMaxW = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nStartX;
+ if ( nW >= nMaxW )
+ {
+ nW = nMaxW;
+ aCurrentTab.bValid = FALSE;
+ }
+ TextPortion* pTabPortion = pParaPortion->GetTextPortions().GetObject( aCurrentTab.nTabPortion );
+ pTabPortion->GetSize().Width() = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nW - nStartX;
+ nTmpWidth = aCurrentTab.nStartPosX + pTabPortion->GetSize().Width() + nWidthAfterTab;
+ }
+
+ nTmpPos = nTmpPos + pPortion->GetLen();
+ nPortionEnd = nTmpPos;
+ nTmpPortion++;
+ if ( aStatus.OneCharPerLine() )
+ bEOL = sal_True;
+ }
+
+ DBG_ASSERT( pPortion, "no portion!?" );
+
+ aCurrentTab.bValid = FALSE;
+
+ // das war evtl. eine Portion zu weit:
+ sal_Bool bFixedEnd = sal_False;
+ if ( aStatus.OneCharPerLine() )
+ {
+ // Zustand vor Portion: ( bis auf nTmpWidth )
+ nPortionEnd = nTmpPos;
+ nTmpPos -= pPortion ? pPortion->GetLen() : 0;
+ nPortionStart = nTmpPos;
+ nTmpPortion--;
+
+ bEOL = sal_True;
+ bEOC = sal_False;
+
+ // Und jetzt genau ein Zeichen:
+ nTmpPos++;
+ nTmpPortion++;
+ nPortionEnd = nTmpPortion;
+ // Eine Nicht-Feature-Portion muss gebrochen werden
+ if ( pPortion->GetLen() > 1 )
+ {
+ DBG_ASSERT( pPortion && (pPortion->GetKind() == PORTIONKIND_TEXT), "Len>1, aber keine TextPortion?" );
+ nTmpWidth -= pPortion ? pPortion->GetSize().Width() : 0;
+ sal_uInt16 nP = SplitTextPortion( pParaPortion, nTmpPos, pLine );
+ TextPortion* p = pParaPortion->GetTextPortions().GetObject( nP );
+ DBG_ASSERT( p, "Portion ?!" );
+ nTmpWidth += p->GetSize().Width();
+ }
+ }
+ else if ( nTmpWidth >= nXWidth )
+ {
+ nPortionEnd = nTmpPos;
+ nTmpPos -= pPortion ? pPortion->GetLen() : 0;
+ nPortionStart = nTmpPos;
+ nTmpPortion--;
+ bEOL = sal_False;
+ bEOC = sal_False;
+ if( pPortion ) switch ( pPortion->GetKind() )
+ {
+ case PORTIONKIND_TEXT:
+ {
+ nTmpWidth -= pPortion->GetSize().Width();
+ }
+ break;
+ case PORTIONKIND_FIELD:
+ case PORTIONKIND_TAB:
+ {
+ nTmpWidth -= pPortion->GetSize().Width();
+ bEOL = sal_True;
+ bFixedEnd = sal_True;
+ }
+ break;
+ default:
+ {
+ // Ein Feature wird nicht umgebrochen:
+ DBG_ASSERT( ( pPortion->GetKind() == PORTIONKIND_LINEBREAK ), "Was fuer ein Feature ?" );
+ bEOL = sal_True;
+ bFixedEnd = sal_True;
+ }
+ }
+ }
+ else
+ {
+ bEOL = sal_True;
+ bEOC = sal_True;
+ pLine->SetEnd( nPortionEnd );
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
+ pLine->SetEndPortion( (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1 );
+ }
+
+ if ( aStatus.OneCharPerLine() )
+ {
+ pLine->SetEnd( nPortionEnd );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ }
+ else if ( bFixedEnd )
+ {
+ pLine->SetEnd( nPortionStart );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ }
+ else if ( bLineBreak || bBrokenLine )
+ {
+ pLine->SetEnd( nPortionStart+1 );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen?
+ }
+ else if ( !bEOL )
+ {
+ DBG_ASSERT( pPortion && ((nPortionEnd-nPortionStart) == pPortion->GetLen()), "Doch eine andere Portion?!" );
+ long nRemainingWidth = nMaxLineWidth - nTmpWidth;
+ sal_Bool bCanHyphenate = ( aTmpFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL );
+ if ( bCompressedChars && pPortion && ( pPortion->GetLen() > 1 ) && pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
+ {
+ // I need the manipulated DXArray for determining the break postion...
+ ImplCalcAsianCompression( pNode, pPortion, nPortionStart, const_cast<sal_Int32*>(( pLine->GetCharPosArray().GetData() + (nPortionStart-pLine->GetStart()) )), 10000, TRUE );
+ }
+ if( pPortion )
+ ImpBreakLine( pParaPortion, pLine, pPortion, nPortionStart,
+ nRemainingWidth, bCanHyphenate && bHyphenatePara );
+ }
+
+ // ------------------------------------------------------------------
+ // Zeile fertig => justieren
+ // ------------------------------------------------------------------
+
+ // CalcTextSize sollte besser durch ein kontinuierliches
+ // Registrieren ersetzt werden !
+ Size aTextSize = pLine->CalcTextSize( *pParaPortion );
+
+ if ( aTextSize.Height() == 0 )
+ {
+ SeekCursor( pNode, pLine->GetStart()+1, aTmpFont );
+ aTmpFont.SetPhysFont( pRefDev );
+ ImplInitDigitMode( pRefDev, 0, 0, 0, aTmpFont.GetLanguage() );
+
+ if ( IsFixedCellHeight() )
+ aTextSize.Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
+ else
+ aTextSize.Height() = aTmpFont.GetPhysTxtSize( pRefDev, String() ).Height();
+ pLine->SetHeight( (sal_uInt16)aTextSize.Height() );
+ }
+
+ // Die Fontmetriken koennen nicht kontinuierlich berechnet werden,
+ // wenn der Font sowieso eingestellt ist, weil ggf. ein grosser Font
+ // erst nach dem Umbrechen ploetzlich in der naechsten Zeile landet
+ // => Font-Metriken zu gross.
+ FormatterFontMetric aFormatterMetrics;
+ sal_uInt16 nTPos = pLine->GetStart();
+ for ( sal_uInt16 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
+ {
+ TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nP );
+ // #95819# problem with hard font height attribute, when everthing but the line break has this attribute
+ if ( pTP->GetKind() != PORTIONKIND_LINEBREAK )
+ {
+ SeekCursor( pNode, nTPos+1, aTmpFont );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+ RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
+ }
+ nTPos = nTPos + pTP->GetLen();
+ }
+ sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
+ if ( nLineHeight > pLine->GetHeight() )
+ pLine->SetHeight( nLineHeight );
+ pLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
+
+ bSameLineAgain = sal_False;
+ if ( GetTextRanger() && ( pLine->GetHeight() > nTextLineHeight ) )
+ {
+ // Nochmal mit der anderen Groesse aufsetzen!
+ bSameLineAgain = sal_True;
+ }
+
+
+ if ( !bSameLineAgain && !aStatus.IsOutliner() )
+ {
+ if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
+ {
+ sal_uInt16 nMinHeight = GetYValue( rLSItem.GetLineHeight() );
+ sal_uInt16 nTxtHeight = pLine->GetHeight();
+ if ( nTxtHeight < nMinHeight )
+ {
+ // Der Ascent muss um die Differenz angepasst werden:
+ long nDiff = nMinHeight - nTxtHeight;
+ pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff) );
+ pLine->SetHeight( nMinHeight, nTxtHeight );
+ }
+ }
+ else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
+ {
+ if ( nPara || IsFixedCellHeight() || pLine->GetStartPortion() ) // Nicht die aller erste Zeile
+ {
+ // #100508# There are documents with PropLineSpace 0, why?
+ // (cmc: re above question :-) such documents can be seen by importing a .ppt
+ if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
+ {
+ sal_uInt16 nTxtHeight = pLine->GetHeight();
+ sal_Int32 nH = nTxtHeight;
+ nH *= rLSItem.GetPropLineSpace();
+ nH /= 100;
+ // Der Ascent muss um die Differenz angepasst werden:
+ long nDiff = pLine->GetHeight() - nH;
+ if ( nDiff > pLine->GetMaxAscent() )
+ nDiff = pLine->GetMaxAscent();
+ pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() - nDiff) );
+ pLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
+ }
+ }
+ }
+ }
+
+
+ // #80582# - Bullet should not influence line height
+// if ( !nLine )
+// {
+// long nBulletHeight = aBulletArea.GetHeight();
+// if ( nBulletHeight > (long)pLine->GetHeight() )
+// {
+// long nDiff = nBulletHeight - (long)pLine->GetHeight();
+// // nDiff auf oben und unten verteilen.
+// pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff/2) );
+// pLine->SetHeight( (sal_uInt16)nBulletHeight );
+// }
+// }
+
+ if ( ( !IsVertical() && aStatus.AutoPageWidth() ) ||
+ ( IsVertical() && aStatus.AutoPageHeight() ) )
+ {
+ // Wenn die Zeile in die aktuelle Papierbreite passt, muss
+ // diese Breite fuer die Ausrichting verwendet werden.
+ // Wenn sie nicht passt oder sie die Papierbreite aendert,
+ // wird bei Justification != LEFT sowieso noch mal formatiert.
+ long nMaxLineWidthFix = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() )
+ - GetXValue( rLRItem.GetRight() ) - nStartX;
+ if ( aTextSize.Width() < nMaxLineWidthFix )
+ nMaxLineWidth = nMaxLineWidthFix;
+ }
+
+ if ( bCompressedChars )
+ {
+ long nRemainingWidth = nMaxLineWidth - aTextSize.Width();
+ if ( nRemainingWidth > 0 )
+ {
+ ImplExpandCompressedPortions( pLine, pParaPortion, nRemainingWidth );
+ aTextSize = pLine->CalcTextSize( *pParaPortion );
+ }
+ }
+
+ if ( pLine->IsHangingPunctuation() )
+ {
+ // Width from HangingPunctuation was set to 0 in ImpBreakLine,
+ // check for rel width now, maybe create compression...
+ long n = nMaxLineWidth - aTextSize.Width();
+ TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( pLine->GetEndPortion() );
+ sal_uInt16 nPosInArray = pLine->GetEnd()-1-pLine->GetStart();
+ long nNewValue = ( nPosInArray ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 ) + n;
+ pLine->GetCharPosArray()[ nPosInArray ] = nNewValue;
+ pTP->GetSize().Width() += n;
+ }
+
+ pLine->SetTextWidth( aTextSize.Width() );
+ switch ( eJustification )
+ {
+ case SVX_ADJUST_CENTER:
+ {
+ long n = ( nMaxLineWidth - aTextSize.Width() ) / 2;
+ n += nStartX; // Einrueckung bleibt erhalten.
+ if ( n > 0 )
+ pLine->SetStartPosX( (sal_uInt16)n );
+ else
+ pLine->SetStartPosX( 0 );
+
+ }
+ break;
+ case SVX_ADJUST_RIGHT:
+ {
+ // Bei automatisch umgebrochenen Zeilen, die ein Blank
+ // am Ende enthalten, darf das Blank nicht ausgegeben werden!
+
+ long n = nMaxLineWidth - aTextSize.Width();
+ n += nStartX; // Einrueckung bleibt erhalten.
+ if ( n > 0 )
+ pLine->SetStartPosX( (sal_uInt16)n );
+ else
+ pLine->SetStartPosX( 0 );
+ }
+ break;
+ case SVX_ADJUST_BLOCK:
+ {
+ long nRemainingSpace = nMaxLineWidth - aTextSize.Width();
+ pLine->SetStartPosX( (sal_uInt16)nStartX );
+ if ( !bEOC && ( nRemainingSpace > 0 ) ) // nicht die letzte Zeile...
+ ImpAdjustBlocks( pParaPortion, pLine, nRemainingSpace );
+ }
+ break;
+ default:
+ {
+ pLine->SetStartPosX( (sal_uInt16)nStartX ); // FI, LI
+ }
+ break;
+ }
+
+ // -----------------------------------------------------------------
+ // pruefen, ob die Zeile neu ausgegeben werden muss...
+ // -----------------------------------------------------------------
+ pLine->SetInvalid();
+
+ // Wenn eine Portion umgebrochen wurde sind ggf. viel zu viele Positionen
+ // im CharPosArray:
+ if ( bCalcCharPositions )
+ {
+ sal_uInt16 nLen = pLine->GetLen();
+ sal_uInt16 nCount = pLine->GetCharPosArray().Count();
+ if ( nCount > nLen )
+ pLine->GetCharPosArray().Remove( nLen, nCount-nLen );
+ }
+
+ if ( GetTextRanger() )
+ {
+ if ( nTextXOffset )
+ pLine->SetStartPosX( (sal_uInt16) ( pLine->GetStartPosX() + nTextXOffset ) );
+ if ( nTextExtraYOffset )
+ {
+ pLine->SetHeight( (sal_uInt16) ( pLine->GetHeight() + nTextExtraYOffset ), 0, pLine->GetHeight() );
+ pLine->SetMaxAscent( (sal_uInt16) ( pLine->GetMaxAscent() + nTextExtraYOffset ) );
+ }
+ }
+
+ // Fuer kleiner 0 noch ueberlegen!
+ if ( pParaPortion->IsSimpleInvalid() /* && ( nInvalidDiff > 0 ) */ )
+ {
+ // Aenderung durch einfache Textaenderung...
+ // Formatierung nicht abbrechen, da Portions evtl. wieder
+ // gesplittet werden muessen!
+ // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
+ // Aber ggf. als Valid markieren, damit weniger Ausgabe...
+ if ( pLine->GetEnd() < nInvalidStart )
+ {
+ if ( *pLine == aSaveLine )
+ {
+ pLine->SetValid();
+ }
+ }
+ else
+ {
+ sal_uInt16 nStart = pLine->GetStart();
+ sal_uInt16 nEnd = pLine->GetEnd();
+
+ if ( nStart > nInvalidEnd )
+ {
+ if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
+ ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
+ {
+ pLine->SetValid();
+ if ( bCalcCharPositions && bQuickFormat )
+ {
+ bCalcCharPositions = sal_False;
+ bLineBreak = sal_False;
+ pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
+ break;
+ }
+ }
+ }
+ else if ( bCalcCharPositions && bQuickFormat && ( nEnd > nInvalidEnd) )
+ {
+ // Wenn die ungueltige Zeile so endet, dass die naechste an
+ // der 'gleichen' Textstelle wie vorher beginnt, also nicht
+ // anders umgebrochen wird, brauche ich dort auch nicht die
+ // textbreiten neu bestimmen:
+ if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
+ {
+ bCalcCharPositions = sal_False;
+ bLineBreak = sal_False;
+ pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bSameLineAgain )
+ {
+ nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende
+ // weil nEnd hinter das letzte Zeichen zeigt!
+
+ sal_uInt16 nEndPortion = pLine->GetEndPortion();
+
+ // Naechste Zeile oder ggf. neue Zeile....
+ pLine = 0;
+ if ( nLine < pParaPortion->GetLines().Count()-1 )
+ pLine = pParaPortion->GetLines().GetObject( ++nLine );
+ if ( pLine && ( nIndex >= pNode->Len() ) )
+ {
+ nDelFromLine = nLine;
+ break;
+ }
+ if ( !pLine )
+ {
+ if ( nIndex < pNode->Len() )
+ {
+ pLine = new EditLine;
+ pParaPortion->GetLines().Insert( pLine, ++nLine );
+ }
+ else if ( nIndex && bLineBreak && GetTextRanger() )
+ {
+ // normaly CreateAndInsertEmptyLine would be called, but I want to use
+ // CreateLines, so I need Polygon code only here...
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
+ pLine = new EditLine;
+ pParaPortion->GetLines().Insert( pLine, ++nLine );
+ bForceOneRun = TRUE;
+ bProcessingEmptyLine = TRUE;
+ }
+ }
+ if ( pLine )
+ {
+ aSaveLine = *pLine;
+ pLine->SetStart( nIndex );
+ pLine->SetEnd( nIndex );
+ pLine->SetStartPortion( nEndPortion+1 );
+ pLine->SetEndPortion( nEndPortion+1 );
+ }
+ }
+ } // while ( Index < Len )
+
+ if ( nDelFromLine != 0xFFFF )
+ pParaPortion->GetLines().DeleteFromLine( nDelFromLine );
+
+ DBG_ASSERT( pParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
+
+ if ( bLineBreak == sal_True )
+ CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
+
+ delete[] pBuf;
+
+ sal_Bool bHeightChanged = FinishCreateLines( pParaPortion );
+
+ if ( bMapChanged )
+ GetRefDevice()->Pop();
+
+ GetRefDevice()->Pop();
+
+ return bHeightChanged;
+}
+
+void ImpEditEngine::CreateAndInsertEmptyLine( ParaPortion* pParaPortion, sal_uInt32 )
+{
+ DBG_ASSERT( !GetTextRanger(), "Don't use CreateAndInsertEmptyLine with a polygon!" );
+
+ EditLine* pTmpLine = new EditLine;
+ pTmpLine->SetStart( pParaPortion->GetNode()->Len() );
+ pTmpLine->SetEnd( pParaPortion->GetNode()->Len() );
+ pParaPortion->GetLines().Insert( pTmpLine, pParaPortion->GetLines().Count() );
+
+ sal_Bool bLineBreak = pParaPortion->GetNode()->Len() ? sal_True : sal_False;
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pParaPortion->GetNode(), &nSpaceBefore );
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pParaPortion->GetNode() );
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pParaPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ short nStartX = GetXValue( (short)(rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBefore));
+
+ Rectangle aBulletArea = Rectangle( Point(), Point() );
+ if ( bLineBreak == sal_True )
+ {
+ nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
+ }
+ else
+ {
+ aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
+ if ( aBulletArea.Right() > 0 )
+ pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
+ else
+ pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
+ if ( pParaPortion->GetBulletX() > nStartX )
+ {
+ nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
+ if ( pParaPortion->GetBulletX() > nStartX )
+ nStartX = pParaPortion->GetBulletX();
+ }
+ }
+
+ SvxFont aTmpFont;
+ SeekCursor( pParaPortion->GetNode(), bLineBreak ? pParaPortion->GetNode()->Len() : 0, aTmpFont );
+ aTmpFont.SetPhysFont( pRefDev );
+
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pDummyPortion->GetSize() = aTmpFont.GetPhysTxtSize( pRefDev, String() );
+ if ( IsFixedCellHeight() )
+ pDummyPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
+ pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
+ FormatterFontMetric aFormatterMetrics;
+ RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
+ pTmpLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
+ pTmpLine->SetHeight( (sal_uInt16) pDummyPortion->GetSize().Height() );
+ sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
+ if ( nLineHeight > pTmpLine->GetHeight() )
+ pTmpLine->SetHeight( nLineHeight );
+
+ if ( !aStatus.IsOutliner() )
+ {
+ USHORT nPara = GetParaPortions().GetPos( pParaPortion );
+ SvxAdjust eJustification = GetJustification( nPara );
+ long nMaxLineWidth = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
+ nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
+ long nTextXOffset = 0;
+ if ( nMaxLineWidth < 0 )
+ nMaxLineWidth = 1;
+ if ( eJustification == SVX_ADJUST_CENTER )
+ nStartX = sal::static_int_cast< short >(nMaxLineWidth / 2);
+ else if ( eJustification == SVX_ADJUST_RIGHT )
+ nStartX = sal::static_int_cast< short >(nMaxLineWidth);
+
+ nStartX = sal::static_int_cast< short >(nStartX + nTextXOffset);
+ }
+
+ pTmpLine->SetStartPosX( nStartX );
+
+ if ( !aStatus.IsOutliner() )
+ {
+ if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
+ {
+ sal_uInt16 nMinHeight = rLSItem.GetLineHeight();
+ sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
+ if ( nTxtHeight < nMinHeight )
+ {
+ // Der Ascent muss um die Differenz angepasst werden:
+ long nDiff = nMinHeight - nTxtHeight;
+ pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff) );
+ pTmpLine->SetHeight( nMinHeight, nTxtHeight );
+ }
+ }
+ else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
+ {
+ USHORT nPara = GetParaPortions().GetPos( pParaPortion );
+ if ( nPara || IsFixedCellHeight() || pTmpLine->GetStartPortion() ) // Nicht die aller erste Zeile
+ {
+ // #100508# There are documents with PropLineSpace 0, why?
+ // (cmc: re above question :-) such documents can be seen by importing a .ppt
+ if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
+ {
+ sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
+ sal_Int32 nH = nTxtHeight;
+ nH *= rLSItem.GetPropLineSpace();
+ nH /= 100;
+ // Der Ascent muss um die Differenz angepasst werden:
+ long nDiff = pTmpLine->GetHeight() - nH;
+ if ( nDiff > pTmpLine->GetMaxAscent() )
+ nDiff = pTmpLine->GetMaxAscent();
+ pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() - nDiff) );
+ pTmpLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
+ }
+ }
+ }
+ }
+
+ if ( !bLineBreak )
+ {
+ long nMinHeight = aBulletArea.GetHeight();
+ if ( nMinHeight > (long)pTmpLine->GetHeight() )
+ {
+ long nDiff = nMinHeight - (long)pTmpLine->GetHeight();
+ // nDiff auf oben und unten verteilen.
+ pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff/2) );
+ pTmpLine->SetHeight( (sal_uInt16)nMinHeight );
+ }
+ }
+ else
+ {
+ // -2: Die neue ist bereits eingefuegt.
+#ifdef DBG_UTIL
+ EditLine* pLastLine = pParaPortion->GetLines().GetObject( pParaPortion->GetLines().Count()-2 );
+ DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
+ DBG_ASSERT( pLastLine->GetEnd() == pParaPortion->GetNode()->Len(), "Doch anders?" );
+#endif
+// pTmpLine->SetStart( pLastLine->GetEnd() );
+// pTmpLine->SetEnd( pLastLine->GetEnd() );
+ sal_uInt16 nPos = (sal_uInt16) pParaPortion->GetTextPortions().Count() - 1 ;
+ pTmpLine->SetStartPortion( nPos );
+ pTmpLine->SetEndPortion( nPos );
+ }
+}
+
+sal_Bool ImpEditEngine::FinishCreateLines( ParaPortion* pParaPortion )
+{
+// CalcCharPositions( pParaPortion );
+ pParaPortion->SetValid();
+ long nOldHeight = pParaPortion->GetHeight();
+// sal_uInt16 nPos = GetParaPortions().GetPos( pParaPortion );
+// DBG_ASSERT( nPos != USHRT_MAX, "FinishCreateLines: Portion nicht in Liste!" );
+// ParaPortion* pPrev = nPos ? GetParaPortions().GetObject( nPos-1 ) : 0;
+ CalcHeight( pParaPortion );
+
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "FinishCreateLines: Keine Text-Portion?" );
+ sal_Bool bRet = ( pParaPortion->GetHeight() != nOldHeight );
+ return bRet;
+}
+
+void ImpEditEngine::ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion* pPortion, sal_uInt16 nPortionStart, long nRemainingWidth, sal_Bool bCanHyphenate )
+{
+ ContentNode* const pNode = pParaPortion->GetNode();
+
+ sal_uInt16 nBreakInLine = nPortionStart - pLine->GetStart();
+ sal_uInt16 nMax = nBreakInLine + pPortion->GetLen();
+ while ( ( nBreakInLine < nMax ) && ( pLine->GetCharPosArray()[nBreakInLine] < nRemainingWidth ) )
+ nBreakInLine++;
+
+ sal_uInt16 nMaxBreakPos = nBreakInLine + pLine->GetStart();
+ sal_uInt16 nBreakPos = 0xFFFF;
+
+ sal_Bool bCompressBlank = sal_False;
+ sal_Bool bHyphenated = sal_False;
+ sal_Bool bHangingPunctuation = sal_False;
+ sal_Unicode cAlternateReplChar = 0;
+ sal_Unicode cAlternateExtraChar = 0;
+
+ if ( ( nMaxBreakPos < ( nMax + pLine->GetStart() ) ) && ( pNode->GetChar( nMaxBreakPos ) == ' ' ) )
+ {
+ // Break behind the blank, blank will be compressed...
+ nBreakPos = nMaxBreakPos + 1;
+ bCompressBlank = sal_True;
+ }
+ else
+ {
+ sal_uInt16 nMinBreakPos = pLine->GetStart();
+ USHORT nAttrs = pNode->GetCharAttribs().GetAttribs().Count();
+ for ( USHORT nAttr = nAttrs; nAttr; )
+ {
+ EditCharAttrib* pAttr = pNode->GetCharAttribs().GetAttribs()[--nAttr];
+ if ( pAttr->IsFeature() && ( pAttr->GetEnd() > nMinBreakPos ) && ( pAttr->GetEnd() <= nMaxBreakPos ) )
+ {
+ nMinBreakPos = pAttr->GetEnd();
+ break;
+ }
+ }
+
+ lang::Locale aLocale = GetLocale( EditPaM( pNode, nMaxBreakPos ) );
+
+ Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ OUString aText( *pNode );
+ Reference< XHyphenator > xHyph;
+ if ( bCanHyphenate )
+ xHyph = GetHyphenator();
+ i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, Sequence< PropertyValue >(), 1 );
+ i18n::LineBreakUserOptions aUserOptions;
+
+ const i18n::ForbiddenCharacters* pForbidden = GetForbiddenCharsTable()->GetForbiddenCharacters( SvxLocaleToLanguage( aLocale ), TRUE );
+ aUserOptions.forbiddenBeginCharacters = pForbidden->beginLine;
+ aUserOptions.forbiddenEndCharacters = pForbidden->endLine;
+ aUserOptions.applyForbiddenRules = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_FORBIDDENRULES )).GetValue();
+ aUserOptions.allowPunctuationOutsideMargin = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HANGINGPUNCTUATION )).GetValue();
+ aUserOptions.allowHyphenateEnglish = FALSE;
+
+ i18n::LineBreakResults aLBR = _xBI->getLineBreak( *pNode, nMaxBreakPos, aLocale, nMinBreakPos, aHyphOptions, aUserOptions );
+ nBreakPos = (USHORT)aLBR.breakIndex;
+
+ // BUG in I18N - under special condition (break behind field, #87327#) breakIndex is < nMinBreakPos
+ if ( nBreakPos < nMinBreakPos )
+ {
+ nBreakPos = nMinBreakPos;
+ }
+ else if ( ( nBreakPos > nMaxBreakPos ) && !aUserOptions.allowPunctuationOutsideMargin )
+ {
+ DBG_ERROR( "I18N: XBreakIterator::getLineBreak returns position > Max" );
+ nBreakPos = nMaxBreakPos;
+ }
+
+ // #101795# nBreakPos can never be outside the portion, even not with hangig punctuation
+ if ( nBreakPos > nMaxBreakPos )
+ nBreakPos = nMaxBreakPos;
+
+ // BUG in I18N - the japanese dot is in the next line!
+ // !!! Testen!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if ( (nBreakPos + ( aUserOptions.allowPunctuationOutsideMargin ? 0 : 1 ) ) <= nMaxBreakPos )
+ {
+ sal_Unicode cFirstInNextLine = ( (nBreakPos+1) < pNode->Len() ) ? pNode->GetChar( nBreakPos ) : 0;
+ if ( cFirstInNextLine == 12290 )
+ nBreakPos++;
+ }
+
+ bHangingPunctuation = ( nBreakPos > nMaxBreakPos ) ? sal_True : sal_False;
+ pLine->SetHangingPunctuation( bHangingPunctuation );
+
+ #ifndef SVX_LIGHT
+ // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch
+ // die Silbentrennung jagen...
+ // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt,
+ // nBreakPos ist der Wort-Anfang
+ // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort
+ // auf mehr als Zwei Zeilen gebrochen wird...
+ if ( !bHangingPunctuation && bCanHyphenate && GetHyphenator().is() )
+ {
+ i18n::Boundary aBoundary = _xBI->getWordBoundary( *pNode, nBreakPos, GetLocale( EditPaM( pNode, nBreakPos ) ), ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
+// sal_uInt16 nWordStart = nBreakPos;
+// sal_uInt16 nBreakPos_OLD = nBreakPos;
+ sal_uInt16 nWordStart = nBreakPos;
+ sal_uInt16 nWordEnd = (USHORT) aBoundary.endPos;
+ DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
+
+ USHORT nWordLen = nWordEnd - nWordStart;
+ if ( ( nWordEnd >= nMaxBreakPos ) && ( nWordLen > 3 ) )
+ {
+ // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
+ // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
+ String aWord( *pNode, nWordStart, nWordLen );
+ sal_uInt16 nMinTrail = nWordEnd-nMaxBreakPos+1; //+1: Vor dem angeknacksten Buchstaben
+ Reference< XHyphenatedWord > xHyphWord;
+ if (xHyphenator.is())
+ xHyphWord = xHyphenator->hyphenate( aWord, aLocale, aWord.Len() - nMinTrail, Sequence< PropertyValue >() );
+ if (xHyphWord.is())
+ {
+ sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
+ sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos();
+
+ if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= (pLine->GetStart() + 2 ) ) )
+ {
+ if ( !bAlternate )
+ {
+ bHyphenated = sal_True;
+ nBreakPos = nWordStart + _nWordLen;
+ }
+ else
+ {
+ String aAlt( xHyphWord->getHyphenatedWord() );
+
+ // Wir gehen von zwei Faellen aus, die nun
+ // vorliegen koennen:
+ // 1) packen wird zu pak-ken
+ // 2) Schiffahrt wird zu Schiff-fahrt
+ // In Fall 1 muss ein Zeichen ersetzt werden,
+ // in Fall 2 wird ein Zeichen hinzugefuegt.
+ // Die Identifikation wird erschwert durch Worte wie
+ // "Schiffahrtsbrennesseln", da der Hyphenator alle
+ // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln"
+ // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom
+ // Index des AlternativWord auf aWord schliessen.
+
+ // Das ganze geraffel wird durch eine Funktion am
+ // Hyphenator vereinfacht werden, sobald AMA sie einbaut...
+ sal_uInt16 nAltStart = _nWordLen - 1;
+ sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len());
+ sal_uInt16 nTxtEnd = nTxtStart;
+ sal_uInt16 nAltEnd = nAltStart;
+
+ // Die Bereiche zwischen den nStart und nEnd ist
+ // die Differenz zwischen Alternativ- und OriginalString.
+ while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() &&
+ aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) )
+ {
+ ++nTxtEnd;
+ ++nAltEnd;
+ }
+
+ // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt:
+ if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
+ aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) )
+ {
+ ++nAltEnd;
+ ++nTxtStart;
+ ++nTxtEnd;
+ }
+
+ DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" );
+
+ if ( nTxtEnd > nTxtStart )
+ cAlternateReplChar = aAlt.GetChar( nAltStart );
+ else
+ cAlternateExtraChar = aAlt.GetChar( nAltStart );
+
+ bHyphenated = sal_True;
+ nBreakPos = nWordStart + nTxtStart;
+ if ( cAlternateReplChar )
+ nBreakPos++;
+ }
+ }
+ }
+ }
+ }
+
+ #endif // !SVX_LIGHT
+
+ if ( nBreakPos <= pLine->GetStart() )
+ {
+ // keine Trenner in Zeile => abhacken !
+ nBreakPos = nMaxBreakPos;
+ // MT: I18N nextCharacters !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if ( nBreakPos <= pLine->GetStart() )
+ nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife!
+ }
+ }
+
+ // die angeknackste Portion ist die End-Portion
+ pLine->SetEnd( nBreakPos );
+
+ sal_uInt16 nEndPortion = SplitTextPortion( pParaPortion, nBreakPos, pLine );
+
+ if ( !bCompressBlank && !bHangingPunctuation )
+ {
+ // #96187# When justification is not SVX_ADJUST_LEFT, it's important to compress
+ // the trailing space even if there is enough room for the space...
+ // Don't check for SVX_ADJUST_LEFT, doesn't matter to compress in this case too...
+ DBG_ASSERT( nBreakPos > pLine->GetStart(), "ImpBreakLines - BreakPos not expected!" );
+ if ( pNode->GetChar( nBreakPos-1 ) == ' ' )
+ bCompressBlank = sal_True;
+ }
+
+ if ( bCompressBlank || bHangingPunctuation )
+ {
+ TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nEndPortion );
+ DBG_ASSERT( pTP->GetKind() == PORTIONKIND_TEXT, "BlankRubber: Keine TextPortion!" );
+ DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
+ sal_uInt16 nPosInArray = nBreakPos - 1 - pLine->GetStart();
+ pTP->GetSize().Width() = ( nPosInArray && ( pTP->GetLen() > 1 ) ) ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0;
+ pLine->GetCharPosArray()[ nPosInArray ] = pTP->GetSize().Width();
+ }
+ else if ( bHyphenated )
+ {
+ // Eine Portion fuer den Trenner einbauen...
+ TextPortion* pHyphPortion = new TextPortion( 0 );
+ pHyphPortion->GetKind() = PORTIONKIND_HYPHENATOR;
+ String aHyphText( CH_HYPH );
+ if ( cAlternateReplChar )
+ {
+ TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nEndPortion );
+ DBG_ASSERT( pPrev && pPrev->GetLen(), "Hyphenate: Prev portion?!" );
+ pPrev->SetLen( pPrev->GetLen() - 1 );
+ pHyphPortion->SetLen( 1 );
+ pHyphPortion->SetExtraValue( cAlternateReplChar );
+ // Breite der Portion davor korrigieren:
+ pPrev->GetSize().Width() =
+ pLine->GetCharPosArray()[ nBreakPos-1 - pLine->GetStart() - 1 ];
+ }
+ else if ( cAlternateExtraChar )
+ {
+ pHyphPortion->SetExtraValue( cAlternateExtraChar );
+ aHyphText.Insert( cAlternateExtraChar, 0 );
+ }
+
+ // Breite der Hyph-Portion ermitteln:
+ SvxFont aFont;
+ SeekCursor( pParaPortion->GetNode(), nBreakPos, aFont );
+ aFont.SetPhysFont( GetRefDevice() );
+ pHyphPortion->GetSize().Height() = GetRefDevice()->GetTextHeight();
+ pHyphPortion->GetSize().Width() = GetRefDevice()->GetTextWidth( aHyphText );
+
+ pParaPortion->GetTextPortions().Insert( pHyphPortion, ++nEndPortion );
+ }
+ pLine->SetEndPortion( nEndPortion );
+}
+
+void ImpEditEngine::ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, long nRemainingSpace )
+{
+ DBG_ASSERT( nRemainingSpace > 0, "AdjustBlocks: Etwas zuwenig..." );
+ DBG_ASSERT( pLine, "AdjustBlocks: Zeile ?!" );
+ if ( ( nRemainingSpace < 0 ) || pLine->IsEmpty() )
+ return ;
+
+ const USHORT nFirstChar = pLine->GetStart();
+ const USHORT nLastChar = pLine->GetEnd() -1; // Last zeigt dahinter
+ ContentNode* pNode = pParaPortion->GetNode();
+
+ DBG_ASSERT( nLastChar < pNode->Len(), "AdjustBlocks: Out of range!" );
+
+ // Search blanks or Kashidas...
+ SvUShorts aPositions;
+ USHORT nChar;
+ for ( nChar = nFirstChar; nChar <= nLastChar; nChar++ )
+ {
+ if ( pNode->GetChar(nChar) == ' ' )
+ {
+ // Don't use blank if language is arabic
+ LanguageType eLang = GetLanguage( EditPaM( pNode, nChar ) );
+ if ( MsLangId::getPrimaryLanguage( eLang) != LANGUAGE_ARABIC_PRIMARY_ONLY )
+ aPositions.Insert( nChar, aPositions.Count() );
+ }
+ }
+
+ // Kashidas ?
+ ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
+
+
+ if ( !aPositions.Count() )
+ return;
+
+ // Wenn das letzte Zeichen ein Blank ist, will ich es nicht haben!
+ // Die Breite muss auf die Blocker davor verteilt werden...
+ // Aber nicht, wenn es das einzige ist
+ if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.Count() > 1 ) && ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ) ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) )
+ {
+ aPositions.Remove( aPositions.Count()-1, 1 );
+ USHORT nPortionStart, nPortion;
+ nPortion = pParaPortion->GetTextPortions().FindPortion( nLastChar+1, nPortionStart );
+ TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
+ long nRealWidth = pLine->GetCharPosArray()[nLastChar-nFirstChar];
+ long nBlankWidth = nRealWidth;
+ if ( nLastChar > nPortionStart )
+ nBlankWidth -= pLine->GetCharPosArray()[nLastChar-nFirstChar-1];
+ // Evtl. ist das Blank schon in ImpBreakLine abgezogen worden:
+ if ( nRealWidth == pLastPortion->GetSize().Width() )
+ {
+ // Beim letzten Zeichen muss die Portion hinter dem Blank aufhoeren
+ // => Korrektur vereinfachen:
+ DBG_ASSERT( ( nPortionStart + pLastPortion->GetLen() ) == ( nLastChar+1 ), "Blank doch nicht am Portion-Ende?!" );
+ pLastPortion->GetSize().Width() -= nBlankWidth;
+ nRemainingSpace += nBlankWidth;
+ }
+ pLine->GetCharPosArray()[nLastChar-nFirstChar] -= nBlankWidth;
+ }
+
+ USHORT nGaps = aPositions.Count();
+ const long nMore4Everyone = nRemainingSpace / nGaps;
+ long nSomeExtraSpace = nRemainingSpace - nMore4Everyone*nGaps;
+
+ DBG_ASSERT( nSomeExtraSpace < (long)nGaps, "AdjustBlocks: ExtraSpace zu gross" );
+ DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
+
+ // Die Positionen im Array und die Portion-Breiten korrigieren:
+ // Letztes Zeichen wird schon nicht mehr beachtet...
+ for ( USHORT n = 0; n < aPositions.Count(); n++ )
+ {
+ nChar = aPositions[n];
+ if ( nChar < nLastChar )
+ {
+ USHORT nPortionStart, nPortion;
+ nPortion = pParaPortion->GetTextPortions().FindPortion( nChar, nPortionStart );
+ TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
+
+ // Die Breite der Portion:
+ pLastPortion->GetSize().Width() += nMore4Everyone;
+ if ( nSomeExtraSpace )
+ pLastPortion->GetSize().Width()++;
+
+ // Correct positions in array
+ // Even for kashidas just change positions, VCL will then draw the kashida automaticly
+ USHORT nPortionEnd = nPortionStart + pLastPortion->GetLen();
+ for ( USHORT _n = nChar; _n < nPortionEnd; _n++ )
+ {
+ pLine->GetCharPosArray()[_n-nFirstChar] += nMore4Everyone;
+ if ( nSomeExtraSpace )
+ pLine->GetCharPosArray()[_n-nFirstChar]++;
+ }
+
+ if ( nSomeExtraSpace )
+ nSomeExtraSpace--;
+ }
+ }
+
+ // Now the text width contains the extra width...
+ pLine->SetTextWidth( pLine->GetTextWidth() + nRemainingSpace );
+}
+
+void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, USHORT nStart, USHORT nEnd, SvUShorts& rArray )
+{
+ // the search has to be performed on a per word base
+
+ EditSelection aWordSel( EditPaM( pNode, nStart ) );
+ aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
+ if ( aWordSel.Min().GetIndex() < nStart )
+ aWordSel.Min().GetIndex() = nStart;
+
+ while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) )
+ {
+ USHORT nSavPos = aWordSel.Max().GetIndex();
+ if ( aWordSel.Max().GetIndex() > nEnd )
+ aWordSel.Max().GetIndex() = nEnd;
+
+ String aWord = GetSelected( aWordSel );
+
+ // restore selection for proper iteration at the end of the function
+ aWordSel.Max().GetIndex() = nSavPos;
+
+ xub_StrLen nIdx = 0;
+ xub_StrLen nKashidaPos = STRING_LEN;
+ xub_Unicode cCh;
+ xub_Unicode cPrevCh = 0;
+
+ while ( nIdx < aWord.Len() )
+ {
+ cCh = aWord.GetChar( nIdx );
+
+ // 1. Priority:
+ // after user inserted kashida
+ if ( 0x640 == cCh )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
+ break;
+ }
+
+ // 2. Priority:
+ // after a Seen or Sad
+ if ( nIdx + 1 < aWord.Len() &&
+ ( 0x633 == cCh || 0x635 == cCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
+ break;
+ }
+
+ // 3. Priority:
+ // before final form of Teh Marbuta, Hah, Dal
+ // 4. Priority:
+ // before final form of Alef, Lam or Kaf
+ if ( nIdx && nIdx + 1 == aWord.Len() &&
+ ( 0x629 == cCh || 0x62D == cCh || 0x62F == cCh ||
+ 0x627 == cCh || 0x644 == cCh || 0x643 == cCh ) )
+ {
+ DBG_ASSERT( 0 != cPrevCh, "No previous character" );
+
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
+ break;
+ }
+ }
+
+ // 5. Priority:
+ // before media Bah
+ if ( nIdx && nIdx + 1 < aWord.Len() && 0x628 == cCh )
+ {
+ DBG_ASSERT( 0 != cPrevCh, "No previous character" );
+
+ // check if next character is Reh, Yeh or Alef Maksura
+ xub_Unicode cNextCh = aWord.GetChar( nIdx + 1 );
+
+ if ( 0x631 == cNextCh || 0x64A == cNextCh ||
+ 0x649 == cNextCh )
+ {
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
+ }
+ }
+
+ // 6. Priority:
+ // other connecting possibilities
+ if ( nIdx && nIdx + 1 == aWord.Len() &&
+ 0x60C <= cCh && 0x6FE >= cCh )
+ {
+ DBG_ASSERT( 0 != cPrevCh, "No previous character" );
+
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ // only choose this position if we did not find
+ // a better one:
+ if ( STRING_LEN == nKashidaPos )
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
+ break;
+ }
+ }
+
+ // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
+ // Damma, Kasra, Shadda and Sukun when checking if
+ // a character can be connected to previous character.
+ if ( cCh < 0x64B || cCh > 0x652 )
+ cPrevCh = cCh;
+
+ ++nIdx;
+ } // end of current word
+
+ if ( STRING_LEN != nKashidaPos )
+ rArray.Insert( nKashidaPos, rArray.Count() );
+
+ aWordSel = WordRight( aWordSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
+ aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
+ }
+}
+
+sal_uInt16 ImpEditEngine::SplitTextPortion( ParaPortion* pPortion, sal_uInt16 nPos, EditLine* pCurLine )
+{
+ DBG_ASSERT( pPortion, "SplitTextPortion: Welche ?" );
+
+ // Die Portion bei nPos wird geplittet, wenn bei nPos nicht
+ // sowieso ein Wechsel ist
+ if ( nPos == 0 )
+ return 0;
+
+ sal_uInt16 nSplitPortion;
+ sal_uInt16 nTmpPos = 0;
+ TextPortion* pTextPortion = 0;
+ sal_uInt16 nPortions = pPortion->GetTextPortions().Count();
+ for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
+ {
+ TextPortion* pTP = pPortion->GetTextPortions().GetObject(nSplitPortion);
+ nTmpPos = nTmpPos + pTP->GetLen();
+ if ( nTmpPos >= nPos )
+ {
+ if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden
+ {
+ // Skip Portions with ExtraSpace
+// while ( ( (nSplitPortion+1) < nPortions ) && (pPortion->GetTextPortions().GetObject(nSplitPortion+1)->GetKind() == PORTIONKIND_EXTRASPACE ) )
+// nSplitPortion++;
+
+ return nSplitPortion;
+ }
+ pTextPortion = pTP;
+ break;
+ }
+ }
+
+ DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
+ DBG_ASSERT( pTextPortion->GetKind() == PORTIONKIND_TEXT, "SplitTextPortion: Keine TextPortion!" );
+
+ sal_uInt16 nOverlapp = nTmpPos - nPos;
+ pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
+ TextPortion* pNewPortion = new TextPortion( nOverlapp );
+ pPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
+ // Groessen setzen:
+ if ( pCurLine )
+ {
+ // Kein neues GetTextSize, sondern Werte aus Array verwenden:
+ DBG_ASSERT( nPos > pCurLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
+ pTextPortion->GetSize().Width() = pCurLine->GetCharPosArray()[ nPos-pCurLine->GetStart()-1 ];
+
+ if ( pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed )
+ {
+ // We need the original size from the portion
+ USHORT nTxtPortionStart = pPortion->GetTextPortions().GetStartPos( nSplitPortion );
+ SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
+ SeekCursor( pPortion->GetNode(), nTxtPortionStart+1, aTmpFont );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ GetRefDevice()->Push( PUSH_TEXTLANGUAGE );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+ Size aSz = aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nTxtPortionStart, pTextPortion->GetLen(), NULL );
+ GetRefDevice()->Pop();
+ pTextPortion->GetExtraInfos()->nOrgWidth = aSz.Width();
+ }
+ }
+ else
+ pTextPortion->GetSize().Width() = (-1);
+
+ return nSplitPortion;
+}
+
+void ImpEditEngine::CreateTextPortions( ParaPortion* pParaPortion, sal_uInt16& rStart /* , sal_Bool bCreateBlockPortions */ )
+{
+ sal_uInt16 nStartPos = rStart;
+ ContentNode* pNode = pParaPortion->GetNode();
+ DBG_ASSERT( pNode->Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
+
+ SortedPositions aPositions;
+ aPositions.Insert( (sal_uInt32) 0 );
+
+ sal_uInt16 nAttr = 0;
+ EditCharAttrib* pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ while ( pAttrib )
+ {
+ // Start und Ende in das Array eintragen...
+ // Die InsertMethode laesst keine doppelten Werte zu....
+ aPositions.Insert( pAttrib->GetStart() );
+ aPositions.Insert( pAttrib->GetEnd() );
+ nAttr++;
+ pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ aPositions.Insert( pNode->Len() );
+
+ if ( !pParaPortion->aScriptInfos.Count() )
+ ((ImpEditEngine*)this)->InitScriptTypes( GetParaPortions().GetPos( pParaPortion ) );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ for ( USHORT nT = 0; nT < rTypes.Count(); nT++ )
+ aPositions.Insert( rTypes[nT].nStartPos );
+
+ const WritingDirectionInfos& rWritingDirections = pParaPortion->aWritingDirectionInfos;
+ for ( USHORT nD = 0; nD < rWritingDirections.Count(); nD++ )
+ aPositions.Insert( rWritingDirections[nD].nStartPos );
+
+ if ( mpIMEInfos && mpIMEInfos->nLen && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) )
+ {
+ sal_uInt16 nLastAttr = 0xFFFF;
+ for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
+ {
+ if ( mpIMEInfos->pAttribs[n] != nLastAttr )
+ {
+ aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
+ nLastAttr = mpIMEInfos->pAttribs[n];
+ }
+ }
+ aPositions.Insert( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen );
+ }
+
+ // Ab ... loeschen:
+ // Leider muss die Anzahl der TextPortions mit aPositions.Count()
+ // nicht uebereinstimmen, da evtl. Zeilenumbrueche...
+ sal_uInt16 nPortionStart = 0;
+ sal_uInt16 nInvPortion = 0;
+ sal_uInt16 nP;
+ for ( nP = 0; nP < pParaPortion->GetTextPortions().Count(); nP++ )
+ {
+ TextPortion* pTmpPortion = pParaPortion->GetTextPortions().GetObject(nP);
+ nPortionStart = nPortionStart + pTmpPortion->GetLen();
+ if ( nPortionStart >= nStartPos )
+ {
+ nPortionStart = nPortionStart - pTmpPortion->GetLen();
+ rStart = nPortionStart;
+ nInvPortion = nP;
+ break;
+ }
+ }
+ DBG_ASSERT( nP < pParaPortion->GetTextPortions().Count() || !pParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
+ if ( nInvPortion && ( nPortionStart+pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
+ {
+ // lieber eine davor...
+ // Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
+ // die einzige in der Zeile davor !
+ nInvPortion--;
+ nPortionStart = nPortionStart - pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
+ }
+ pParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
+
+ // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
+ aPositions.Insert( nPortionStart );
+
+ sal_uInt16 nInvPos;
+#ifdef DBG_UTIL
+ sal_Bool bFound =
+#endif
+ aPositions.Seek_Entry( nPortionStart, &nInvPos );
+
+ DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
+ for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ )
+ {
+ TextPortion* pNew = new TextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] );
+ pParaPortion->GetTextPortions().Insert( pNew, pParaPortion->GetTextPortions().Count());
+ }
+
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
+#ifdef EDITDEBUG
+ DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
+#endif
+}
+
+void ImpEditEngine::RecalcTextPortion( ParaPortion* pParaPortion, sal_uInt16 nStartPos, short nNewChars )
+{
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions!" );
+ DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
+
+ ContentNode* const pNode = pParaPortion->GetNode();
+ if ( nNewChars > 0 )
+ {
+ // Wenn an nStartPos ein Attribut beginnt/endet, faengt eine neue Portion
+ // an, ansonsten wird die Portion an nStartPos erweitert.
+
+ if ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) || IsScriptChange( EditPaM( pNode, nStartPos ) ) )
+ {
+ sal_uInt16 nNewPortionPos = 0;
+ if ( nStartPos )
+ nNewPortionPos = SplitTextPortion( pParaPortion, nStartPos ) + 1;
+
+ // Eine leere Portion kann hier stehen, wenn der Absatz leer war,
+ // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
+ if ( ( nNewPortionPos < pParaPortion->GetTextPortions().Count() ) &&
+ !pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
+ {
+ DBG_ASSERT( pParaPortion->GetTextPortions()[nNewPortionPos]->GetKind() == PORTIONKIND_TEXT, "Leere Portion war keine TextPortion!" );
+ USHORT & r =
+ pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
+ r = r + nNewChars;
+ }
+ else
+ {
+ TextPortion* pNewPortion = new TextPortion( nNewChars );
+ pParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
+ }
+ }
+ else
+ {
+ sal_uInt16 nPortionStart;
+ const sal_uInt16 nTP = pParaPortion->GetTextPortions().
+ FindPortion( nStartPos, nPortionStart );
+ TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
+ DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
+ pTP->GetLen() = pTP->GetLen() + nNewChars;
+ pTP->GetSize().Width() = (-1);
+ }
+ }
+ else
+ {
+ // Portion schrumpfen oder ggf. entfernen.
+ // Vor Aufruf dieser Methode muss sichergestellt sein, dass
+ // keine Portions in dem geloeschten Bereich lagen!
+
+ // Es darf keine reinragende oder im Bereich startende Portion geben,
+ // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
+ sal_uInt16 nPortion = 0;
+ sal_uInt16 nPos = 0;
+ sal_uInt16 nEnd = nStartPos-nNewChars;
+ sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
+ TextPortion* pTP = 0;
+ for ( nPortion = 0; nPortion < nPortions; nPortion++ )
+ {
+ pTP = pParaPortion->GetTextPortions()[ nPortion ];
+ if ( ( nPos+pTP->GetLen() ) > nStartPos )
+ {
+ DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
+ DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
+ break;
+ }
+ nPos = nPos + pTP->GetLen();
+ }
+ DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
+ if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
+ {
+ // Portion entfernen;
+ BYTE nType = pTP->GetKind();
+ pParaPortion->GetTextPortions().Remove( nPortion );
+ delete pTP;
+ if ( nType == PORTIONKIND_LINEBREAK )
+ {
+ TextPortion* pNext = pParaPortion->GetTextPortions()[ nPortion ];
+ if ( pNext && !pNext->GetLen() )
+ {
+ // Dummy-Portion entfernen
+ pParaPortion->GetTextPortions().Remove( nPortion );
+ delete pNext;
+ }
+ }
+ }
+ else
+ {
+ DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
+ pTP->GetLen() = pTP->GetLen() + nNewChars;
+ }
+
+ // ganz am Schluss darf keine HYPHENATOR-Portion stehen bleiben...
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
+ sal_uInt16 nLastPortion = pParaPortion->GetTextPortions().Count() - 1;
+ pTP = pParaPortion->GetTextPortions().GetObject( nLastPortion );
+ if ( pTP->GetKind() == PORTIONKIND_HYPHENATOR )
+ {
+ // Portion wegschmeissen, ggf. die davor korrigieren, wenn
+ // die Hyph-Portion ein Zeichen geschluckt hat...
+ pParaPortion->GetTextPortions().Remove( nLastPortion );
+ if ( nLastPortion && pTP->GetLen() )
+ {
+ TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nLastPortion - 1 );
+ DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
+ pPrev->SetLen( pPrev->GetLen() + pTP->GetLen() );
+ pPrev->GetSize().Width() = (-1);
+ }
+ delete pTP;
+ }
+ }
+#ifdef EDITDEBUG
+ DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
+#endif
+}
+
+void ImpEditEngine::SetTextRanger( TextRanger* pRanger )
+{
+ if ( pTextRanger != pRanger )
+ {
+ delete pTextRanger;
+ pTextRanger = pRanger;
+
+ for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
+ {
+ ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
+ pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
+ pParaPortion->GetLines().Reset();
+ }
+
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ if ( GetUpdateMode() && GetActiveView() )
+ pActiveView->ShowCursor( sal_False, sal_False );
+ }
+}
+
+void ImpEditEngine::SetVertical( BOOL bVertical )
+{
+ if ( IsVertical() != bVertical )
+ {
+ GetEditDoc().SetVertical( bVertical );
+ sal_Bool bUseCharAttribs = ( aStatus.GetControlWord() & EE_CNTRL_USECHARATTRIBS ) ? sal_True : sal_False;
+ GetEditDoc().CreateDefFont( bUseCharAttribs );
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+ }
+}
+
+void ImpEditEngine::SetFixedCellHeight( BOOL bUseFixedCellHeight )
+{
+ if ( IsFixedCellHeight() != bUseFixedCellHeight )
+ {
+ GetEditDoc().SetFixedCellHeight( bUseFixedCellHeight );
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+ }
+}
+
+void ImpEditEngine::SeekCursor( ContentNode* pNode, sal_uInt16 nPos, SvxFont& rFont, OutputDevice* pOut, sal_uInt16 nIgnoreWhich )
+{
+ // Es war mal geplant, SeekCursor( nStartPos, nEndPos, ... ), damit nur
+ // ab der StartPosition neu gesucht wird.
+ // Problem: Es mussten zwei Listen beruecksichtigt/gefuehrt werden:
+ // OrderedByStart,OrderedByEnd.
+
+ if ( nPos > pNode->Len() )
+ nPos = pNode->Len();
+
+ rFont = pNode->GetCharAttribs().GetDefFont();
+
+ short nScriptType = GetScriptType( EditPaM( pNode, nPos ) );
+ if ( ( nScriptType == i18n::ScriptType::ASIAN ) || ( nScriptType == i18n::ScriptType::COMPLEX ) )
+ {
+ const SvxFontItem& rFontItem = (const SvxFontItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) );
+ rFont.SetName( rFontItem.GetFamilyName() );
+ rFont.SetFamily( rFontItem.GetFamily() );
+ rFont.SetPitch( rFontItem.GetPitch() );
+ rFont.SetCharSet( rFontItem.GetCharSet() );
+ Size aSz( rFont.GetSize() );
+ aSz.Height() = ((const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) ).GetHeight();
+ rFont.SetSize( aSz );
+ rFont.SetWeight( ((const SvxWeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ))).GetWeight() );
+ rFont.SetItalic( ((const SvxPostureItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ))).GetPosture() );
+ rFont.SetLanguage( ((const SvxLanguageItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ))).GetLanguage() );
+ }
+
+ sal_uInt16 nRelWidth = ((const SvxCharScaleWidthItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH)).GetValue();
+
+ if ( pOut )
+ {
+ const SvxUnderlineItem& rTextLineColor = (const SvxUnderlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_UNDERLINE );
+ if ( rTextLineColor.GetColor() != COL_TRANSPARENT )
+ pOut->SetTextLineColor( rTextLineColor.GetColor() );
+ else
+ pOut->SetTextLineColor();
+ }
+
+ if ( pOut )
+ {
+ const SvxOverlineItem& rOverlineColor = (const SvxOverlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_OVERLINE );
+ if ( rOverlineColor.GetColor() != COL_TRANSPARENT )
+ pOut->SetOverlineColor( rOverlineColor.GetColor() );
+ else
+ pOut->SetOverlineColor();
+ }
+
+ const SvxLanguageItem* pCJKLanguageItem = NULL;
+
+ if ( aStatus.UseCharAttribs() )
+ {
+ const CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ sal_uInt16 nAttr = 0;
+ EditCharAttrib* pAttrib = GetAttrib( rAttribs, nAttr );
+ while ( pAttrib && ( pAttrib->GetStart() <= nPos ) )
+ {
+ // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
+ // Leere Attribute werden beruecksichtigt( verwendet), da diese
+ // gerade eingestellt wurden.
+ // 12.4.95: Doch keine Leeren Attribute verwenden:
+ // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
+ // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
+ if ( ( pAttrib->Which() != nIgnoreWhich ) &&
+ ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
+ || ( !pNode->Len() ) ) )
+ {
+ DBG_ASSERT( ( pAttrib->Which() >= EE_CHAR_START ) && ( pAttrib->Which() <= EE_FEATURE_END ), "Unglueltiges Attribut in Seek() " );
+ if ( IsScriptItemValid( pAttrib->Which(), nScriptType ) )
+ {
+ pAttrib->SetFont( rFont, pOut );
+ // #i1550# hard color attrib should win over text color from field
+ if ( pAttrib->Which() == EE_FEATURE_FIELD )
+ {
+ EditCharAttrib* pColorAttr = pNode->GetCharAttribs().FindAttrib( EE_CHAR_COLOR, nPos );
+ if ( pColorAttr )
+ pColorAttr->SetFont( rFont, pOut );
+ }
+ }
+ if ( pAttrib->Which() == EE_CHAR_FONTWIDTH )
+ nRelWidth = ((const SvxCharScaleWidthItem*)pAttrib->GetItem())->GetValue();
+ if ( pAttrib->Which() == EE_CHAR_LANGUAGE_CJK )
+ pCJKLanguageItem = (const SvxLanguageItem*) pAttrib->GetItem();
+ }
+ pAttrib = GetAttrib( rAttribs, ++nAttr );
+ }
+ }
+
+ if ( !pCJKLanguageItem )
+ pCJKLanguageItem = (const SvxLanguageItem*) &pNode->GetContentAttribs().GetItem( EE_CHAR_LANGUAGE_CJK );
+
+ rFont.SetCJKContextLanguage( pCJKLanguageItem->GetLanguage() );
+
+ if ( rFont.GetKerning() && IsKernAsianPunctuation() && ( nScriptType == i18n::ScriptType::ASIAN ) )
+ rFont.SetKerning( rFont.GetKerning() | KERNING_ASIAN );
+
+ if ( aStatus.DoNotUseColors() )
+ {
+ // Hack fuer DL,weil JOE staendig die Pooldefaults verbiegt!
+ // const SvxColorItem& rColorItem = (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR );
+ rFont.SetColor( /* rColorItem.GetValue() */ COL_BLACK );
+ }
+
+ if ( aStatus.DoStretch() || ( nRelWidth != 100 ) )
+ {
+ // Fuer das aktuelle Ausgabegeraet, weil es sonst bei einem
+ // Drucker als RefDev auf dem Bildschirm #?!@' aussieht!
+ OutputDevice* pDev = pOut ? pOut : GetRefDevice();
+ rFont.SetPhysFont( pDev );
+ FontMetric aMetric( pDev->GetFontMetric() );
+ // Fuer die Hoehe nicht die Metriken nehmen, da das bei
+ // Hoch-/Tiefgestellt schief geht.
+ Size aRealSz( aMetric.GetSize().Width(), rFont.GetSize().Height() );
+ if ( aStatus.DoStretch() )
+ {
+ if ( nStretchY != 100 )
+ {
+ aRealSz.Height() *= nStretchY;
+ aRealSz.Height() /= 100;
+ }
+ if ( nStretchX != 100 )
+ {
+ aRealSz.Width() *= nStretchX;
+ aRealSz.Width() /= 100;
+
+ // Auch das Kerning: (long wegen Zwischenergebnis)
+ long nKerning = rFont.GetFixKerning();
+/*
+ Die Ueberlegung war: Wenn neg. Kerning, aber StretchX = 200
+ => Nicht das Kerning verdoppelt, also die Buchstaben weiter
+ zusammenziehen
+ ---------------------------
+ Kern StretchX =>Kern
+ ---------------------------
+ >0 <100 < (Proportional)
+ <0 <100 < (Proportional)
+ >0 >100 > (Proportional)
+ <0 >100 < (Der Betrag, also Antiprop)
+*/
+ if ( ( nKerning < 0 ) && ( nStretchX > 100 ) )
+ {
+ // Antiproportional
+ nKerning *= 100;
+ nKerning /= nStretchX;
+ }
+ else if ( nKerning )
+ {
+ // Proportional
+ nKerning *= nStretchX;
+ nKerning /= 100;
+ }
+ rFont.SetFixKerning( (short)nKerning );
+ }
+ }
+ if ( nRelWidth != 100 )
+ {
+ aRealSz.Width() *= nRelWidth;
+ aRealSz.Width() /= 100;
+ }
+ rFont.SetSize( aRealSz );
+ // Font wird nicht restauriert...
+ }
+
+ if ( ( ( rFont.GetColor() == COL_AUTO ) || ( IsForceAutoColor() ) ) && pOut )
+ {
+ // #i75566# Do not use AutoColor when printing OR Pdf export
+ const bool bPrinting(OUTDEV_PRINTER == pOut->GetOutDevType());
+ const bool bPDFExporting(0 != pOut->GetPDFWriter());
+
+ if ( IsAutoColorEnabled() && !bPrinting && !bPDFExporting)
+ {
+ // Never use WindowTextColor on the printer
+ rFont.SetColor( GetAutoColor() );
+ }
+ else
+ {
+ if ( ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() )
+ rFont.SetColor( COL_WHITE );
+ else
+ rFont.SetColor( COL_BLACK );
+ }
+ }
+
+ if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) &&
+ ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
+ {
+ sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
+ if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
+ rFont.SetUnderline( UNDERLINE_SINGLE );
+ else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
+ rFont.SetUnderline( UNDERLINE_BOLD );
+ else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
+ rFont.SetUnderline( UNDERLINE_DOTTED );
+ else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
+ rFont.SetUnderline( UNDERLINE_DOTTED );
+ else if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
+ rFont.SetColor( Color( COL_RED ) );
+ else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
+ rFont.SetColor( Color( COL_LIGHTGRAY ) );
+ if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
+ rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
+ rFont.SetTransparent( FALSE );
+ }
+ else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
+ {
+ rFont.SetUnderline( UNDERLINE_WAVE );
+ if( pOut )
+ pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
+ }
+ }
+}
+
+void ImpEditEngine::RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont )
+{
+ // Fuer Zeilenhoehe bei Hoch/Tief erstmal ohne Propr!
+ sal_uInt16 nPropr = rFont.GetPropr();
+ DBG_ASSERT( ( nPropr == 100 ) || rFont.GetEscapement(), "Propr ohne Escape?!" );
+ if ( nPropr != 100 )
+ {
+ rFont.SetPropr( 100 );
+ rFont.SetPhysFont( pRefDev );
+ }
+ sal_uInt16 nAscent, nDescent;
+
+ FontMetric aMetric( pRefDev->GetFontMetric() );
+ nAscent = (sal_uInt16)aMetric.GetAscent();
+ if ( IsAddExtLeading() )
+ nAscent = sal::static_int_cast< sal_uInt16 >(
+ nAscent + aMetric.GetExtLeading() );
+ nDescent = (sal_uInt16)aMetric.GetDescent();
+
+ if ( IsFixedCellHeight() )
+ {
+/* creating correct proportional ascent and descent values lead to problems if different fonts are used
+ in the same portion, it results in a bigger linespacing.
+ sal_Int32 f = nAscent + nDescent;
+ if ( f )
+ {
+ sal_Int32 nHeight = ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() );
+ nAscent = (sal_Int16)(( nHeight * nAscent ) / f );
+ nDescent = (sal_Int16)(nHeight - nAscent);
+ }
+*/
+ nAscent = sal::static_int_cast< sal_uInt16 >( rFont.GetHeight() );
+ nDescent= sal::static_int_cast< sal_uInt16 >( ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() ) - nAscent );
+ }
+ else
+ {
+ sal_uInt16 nIntLeading = ( aMetric.GetIntLeading() > 0 ) ? (sal_uInt16)aMetric.GetIntLeading() : 0;
+ // Fonts ohne Leading bereiten Probleme
+ if ( ( nIntLeading == 0 ) && ( pRefDev->GetOutDevType() == OUTDEV_PRINTER ) )
+ {
+ // Da schaun wir mal, was fuer eine Leading ich auf dem
+ // Bildschirm erhalte
+ VirtualDevice* pVDev = GetVirtualDevice( pRefDev->GetMapMode(), pRefDev->GetDrawMode() );
+ rFont.SetPhysFont( pVDev );
+ aMetric = pVDev->GetFontMetric();
+
+ // Damit sich die Leading nicht wieder rausrechnet,
+ // wenn die ganze Zeile den Font hat, nTmpLeading.
+
+ // 4/96: Kommt bei HP Laserjet 4V auch nicht hin
+ // => Werte komplett vom Bildschirm holen.
+ // sal_uInt16 nTmpLeading = (sal_uInt16)aMetric.GetLeading();
+ // nAscent += nTmpLeading;
+ nAscent = (sal_uInt16)aMetric.GetAscent();
+ nDescent = (sal_uInt16)aMetric.GetDescent();
+ // nLeading = (sal_uInt16)aMetric.GetLeading();
+ }
+ }
+ if ( nAscent > rCurMetrics.nMaxAscent )
+ rCurMetrics.nMaxAscent = nAscent;
+ if ( nDescent > rCurMetrics.nMaxDescent )
+ rCurMetrics.nMaxDescent= nDescent;
+ // Sonderbehandlung Hoch/Tief:
+ if ( rFont.GetEscapement() )
+ {
+ // Jetzt unter Beruecksichtigung von Escape/Propr
+ // Ascent oder Descent ggf vergroessern
+ short nDiff = (short)(rFont.GetSize().Height()*rFont.GetEscapement()/100L);
+ if ( rFont.GetEscapement() > 0 )
+ {
+ nAscent = (sal_uInt16) (((long)nAscent)*nPropr/100 + nDiff);
+ if ( nAscent > rCurMetrics.nMaxAscent )
+ rCurMetrics.nMaxAscent = nAscent;
+ }
+ else // muss < 0 sein
+ {
+ nDescent = (sal_uInt16) (((long)nDescent)*nPropr/100 - nDiff);
+ if ( nDescent > rCurMetrics.nMaxDescent )
+ rCurMetrics.nMaxDescent= nDescent;
+ }
+ }
+}
+
+void ImpEditEngine::Paint( OutputDevice* pOutDev, Rectangle aClipRec, Point aStartPos, sal_Bool bStripOnly, short nOrientation )
+{
+ if ( !GetUpdateMode() && !bStripOnly )
+ return;
+
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ long nFirstVisXPos = - pOutDev->GetMapMode().GetOrigin().X();
+ long nFirstVisYPos = - pOutDev->GetMapMode().GetOrigin().Y();
+
+ EditLine* pLine;
+ Point aTmpPos;
+ Point aRedLineTmpPos;
+ DBG_ASSERT( GetParaPortions().Count(), "Keine ParaPortion?!" );
+ SvxFont aTmpFont( GetParaPortions()[0]->GetNode()->GetCharAttribs().GetDefFont() );
+ Font aOldFont( pOutDev->GetFont() );
+ vcl::PDFExtOutDevData* pPDFExtOutDevData = PTR_CAST( vcl::PDFExtOutDevData, pOutDev->GetExtOutDevData() );
+
+ // Bei gedrehtem Text wird aStartPos als TopLeft angesehen, da andere
+ // Informationen fehlen, und sowieso das ganze Object ungescrollt
+ // dargestellt wird.
+ // Das Rechteck ist unendlich gross.
+ Point aOrigin( aStartPos );
+ double nCos = 0.0, nSin = 0.0;
+ if ( nOrientation )
+ {
+ double nRealOrientation = nOrientation*F_PI1800;
+ nCos = cos( nRealOrientation );
+ nSin = sin( nRealOrientation );
+ }
+
+ // #110496# Added some more optional metafile comments. This
+ // change: factored out some duplicated code.
+ GDIMetaFile* pMtf = pOutDev->GetConnectMetaFile();
+ const bool bMetafileValid( pMtf != NULL );
+
+ // Fuer OnlineSpelling:
+// EditPaM aCursorPos;
+// if( GetStatus().DoOnlineSpelling() && pActiveView )
+// aCurPos = pActiveView->pImpEditView->GetEditSelections().Max();
+
+ // --------------------------------------------------
+ // Ueber alle Absaetze...
+ // --------------------------------------------------
+ for ( sal_uInt16 n = 0; n < GetParaPortions().Count(); n++ )
+ {
+ ParaPortion* pPortion = GetParaPortions().GetObject( n );
+ DBG_ASSERT( pPortion, "NULL-Pointer in TokenList in Paint" );
+ // falls beim Tippen Idle-Formatierung, asynchrones Paint.
+ // Unsichtbare Portions koennen ungueltig sein.
+ if ( pPortion->IsVisible() && pPortion->IsInvalid() )
+ return;
+
+ if ( pPDFExtOutDevData )
+ pPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
+
+ long nParaHeight = pPortion->GetHeight();
+ sal_uInt16 nIndex = 0;
+ if ( pPortion->IsVisible() && (
+ ( !IsVertical() && ( ( aStartPos.Y() + nParaHeight ) > aClipRec.Top() ) ) ||
+ ( IsVertical() && ( ( aStartPos.X() - nParaHeight ) < aClipRec.Right() ) ) ) )
+
+ {
+ // --------------------------------------------------
+ // Ueber die Zeilen des Absatzes...
+ // --------------------------------------------------
+ sal_uInt16 nLines = pPortion->GetLines().Count();
+ sal_uInt16 nLastLine = nLines-1;
+
+ if ( !IsVertical() )
+ aStartPos.Y() += pPortion->GetFirstLineOffset();
+ else
+ aStartPos.X() -= pPortion->GetFirstLineOffset();
+
+ Point aParaStart( aStartPos );
+
+ const SvxLineSpacingItem& rLSItem = ((const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ));
+ sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
+ ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
+ for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
+ {
+ pLine = pPortion->GetLines().GetObject(nLine);
+ DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in UpdateViews" );
+ aTmpPos = aStartPos;
+ if ( !IsVertical() )
+ {
+ aTmpPos.X() += pLine->GetStartPosX();
+ aTmpPos.Y() += pLine->GetMaxAscent();
+ aStartPos.Y() += pLine->GetHeight();
+ }
+ else
+ {
+ aTmpPos.Y() += pLine->GetStartPosX();
+ aTmpPos.X() -= pLine->GetMaxAscent();
+ aStartPos.X() -= pLine->GetHeight();
+ }
+
+ if ( ( !IsVertical() && ( aStartPos.Y() > aClipRec.Top() ) )
+ || ( IsVertical() && aStartPos.X() < aClipRec.Right() ) )
+ {
+ // Why not just also call when stripping portions? This will give the correct values
+ // and needs no position corrections in OutlinerEditEng::DrawingText which tries to call
+ // PaintBullet correctly; exactly what GetEditEnginePtr()->PaintingFirstLine
+ // does, too. No change for not-layouting (painting).
+ if(0 == nLine) // && !bStripOnly)
+ {
+ // VERT???
+ GetEditEnginePtr()->PaintingFirstLine( n, aParaStart, aTmpPos.Y(), aOrigin, nOrientation, pOutDev );
+ }
+
+ // --------------------------------------------------
+ // Ueber die Portions der Zeile...
+ // --------------------------------------------------
+ nIndex = pLine->GetStart();
+ for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
+ {
+ DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
+ TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
+ DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
+
+ long nPortionXOffset = GetPortionXOffset( pPortion, pLine, y );
+ if ( !IsVertical() )
+ {
+ aTmpPos.X() = aStartPos.X() + nPortionXOffset;
+ if ( aTmpPos.X() > aClipRec.Right() )
+ break; // Keine weitere Ausgabe in Zeile noetig
+ }
+ else
+ {
+ aTmpPos.Y() = aStartPos.Y() + nPortionXOffset;
+ if ( aTmpPos.Y() > aClipRec.Bottom() )
+ break; // Keine weitere Ausgabe in Zeile noetig
+ }
+
+ // R2L replaces with obove...
+ // New position after processing R2L text...
+// R2L if ( nR2LWidth && !pTextPortion->GetRightToLeft() )
+// R2L {
+// R2L if ( !IsVertical() )
+// R2L aTmpPos.X() += nR2LWidth;
+// R2L else
+// R2L aTmpPos.Y() += nR2LWidth;
+// R2L
+// R2L nR2LWidth = 0;
+// R2L }
+
+ switch ( pTextPortion->GetKind() )
+ {
+ case PORTIONKIND_TEXT:
+ case PORTIONKIND_FIELD:
+ case PORTIONKIND_HYPHENATOR:
+ {
+ SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
+
+ BOOL bDrawFrame = FALSE;
+
+ if ( ( pTextPortion->GetKind() == PORTIONKIND_FIELD ) && !aTmpFont.IsTransparent() &&
+ ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() &&
+ ( IsAutoColorEnabled() && ( pOutDev->GetOutDevType() != OUTDEV_PRINTER ) ) )
+ {
+ aTmpFont.SetTransparent( TRUE );
+ pOutDev->SetFillColor();
+ pOutDev->SetLineColor( GetAutoColor() );
+ bDrawFrame = TRUE;
+ }
+
+#ifdef EDITDEBUG
+ if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTGRAY );
+ aTmpFont.SetTransparent( sal_False );
+ }
+ if ( pTextPortion->GetRightToLeft() )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTGRAY );
+ aTmpFont.SetTransparent( sal_False );
+ }
+ else if ( GetScriptType( EditPaM( pPortion->GetNode(), nIndex+1 ) ) == i18n::ScriptType::COMPLEX )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTCYAN );
+ aTmpFont.SetTransparent( sal_False );
+ }
+#endif
+ aTmpFont.SetPhysFont( pOutDev );
+
+ // #114278# Saving both layout mode and language (since I'm
+ // potentially changing both)
+ pOutDev->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
+ ImplInitLayoutMode( pOutDev, n, nIndex );
+ ImplInitDigitMode( pOutDev, 0, 0, 0, aTmpFont.GetLanguage() );
+
+ XubString aText;
+ USHORT nTextStart = 0;
+ USHORT nTextLen = 0;
+ const sal_Int32* pDXArray = 0;
+ sal_Int32* pTmpDXArray = 0;
+
+ if ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
+ {
+ aText = *pPortion->GetNode();
+ nTextStart = nIndex;
+ nTextLen = pTextPortion->GetLen();
+ pDXArray = pLine->GetCharPosArray().GetData()+( nIndex-pLine->GetStart() );
+
+ // --> FME 2005-10-18 #i55716# Paint control characters
+ if ( aStatus.MarkFields() )
+ {
+ xub_StrLen nTmpIdx;
+ const xub_StrLen nTmpEnd = nTextStart + pTextPortion->GetLen();
+
+ for ( nTmpIdx = nTextStart; nTmpIdx <= nTmpEnd ; ++nTmpIdx )
+ {
+ const sal_Unicode cChar = ( nTmpIdx != aText.Len() && ( nTmpIdx != nTextStart || 0 == nTextStart ) ) ?
+ aText.GetChar( nTmpIdx ) :
+ 0;
+
+ if ( 0x200B == cChar || 0x2060 == cChar )
+ {
+ const String aBlank( ' ' );
+ long nHalfBlankWidth = aTmpFont.QuickGetTextSize( pOutDev, aBlank, 0, 1, 0 ).Width() / 2;
+
+ const long nAdvanceX = ( nTmpIdx == nTmpEnd ?
+ pTextPortion->GetSize().Width() :
+ pDXArray[ nTmpIdx - nTextStart ] ) - nHalfBlankWidth;
+ const long nAdvanceY = -pLine->GetMaxAscent();
+
+ Point aTopLeftRectPos( aTmpPos );
+ if ( !IsVertical() )
+ {
+ aTopLeftRectPos.X() += nAdvanceX;
+ aTopLeftRectPos.Y() += nAdvanceY;
+ }
+ else
+ {
+ aTopLeftRectPos.Y() += nAdvanceX;
+ aTopLeftRectPos.X() -= nAdvanceY;
+ }
+
+ Point aBottomRightRectPos( aTopLeftRectPos );
+ if ( !IsVertical() )
+ {
+ aBottomRightRectPos.X() += 2 * nHalfBlankWidth;
+ aBottomRightRectPos.Y() += pLine->GetHeight();
+ }
+ else
+ {
+ aBottomRightRectPos.X() -= pLine->GetHeight();
+ aBottomRightRectPos.Y() += 2 * nHalfBlankWidth;
+ }
+
+ pOutDev->Push( PUSH_FILLCOLOR );
+ pOutDev->Push( PUSH_LINECOLOR );
+ pOutDev->SetFillColor( COL_LIGHTGRAY );
+ pOutDev->SetLineColor( COL_LIGHTGRAY );
+
+ const Rectangle aBackRect( aTopLeftRectPos, aBottomRightRectPos );
+ pOutDev->DrawRect( aBackRect );
+
+ pOutDev->Pop();
+ pOutDev->Pop();
+
+ if ( 0x200B == cChar )
+ {
+ const String aSlash( '/' );
+ const short nOldEscapement = aTmpFont.GetEscapement();
+ const BYTE nOldPropr = aTmpFont.GetPropr();
+
+ aTmpFont.SetEscapement( -20 );
+ aTmpFont.SetPropr( 25 );
+ aTmpFont.SetPhysFont( pOutDev );
+
+ const Size aSlashSize = aTmpFont.QuickGetTextSize( pOutDev, aSlash, 0, 1, 0 );
+ Point aSlashPos( aTmpPos );
+ const long nAddX = nHalfBlankWidth - aSlashSize.Width() / 2;
+ if ( !IsVertical() )
+ {
+ aSlashPos.X() = aTopLeftRectPos.X() + nAddX;
+ }
+ else
+ {
+ aSlashPos.Y() = aTopLeftRectPos.Y() + nAddX;
+ }
+
+ aTmpFont.QuickDrawText( pOutDev, aSlashPos, aSlash, 0, 1, 0 );
+
+ aTmpFont.SetEscapement( nOldEscapement );
+ aTmpFont.SetPropr( nOldPropr );
+ aTmpFont.SetPhysFont( pOutDev );
+ }
+ }
+ }
+ }
+ // <--
+ }
+ else if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
+ {
+ EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
+ DBG_ASSERT( pAttr, "Feld nicht gefunden" );
+ DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
+ aText = ((EditCharAttribField*)pAttr)->GetFieldValue();
+ nTextStart = 0;
+ nTextLen = aText.Len();
+
+ pTmpDXArray = new sal_Int32[ aText.Len() ];
+ pDXArray = pTmpDXArray;
+ Font _aOldFont( GetRefDevice()->GetFont() );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
+ if ( aStatus.DoRestoreFont() )
+ GetRefDevice()->SetFont( _aOldFont );
+
+ // add a meta file comment if we record to a metafile
+ if( bMetafileValid )
+ {
+ SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if( pFieldData )
+ pMtf->AddAction( pFieldData->createBeginComment() );
+ }
+ }
+
+ }
+ else if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
+ {
+ if ( pTextPortion->GetExtraValue() )
+ aText = pTextPortion->GetExtraValue();
+ aText += CH_HYPH;
+ nTextStart = 0;
+ nTextLen = aText.Len();
+
+ // #b6668980# crash when accessing 0 pointer in pDXArray
+ pTmpDXArray = new sal_Int32[ aText.Len() ];
+ pDXArray = pTmpDXArray;
+ Font _aOldFont( GetRefDevice()->GetFont() );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
+ if ( aStatus.DoRestoreFont() )
+ GetRefDevice()->SetFont( _aOldFont );
+ }
+
+ long nTxtWidth = pTextPortion->GetSize().Width();
+
+ Point aOutPos( aTmpPos );
+ aRedLineTmpPos = aTmpPos;
+ // In RTL portions spell markup pos should be at the start of the
+ // first chara as well. That is on the right end of the portion
+ if (pTextPortion->IsRightToLeft())
+ aRedLineTmpPos.X() += pTextPortion->GetSize().Width();
+
+//L2R if ( pTextPortion->GetRightToLeft() )
+//L2R {
+//L2R sal_uInt16 nNextPortion = y+1;
+//L2R while ( nNextPortion <= pLine->GetEndPortion() )
+//L2R {
+//L2R TextPortion* pNextTextPortion = pPortion->GetTextPortions().GetObject( nNextPortion );
+//L2R if ( pNextTextPortion->GetRightToLeft() )
+//L2R {
+//L2R if ( !IsVertical() )
+//L2R aOutPos.X() += pNextTextPortion->GetSize().Width();
+//L2R else
+//L2R aOutPos.Y() += pNextTextPortion->GetSize().Width();
+//L2R }
+//L2R else
+//L2R break;
+//L2R nNextPortion++;
+//L2R }
+//L2R }
+ if ( bStripOnly )
+ {
+ EEngineData::WrongSpellVector aWrongSpellVector;
+
+ if(GetStatus().DoOnlineSpelling() && pTextPortion->GetLen())
+ {
+ WrongList* pWrongs = pPortion->GetNode()->GetWrongList();
+
+ if(pWrongs && pWrongs->HasWrongs())
+ {
+ sal_uInt16 nStart(nIndex);
+ sal_uInt16 nEnd(0);
+ sal_Bool bWrong(pWrongs->NextWrong(nStart, nEnd));
+ const sal_uInt16 nMaxEnd(nIndex + pTextPortion->GetLen());
+
+ while(bWrong)
+ {
+ if(nStart >= nMaxEnd)
+ {
+ break;
+ }
+
+ if(nStart < nIndex)
+ {
+ nStart = nIndex;
+ }
+
+ if(nEnd > nMaxEnd)
+ {
+ nEnd = nMaxEnd;
+ }
+
+ // add to vector
+ aWrongSpellVector.push_back(EEngineData::WrongSpellClass(nStart, nEnd));
+
+ // goto next index
+ nStart = nEnd + 1;
+
+ if(nEnd < nMaxEnd)
+ {
+ bWrong = pWrongs->NextWrong(nStart, nEnd);
+ }
+ else
+ {
+ bWrong = sal_False;
+ }
+ }
+ }
+ }
+
+ const SvxFieldData* pFieldData = 0;
+
+ if(PORTIONKIND_FIELD == pTextPortion->GetKind())
+ {
+ EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+ SvxFieldItem* pFieldItem = PTR_CAST(SvxFieldItem, pAttr->GetItem());
+
+ if(pFieldItem)
+ {
+ pFieldData = pFieldItem->GetField();
+ }
+ }
+
+ // support for EOC, EOW, EOS TEXT comments. To support that,
+ // the locale is needed. With the locale and a XBreakIterator it is
+ // possible to re-create the text marking info on primitive level
+ const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(), nIndex + 1)));
+
+ // create EOL and EOP bools
+ const bool bEndOfLine(y == pLine->GetEndPortion());
+ const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
+
+ // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
+ // consequence, but also already set at pOutDev)
+ const Color aOverlineColor(pOutDev->GetOverlineColor());
+
+ // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
+ // consequence, but also already set at pOutDev)
+ const Color aTextLineColor(pOutDev->GetTextLineColor());
+
+ // Unicode code points conversion according to ctl text numeral setting
+ ImplInitDigitMode( 0, &aText, nTextStart, nTextLen, aTmpFont.GetLanguage() );
+
+ // StripPortions() data callback
+ GetEditEnginePtr()->DrawingText( aOutPos, aText, nTextStart, nTextLen, pDXArray,
+ aTmpFont, n, nIndex, pTextPortion->GetRightToLeft(),
+ aWrongSpellVector.size() ? &aWrongSpellVector : 0,
+ pFieldData,
+ bEndOfLine, bEndOfParagraph, false, // support for EOL/EOP TEXT comments
+ &aLocale,
+ aOverlineColor,
+ aTextLineColor);
+ }
+ else
+ {
+ short nEsc = aTmpFont.GetEscapement();
+ if ( nOrientation )
+ {
+ // Bei Hoch/Tief selbst Hand anlegen:
+ if ( aTmpFont.GetEscapement() )
+ {
+ long nDiff = aTmpFont.GetSize().Height() * aTmpFont.GetEscapement() / 100L;
+ if ( !IsVertical() )
+ aOutPos.Y() -= nDiff;
+ else
+ aOutPos.X() += nDiff;
+ aRedLineTmpPos = aOutPos;
+ aTmpFont.SetEscapement( 0 );
+ }
+
+ aOutPos = lcl_ImplCalcRotatedPos( aOutPos, aOrigin, nSin, nCos );
+ aTmpFont.SetOrientation( aTmpFont.GetOrientation()+nOrientation );
+ aTmpFont.SetPhysFont( pOutDev );
+
+ }
+ // nur ausgeben, was im sichtbaren Bereich beginnt:
+ // Wichtig, weil Bug bei einigen Grafikkarten bei transparentem Font, Ausgabe bei neg.
+ if ( nOrientation || ( !IsVertical() && ( ( aTmpPos.X() + nTxtWidth ) >= nFirstVisXPos ) )
+ || ( IsVertical() && ( ( aTmpPos.Y() + nTxtWidth ) >= nFirstVisYPos ) ) )
+ {
+ if ( nEsc && ( ( aTmpFont.GetUnderline() != UNDERLINE_NONE ) ) )
+ {
+ // Das Hoch/Tief ohne Underline malen, das Underline
+ // auf der BaseLine der Original-Fonthoehe ausgeben...
+
+ // Aber nur, wenn davor auch Unterstrichen!
+ sal_Bool bSpecialUnderline = sal_False;
+ EditCharAttrib* pPrev = pPortion->GetNode()->GetCharAttribs().FindAttrib( EE_CHAR_ESCAPEMENT, nIndex );
+ if ( pPrev )
+ {
+ SvxFont aDummy;
+ // Unterstreichung davor?
+ if ( pPrev->GetStart() )
+ {
+ SeekCursor( pPortion->GetNode(), pPrev->GetStart(), aDummy );
+ if ( aDummy.GetUnderline() != UNDERLINE_NONE )
+ bSpecialUnderline = sal_True;
+ }
+ if ( !bSpecialUnderline && ( pPrev->GetEnd() < pPortion->GetNode()->Len() ) )
+ {
+ SeekCursor( pPortion->GetNode(), pPrev->GetEnd()+1, aDummy );
+ if ( aDummy.GetUnderline() != UNDERLINE_NONE )
+ bSpecialUnderline = sal_True;
+ }
+ }
+ if ( bSpecialUnderline )
+ {
+ Size aSz = aTmpFont.GetPhysTxtSize( pOutDev, aText, nTextStart, nTextLen );
+ BYTE nProp = aTmpFont.GetPropr();
+ aTmpFont.SetEscapement( 0 );
+ aTmpFont.SetPropr( 100 );
+ aTmpFont.SetPhysFont( pOutDev );
+ String aBlanks;
+ aBlanks.Fill( nTextLen, ' ' );
+ Point aUnderlinePos( aOutPos );
+ if ( nOrientation )
+ aUnderlinePos = lcl_ImplCalcRotatedPos( aTmpPos, aOrigin, nSin, nCos );
+ pOutDev->DrawStretchText( aUnderlinePos, aSz.Width(), aBlanks, 0, nTextLen );
+
+ aTmpFont.SetUnderline( UNDERLINE_NONE );
+ if ( !nOrientation )
+ aTmpFont.SetEscapement( nEsc );
+ aTmpFont.SetPropr( nProp );
+ aTmpFont.SetPhysFont( pOutDev );
+ }
+ }
+ Point aRealOutPos( aOutPos );
+ if ( ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
+ && pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed
+ && pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation )
+ {
+ aRealOutPos.X() += pTextPortion->GetExtraInfos()->nPortionOffsetX;
+ }
+
+ // --> FME 2005-06-17 #i37132# RTL portions with
+ // compressed blank should not paint this blank:
+ if ( pTextPortion->IsRightToLeft() && nTextLen >= 2 &&
+ pDXArray[ nTextLen - 1 ] ==
+ pDXArray[ nTextLen - 2 ] &&
+ ' ' == aText.GetChar( nTextStart + nTextLen - 1 ) )
+ --nTextLen;
+ // <--
+
+ // output directly
+ aTmpFont.QuickDrawText( pOutDev, aRealOutPos, aText, nTextStart, nTextLen, pDXArray );
+
+ if ( bDrawFrame )
+ {
+ Point aTopLeft( aTmpPos );
+ aTopLeft.Y() -= pLine->GetMaxAscent();
+ if ( nOrientation )
+ aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
+ Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
+ pOutDev->DrawRect( aRect );
+ }
+
+
+ // PDF export:
+ if ( pPDFExtOutDevData )
+ {
+ if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
+ {
+ EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
+ SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if ( pFieldData->ISA( SvxURLField ) )
+ {
+ Point aTopLeft( aTmpPos );
+ aTopLeft.Y() -= pLine->GetMaxAscent();
+// if ( nOrientation )
+// aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
+
+ Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
+ vcl::PDFExtOutDevBookmarkEntry aBookmark;
+ aBookmark.nLinkId = pPDFExtOutDevData->CreateLink( aRect );
+ aBookmark.aBookmark = ((SvxURLField*)pFieldData)->GetURL();
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
+ rBookmarks.push_back( aBookmark );
+ }
+ }
+ }
+ }
+
+ // comment
+
+
+
+
+ }
+
+#ifndef SVX_LIGHT
+ if ( GetStatus().DoOnlineSpelling() && pPortion->GetNode()->GetWrongList()->HasWrongs() && pTextPortion->GetLen() )
+ {
+ {//#105750# adjust LinePos for superscript or subscript text
+ short _nEsc = aTmpFont.GetEscapement();
+ if( _nEsc )
+ {
+ long nShift = ((_nEsc*long(aTmpFont.GetSize().Height()))/ 100L);
+ if( !IsVertical() )
+ aRedLineTmpPos.Y() -= nShift;
+ else
+ aRedLineTmpPos.X() += nShift;
+ }
+ }
+ Color aOldColor( pOutDev->GetLineColor() );
+ pOutDev->SetLineColor( Color( GetColorConfig().GetColorValue( svtools::SPELL ).nColor ) );
+ lcl_DrawRedLines( pOutDev, aTmpFont.GetSize().Height(), aRedLineTmpPos, nIndex, nIndex + pTextPortion->GetLen(), pDXArray, pPortion->GetNode()->GetWrongList(), nOrientation, aOrigin, IsVertical(), pTextPortion->IsRightToLeft() );
+ pOutDev->SetLineColor( aOldColor );
+ }
+#endif // !SVX_LIGHT
+ }
+
+ pOutDev->Pop();
+
+ if ( pTmpDXArray )
+ delete[] pTmpDXArray;
+
+// R2L if ( !pTextPortion->GetRightToLeft() )
+// R2L {
+// R2L if ( !IsVertical() )
+// R2L aTmpPos.X() += nTxtWidth;
+// R2L else
+// R2L aTmpPos.Y() += nTxtWidth;
+// R2L }
+// R2L else
+// R2L {
+// R2L nR2LWidth += nTxtWidth;
+// R2L }
+
+ if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
+ {
+ EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
+ DBG_ASSERT( pAttr, "Feld nicht gefunden" );
+ DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
+
+ // add a meta file comment if we record to a metafile
+ if( bMetafileValid )
+ {
+ SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
+
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if( pFieldData )
+ pMtf->AddAction( pFieldData->createEndComment() );
+ }
+ }
+
+ }
+
+ }
+ break;
+// case PORTIONKIND_EXTRASPACE:
+ case PORTIONKIND_TAB:
+ {
+ if ( pTextPortion->GetExtraValue() && ( pTextPortion->GetExtraValue() != ' ' ) )
+ {
+ SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
+ aTmpFont.SetTransparent( sal_False );
+ aTmpFont.SetEscapement( 0 );
+ aTmpFont.SetPhysFont( pOutDev );
+ long nCharWidth = aTmpFont.QuickGetTextSize( pOutDev, pTextPortion->GetExtraValue(), 0, 1, NULL ).Width();
+ long nChars = 2;
+ if( nCharWidth )
+ nChars = pTextPortion->GetSize().Width() / nCharWidth;
+ if ( nChars < 2 )
+ nChars = 2; // wird durch DrawStretchText gestaucht.
+ else if ( nChars == 2 )
+ nChars = 3; // sieht besser aus
+
+ String aText;
+ aText.Fill( (USHORT)nChars, pTextPortion->GetExtraValue() );
+ pOutDev->DrawStretchText( aTmpPos, pTextPortion->GetSize().Width(), aText );
+ }
+ }
+ break;
+ }
+ nIndex = nIndex + pTextPortion->GetLen();
+ }
+ }
+
+ if ( ( nLine != nLastLine ) && !aStatus.IsOutliner() )
+ {
+ if ( !IsVertical() )
+ aStartPos.Y() += nSBL;
+ else
+ aStartPos.X() -= nSBL;
+ }
+
+ // keine sichtbaren Aktionen mehr?
+ if ( !IsVertical() && ( aStartPos.Y() >= aClipRec.Bottom() ) )
+ break;
+ else if ( IsVertical() && ( aStartPos.X() <= aClipRec.Left() ) )
+ break;
+ }
+
+ if ( !aStatus.IsOutliner() )
+ {
+ const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ long nUL = GetYValue( rULItem.GetLower() );
+ if ( !IsVertical() )
+ aStartPos.Y() += nUL;
+ else
+ aStartPos.X() -= nUL;
+ }
+ }
+ else
+ {
+ if ( !IsVertical() )
+ aStartPos.Y() += nParaHeight;
+ else
+ aStartPos.X() -= nParaHeight;
+ }
+
+ if ( pPDFExtOutDevData )
+ pPDFExtOutDevData->EndStructureElement();
+
+ // keine sichtbaren Aktionen mehr?
+ if ( !IsVertical() && ( aStartPos.Y() > aClipRec.Bottom() ) )
+ break;
+ if ( IsVertical() && ( aStartPos.X() < aClipRec.Left() ) )
+ break;
+ }
+ if ( aStatus.DoRestoreFont() )
+ pOutDev->SetFont( aOldFont );
+}
+
+void ImpEditEngine::Paint( ImpEditView* pView, const Rectangle& rRec, sal_Bool bUseVirtDev )
+{
+ DBG_ASSERT( pView, "Keine View - Kein Paint!" );
+ DBG_CHKOBJ( GetEditEnginePtr(), EditEngine, 0 );
+
+ if ( !GetUpdateMode() || IsInUndo() )
+ return;
+
+ // Schnittmenge aus Paintbereich und OutputArea.
+ Rectangle aClipRec( pView->GetOutputArea() );
+ aClipRec.Intersection( rRec );
+
+ Window* pOutWin = pView->GetWindow();
+
+ if ( bUseVirtDev )
+ {
+ Rectangle aClipRecPixel( pOutWin->LogicToPixel( aClipRec ) );
+ if ( !IsVertical() )
+ {
+ // etwas mehr, falls abgerundet!
+ aClipRecPixel.Right() += 1;
+ aClipRecPixel.Bottom() += 1;
+ }
+ else
+ {
+ aClipRecPixel.Left() -= 1;
+ aClipRecPixel.Bottom() += 1;
+ }
+
+ // Wenn aClipRecPixel > XXXX, dann invalidieren ?!
+
+ VirtualDevice* pVDev = GetVirtualDevice( pOutWin->GetMapMode(), pOutWin->GetDrawMode() );
+ pVDev->SetDigitLanguage( GetRefDevice()->GetDigitLanguage() );
+
+ {
+ Color aBackgroundColor( pView->GetBackgroundColor() );
+ // #i47161# Check if text is visible on background
+ SvxFont aTmpFont;
+ ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
+ SeekCursor( pNode, 1, aTmpFont );
+ Color aFontColor( aTmpFont.GetColor() );
+ if( aFontColor == COL_AUTO )
+ aFontColor = GetAutoColor();
+
+ // #i69346# check for reverse color of input method attribute
+ if( mpIMEInfos && (mpIMEInfos->aPos.GetNode() == pNode &&
+ mpIMEInfos->pAttribs))
+ {
+ sal_uInt16 nAttr = mpIMEInfos->pAttribs[ 0 ];
+ if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ aFontColor = rStyleSettings.GetHighlightColor() ;
+ }
+ }
+
+ UINT8 nColorDiff = aFontColor.GetColorError( aBackgroundColor );
+ if( nColorDiff < 8 )
+ aBackgroundColor = aFontColor.IsDark() ? COL_WHITE : COL_BLACK;
+ pVDev->SetBackground( aBackgroundColor );
+ }
+
+ sal_Bool bVDevValid = sal_True;
+ Size aOutSz( pVDev->GetOutputSizePixel() );
+ if ( ( aOutSz.Width() < aClipRecPixel.GetWidth() ) ||
+ ( aOutSz.Height() < aClipRecPixel.GetHeight() ) )
+ {
+ bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
+ }
+ else
+ {
+ // Das VirtDev kann bei einem Resize sehr gross werden =>
+ // irgendwann mal kleiner machen!
+ if ( ( aOutSz.Height() > ( aClipRecPixel.GetHeight() + RESDIFF ) ) ||
+ ( aOutSz.Width() > ( aClipRecPixel.GetWidth() + RESDIFF ) ) )
+ {
+ bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
+ }
+ else
+ {
+ pVDev->Erase();
+ }
+ }
+ DBG_ASSERT( bVDevValid, "VDef konnte nicht vergroessert werden!" );
+ if ( !bVDevValid )
+ {
+ Paint( pView, rRec, sal_False /* ohne VDev */ );
+ return;
+ }
+
+ // PaintRect fuer VDev nicht mit alignter Groesse,
+ // da sonst die Zeile darunter auch ausgegeben werden muss:
+ Rectangle aTmpRec( Point( 0, 0 ), aClipRec.GetSize() );
+
+ aClipRec = pOutWin->PixelToLogic( aClipRecPixel );
+ Point aStartPos;
+ if ( !IsVertical() )
+ {
+ aStartPos = aClipRec.TopLeft();
+ aStartPos = pView->GetDocPos( aStartPos );
+ aStartPos.X() *= (-1);
+ aStartPos.Y() *= (-1);
+ }
+ else
+ {
+ aStartPos = aClipRec.TopRight();
+ Point aDocPos( pView->GetDocPos( aStartPos ) );
+ aStartPos.X() = aClipRec.GetSize().Width() + aDocPos.Y();
+ aStartPos.Y() = -aDocPos.X();
+ }
+
+ Paint( pVDev, aTmpRec, aStartPos );
+
+ sal_Bool bClipRegion = sal_False;
+ Region aOldRegion;
+ MapMode aOldMapMode;
+ if ( GetTextRanger() )
+ {
+ // Some problems here with push/pop, why?!
+// pOutWin->Push( PUSH_CLIPREGION|PUSH_MAPMODE );
+ bClipRegion = pOutWin->IsClipRegion();
+ aOldRegion = pOutWin->GetClipRegion();
+ // Wie bekomme ich das Polygon an die richtige Stelle????
+ // Das Polygon bezieht sich auf die View, nicht auf das Window
+ // => Origin umsetzen...
+ aOldMapMode = pOutWin->GetMapMode();
+ Point aOrigin = aOldMapMode.GetOrigin();
+ Point aViewPos = pView->GetOutputArea().TopLeft();
+ aOrigin.Move( aViewPos.X(), aViewPos.Y() );
+ aClipRec.Move( -aViewPos.X(), -aViewPos.Y() );
+ MapMode aNewMapMode( aOldMapMode );
+ aNewMapMode.SetOrigin( aOrigin );
+ pOutWin->SetMapMode( aNewMapMode );
+ pOutWin->SetClipRegion( Region( GetTextRanger()->GetPolyPolygon() ) );
+ }
+
+ pOutWin->DrawOutDev( aClipRec.TopLeft(), aClipRec.GetSize(),
+ Point(0,0), aClipRec.GetSize(), *pVDev );
+
+ if ( GetTextRanger() )
+ {
+// pOutWin->Pop();
+ if ( bClipRegion )
+ pOutWin->SetClipRegion( aOldRegion );
+ else
+ pOutWin->SetClipRegion();
+ pOutWin->SetMapMode( aOldMapMode );
+ }
+
+
+ pView->DrawSelection();
+ }
+ else
+ {
+ Point aStartPos;
+ if ( !IsVertical() )
+ {
+ aStartPos = pView->GetOutputArea().TopLeft();
+ aStartPos.X() -= pView->GetVisDocLeft();
+ aStartPos.Y() -= pView->GetVisDocTop();
+ }
+ else
+ {
+ aStartPos = pView->GetOutputArea().TopRight();
+ aStartPos.X() += pView->GetVisDocTop();
+ aStartPos.Y() -= pView->GetVisDocLeft();
+ }
+
+ // Wenn Doc-Breite < OutputArea,Width, nicht umgebrochene Felder,
+ // stehen die Felder sonst �ber, wenn > Zeile.
+ // ( Oben nicht, da dort bereits Doc-Breite von Formatierung mit drin )
+ if ( !IsVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
+ {
+ long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
+ if ( aClipRec.Left() > nMaxX )
+ return;
+ if ( aClipRec.Right() > nMaxX )
+ aClipRec.Right() = nMaxX;
+ }
+
+ sal_Bool bClipRegion = pOutWin->IsClipRegion();
+ Region aOldRegion = pOutWin->GetClipRegion();
+ pOutWin->IntersectClipRegion( aClipRec );
+
+ Paint( pOutWin, aClipRec, aStartPos );
+
+ if ( bClipRegion )
+ pOutWin->SetClipRegion( aOldRegion );
+ else
+ pOutWin->SetClipRegion();
+
+ pView->DrawSelection();
+ }
+
+}
+
+void ImpEditEngine::InsertContent( ContentNode* pNode, sal_uInt16 nPos )
+{
+ DBG_ASSERT( pNode, "NULL-Poointer in InsertContent! " );
+ DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
+ ParaPortion* pNew = new ParaPortion( pNode );
+ GetParaPortions().Insert( pNew, nPos );
+ aEditDoc.Insert( pNode, nPos );
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPos );
+}
+
+EditPaM ImpEditEngine::SplitContent( sal_uInt16 nNode, sal_uInt16 nSepPos )
+{
+ ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
+ DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
+ DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
+ DBG_ASSERT( nSepPos <= pNode->Len(), "Index im Wald: SplitContent" );
+ EditPaM aPaM( pNode, nSepPos );
+ return ImpInsertParaBreak( aPaM );
+}
+
+EditPaM ImpEditEngine::ConnectContents( sal_uInt16 nLeftNode, sal_Bool bBackward )
+{
+ ContentNode* pLeftNode = aEditDoc.SaveGetObject( nLeftNode );
+ ContentNode* pRightNode = aEditDoc.SaveGetObject( nLeftNode+1 );
+ DBG_ASSERT( pLeftNode, "Ungueltiger linker Node in ConnectContents" );
+ DBG_ASSERT( pRightNode, "Ungueltiger rechter Node in ConnectContents" );
+ DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
+ return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
+}
+
+void ImpEditEngine::SetUpdateMode( sal_Bool bUp, EditView* pCurView, sal_Bool bForceUpdate )
+{
+ sal_Bool bChanged = ( GetUpdateMode() != bUp );
+
+ // Beim Umschalten von sal_True auf sal_False waren alle Selektionen sichtbar,
+ // => Wegmalen
+ // Umgekehrt waren alle unsichtbar => malen
+
+// DrawAllSelections(); sieht im Outliner schlecht aus !
+// EditView* pView = aEditViewList.First();
+// while ( pView )
+// {
+// DBG_CHKOBJ( pView, EditView, 0 );
+// pView->pImpEditView->DrawSelection();
+// pView = aEditViewList.Next();
+// }
+
+ // Wenn !bFormatted, also z.B. nach SetText, braucht bei UpdateMode sal_True
+ // nicht sofort formatiert werden, weil warscheinlich noch Text kommt.
+ // Spaetestens bei einem Paint / CalcTextWidth wird formatiert.
+
+ bUpdate = bUp;
+ if ( bUpdate && ( bChanged || bForceUpdate ) )
+ FormatAndUpdate( pCurView );
+}
+
+void ImpEditEngine::ShowParagraph( sal_uInt16 nParagraph, sal_Bool bShow )
+{
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "ShowParagraph: Absatz existiert nicht!" );
+ if ( pPPortion && ( pPPortion->IsVisible() != bShow ) )
+ {
+ pPPortion->SetVisible( bShow );
+
+ if ( !bShow )
+ {
+ // Als deleted kenzeichnen, damit keine Selektion auf diesem
+ // Absatz beginnt oder endet...
+ DeletedNodeInfo* pDelInfo = new DeletedNodeInfo( (sal_uIntPtr)pPPortion->GetNode(), nParagraph );
+ aDeletedNodes.Insert( pDelInfo, aDeletedNodes.Count() );
+ UpdateSelections();
+ // Dann kriege ich den unteren Bereich nicht invalidiert,
+ // wenn UpdateMode = sal_False!
+ // Wenn doch, dann vor SetVisible auf sal_False merken!
+// nCurTextHeight -= pPPortion->GetHeight();
+ }
+
+ if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
+ {
+ if ( !GetTextRanger() )
+ {
+ if ( pPPortion->IsInvalid() )
+ {
+ Font aOldFont( GetRefDevice()->GetFont() );
+ CreateLines( nParagraph, 0 ); // 0: Kein TextRanger
+ if ( aStatus.DoRestoreFont() )
+ GetRefDevice()->SetFont( aOldFont );
+ }
+ else
+ {
+ CalcHeight( pPPortion );
+ }
+ nCurTextHeight += pPPortion->GetHeight();
+ }
+ else
+ {
+ nCurTextHeight = 0x7fffffff;
+ }
+ }
+
+ pPPortion->SetMustRepaint( sal_True );
+ if ( GetUpdateMode() && !IsInUndo() && !GetTextRanger() )
+ {
+ aInvalidRec = Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
+ Point( GetPaperSize().Width(), nCurTextHeight ) );
+ UpdateViews( GetActiveView() );
+ }
+ }
+}
+
+sal_Bool ImpEditEngine::IsParagraphVisible( sal_uInt16 nParagraph )
+{
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "IsParagraphVisible: Absatz existiert nicht!" );
+ if ( pPPortion )
+ return pPPortion->IsVisible();
+ return sal_False;
+}
+
+EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_uInt16 nNewPos, EditView* pCurView )
+{
+ DBG_ASSERT( GetParaPortions().Count() != 0, "Keine Absaetze gefunden: MoveParagraphs" );
+ if ( GetParaPortions().Count() == 0 )
+ return EditSelection();
+ aOldPositions.Justify();
+
+ EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
+
+ if ( nNewPos >= GetParaPortions().Count() )
+ nNewPos = GetParaPortions().Count() - 1;
+
+ // Dort, wo der Absatz eingefuegt wurde, muss richtig gepainted werden:
+ // Dort, wo der Absatz entfernt wurde, muss richtig gepainted werden:
+ // ( Und dazwischen entsprechend auch...)
+ if ( pCurView && ( GetUpdateMode() == sal_True ) )
+ {
+ // in diesem Fall kann ich direkt neu malen, ohne die
+ // Portions zu Invalidieren.
+ sal_uInt16 nFirstPortion = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
+ sal_uInt16 nLastPortion = Max( (sal_uInt16)aOldPositions.Max(), nNewPos );
+
+ ParaPortion* pUpperPortion = GetParaPortions().SaveGetObject( nFirstPortion );
+ ParaPortion* pLowerPortion = GetParaPortions().SaveGetObject( nLastPortion );
+
+ aInvalidRec = Rectangle(); // leermachen
+ aInvalidRec.Left() = 0;
+ aInvalidRec.Right() = aPaperSize.Width();
+ aInvalidRec.Top() = GetParaPortions().GetYOffset( pUpperPortion );
+ aInvalidRec.Bottom() = GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight();
+
+ UpdateViews( pCurView );
+ }
+ else
+ {
+ // aber der oberen ungueltigen Position neu painten...
+ sal_uInt16 nFirstInvPara = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
+ InvalidateFromParagraph( nFirstInvPara );
+ }
+ return aSel;
+}
+
+void ImpEditEngine::InvalidateFromParagraph( sal_uInt16 nFirstInvPara )
+{
+ // Es werden nicht die folgenden Absaetze invalidiert,
+ // da ResetHeight() => Groessenanderung => alles folgende wird
+ // sowieso neu ausgegeben.
+ ParaPortion* pTmpPortion;
+ if ( nFirstInvPara != 0 )
+ {
+ pTmpPortion = GetParaPortions().GetObject( nFirstInvPara-1 );
+ pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
+ }
+ else
+ {
+ pTmpPortion = GetParaPortions().GetObject( 0 );
+ pTmpPortion->MarkSelectionInvalid( 0, pTmpPortion->GetNode()->Len() );
+ }
+ pTmpPortion->ResetHeight();
+}
+
+IMPL_LINK_INLINE_START( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
+{
+ CallStatusHdl();
+ return 0;
+}
+IMPL_LINK_INLINE_END( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
+
+void ImpEditEngine::CallStatusHdl()
+{
+ if ( aStatusHdlLink.IsSet() && aStatus.GetStatusWord() )
+ {
+ // Der Status muss vor Call zurueckgesetzt werden,
+ // da im Hdl evtl. weitere Fags gesetzt werden...
+ EditStatus aTmpStatus( aStatus );
+ aStatus.Clear();
+ aStatusHdlLink.Call( &aTmpStatus );
+ aStatusTimer.Stop(); // Falls von Hand gerufen...
+ }
+}
+
+ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode* pCurNode )
+{
+ ParaPortion* pPortion = FindParaPortion( pCurNode );
+ DBG_ASSERT( pPortion, "GetPrevVisibleNode: Keine passende Portion!" );
+ pPortion = GetPrevVisPortion( pPortion );
+ if ( pPortion )
+ return pPortion->GetNode();
+ return 0;
+}
+
+ContentNode* ImpEditEngine::GetNextVisNode( ContentNode* pCurNode )
+{
+ ParaPortion* pPortion = FindParaPortion( pCurNode );
+ DBG_ASSERT( pPortion, "GetNextVisibleNode: Keine passende Portion!" );
+ pPortion = GetNextVisPortion( pPortion );
+ if ( pPortion )
+ return pPortion->GetNode();
+ return 0;
+}
+
+ParaPortion* ImpEditEngine::GetPrevVisPortion( ParaPortion* pCurPortion )
+{
+ sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
+ DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisPortion" );
+ ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : 0;
+ while ( pPortion && !pPortion->IsVisible() )
+ pPortion = nPara ? GetParaPortions()[--nPara] : 0;
+
+ return pPortion;
+}
+
+ParaPortion* ImpEditEngine::GetNextVisPortion( ParaPortion* pCurPortion )
+{
+ sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
+ DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisNode" );
+ ParaPortion* pPortion = GetParaPortions().SaveGetObject( ++nPara );
+ while ( pPortion && !pPortion->IsVisible() )
+ pPortion = GetParaPortions().SaveGetObject( ++nPara );
+
+ return pPortion;
+}
+
+EditPaM ImpEditEngine::InsertParagraph( sal_uInt16 nPara )
+{
+ EditPaM aPaM;
+ if ( nPara != 0 )
+ {
+ ContentNode* pNode = GetEditDoc().SaveGetObject( nPara-1 );
+ if ( !pNode )
+ pNode = GetEditDoc().SaveGetObject( GetEditDoc().Count() - 1 );
+ DBG_ASSERT( pNode, "Kein einziger Absatz in InsertParagraph ?" );
+ aPaM = EditPaM( pNode, pNode->Len() );
+ }
+ else
+ {
+ ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
+ aPaM = EditPaM( pNode, 0 );
+ }
+
+ return ImpInsertParaBreak( aPaM );
+}
+
+EditSelection* ImpEditEngine::SelectParagraph( sal_uInt16 nPara )
+{
+ EditSelection* pSel = 0;
+ ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
+ DBG_ASSERTWARNING( pNode, "Absatz existiert nicht: SelectParagraph" );
+ if ( pNode )
+ pSel = new EditSelection( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
+
+ return pSel;
+}
+
+void ImpEditEngine::FormatAndUpdate( EditView* pCurView )
+{
+ if ( bDowning )
+ return ;
+
+ if ( IsInUndo() )
+ IdleFormatAndUpdate( pCurView );
+ else
+ {
+ FormatDoc();
+ UpdateViews( pCurView );
+ }
+}
+
+void ImpEditEngine::SetFlatMode( sal_Bool bFlat )
+{
+ if ( bFlat != aStatus.UseCharAttribs() )
+ return;
+
+ if ( !bFlat )
+ aStatus.TurnOnFlags( EE_CNTRL_USECHARATTRIBS );
+ else
+ aStatus.TurnOffFlags( EE_CNTRL_USECHARATTRIBS );
+
+ aEditDoc.CreateDefFont( !bFlat );
+
+ FormatFullDoc();
+ UpdateViews( (EditView*) 0);
+ if ( pActiveView )
+ pActiveView->ShowCursor();
+}
+
+void ImpEditEngine::SetCharStretching( sal_uInt16 nX, sal_uInt16 nY )
+{
+ if ( !IsVertical() )
+ {
+ nStretchX = nX;
+ nStretchY = nY;
+ }
+ else
+ {
+ nStretchX = nY;
+ nStretchY = nX;
+ }
+
+ if ( aStatus.DoStretch() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+}
+
+void ImpEditEngine::DoStretchChars( sal_uInt16 nX, sal_uInt16 nY )
+{
+ UndoActionStart( EDITUNDO_STRETCH );
+ sal_uInt16 nParas = GetEditDoc().Count();
+ for ( sal_uInt16 nPara = 0; nPara < nParas; nPara++ )
+ {
+ ContentNode* pNode = GetEditDoc()[nPara];
+ SfxItemSet aTmpSet( pNode->GetContentAttribs().GetItems() );
+
+ if ( nX != 100 )
+ {
+ // Fontbreite
+ SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*) pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH ).Clone();
+ sal_uInt32 nProp = pNewWidth->GetValue(); // sal_uInt32, kann temporaer gross werden
+ nProp *= nX;
+ nProp /= 100;
+ pNewWidth->SetValue( (sal_uInt16)nProp );
+ aTmpSet.Put( *pNewWidth );
+ delete pNewWidth;
+
+ // Kerning:
+ const SvxKerningItem& rKerningItem =
+ (const SvxKerningItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_KERNING );
+ SvxKerningItem* pNewKerning = (SvxKerningItem*)rKerningItem.Clone();
+ long nKerning = pNewKerning->GetValue();
+ if ( nKerning > 0 )
+ {
+ nKerning *= nX;
+ nKerning /= 100;
+ }
+ else if ( nKerning < 0 )
+ {
+ // Bei Negativen Werten:
+ // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
+ nKerning *= 100;
+ nKerning /= nX;
+ }
+ pNewKerning->SetValue( (short)nKerning );
+ aTmpSet.Put( *pNewKerning);
+ delete pNewKerning;
+ }
+ else
+ aTmpSet.ClearItem( EE_CHAR_FONTWIDTH );
+
+ if ( nY != 100 )
+ {
+ // Fonthoehe
+ for ( int nItem = 0; nItem < 3; nItem++ )
+ {
+ USHORT nItemId = EE_CHAR_FONTHEIGHT;
+ if ( nItem == 1 )
+ nItemId = EE_CHAR_FONTHEIGHT_CJK;
+ else if ( nItem == 2 )
+ nItemId = EE_CHAR_FONTHEIGHT_CTL;
+
+ const SvxFontHeightItem& rHeightItem =
+ (const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( nItemId );
+ SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)rHeightItem.Clone();
+ sal_uInt32 nHeight = pNewHeight->GetHeight();
+ nHeight *= nY;
+ nHeight /= 100;
+ pNewHeight->SetHeightValue( nHeight );
+ aTmpSet.Put( *pNewHeight );
+ delete pNewHeight;
+ }
+
+ // Absatzabstaende
+ const SvxULSpaceItem& rULSpaceItem =
+ (const SvxULSpaceItem&)pNode->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ SvxULSpaceItem* pNewUL = (SvxULSpaceItem*)rULSpaceItem.Clone();
+ sal_uInt32 nUpper = pNewUL->GetUpper();
+ nUpper *= nY;
+ nUpper /= 100;
+ pNewUL->SetUpper( (sal_uInt16)nUpper );
+ sal_uInt32 nLower = pNewUL->GetLower();
+ nLower *= nY;
+ nLower /= 100;
+ pNewUL->SetLower( (sal_uInt16)nLower );
+ aTmpSet.Put( *pNewUL );
+ delete pNewUL;
+ }
+ else
+ aTmpSet.ClearItem( EE_CHAR_FONTHEIGHT );
+
+ SetParaAttribs( nPara, aTmpSet );
+
+ // harte Attribute:
+ sal_uInt16 nLastEnd = 0; // damit nach entfernen und neu nicht nochmal
+ CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ sal_uInt16 nAttribs = rAttribs.Count();
+ for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
+ {
+ EditCharAttrib* pAttr = rAttribs[nAttr];
+ if ( pAttr->GetStart() >= nLastEnd )
+ {
+ sal_uInt16 nWhich = pAttr->Which();
+ SfxPoolItem* pNew = 0;
+ if ( nWhich == EE_CHAR_FONTHEIGHT )
+ {
+ SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)pAttr->GetItem()->Clone();
+ sal_uInt32 nHeight = pNewHeight->GetHeight();
+ nHeight *= nY;
+ nHeight /= 100;
+ pNewHeight->SetHeightValue( nHeight );
+ pNew = pNewHeight;
+ }
+ else if ( nWhich == EE_CHAR_FONTWIDTH )
+ {
+ SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*)pAttr->GetItem()->Clone();
+ sal_uInt32 nProp = pNewWidth->GetValue();
+ nProp *= nX;
+ nProp /= 100;
+ pNewWidth->SetValue( (sal_uInt16)nProp );
+ pNew = pNewWidth;
+ }
+ else if ( nWhich == EE_CHAR_KERNING )
+ {
+ SvxKerningItem* pNewKerning = (SvxKerningItem*)pAttr->GetItem()->Clone();
+ long nKerning = pNewKerning->GetValue();
+ if ( nKerning > 0 )
+ {
+ nKerning *= nX;
+ nKerning /= 100;
+ }
+ else if ( nKerning < 0 )
+ {
+ // Bei Negativen Werten:
+ // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
+ nKerning *= 100;
+ nKerning /= nX;
+ }
+ pNewKerning->SetValue( (short)nKerning );
+ pNew = pNewKerning;
+ }
+ if ( pNew )
+ {
+ SfxItemSet _aTmpSet( GetEmptyItemSet() );
+ _aTmpSet.Put( *pNew );
+ SetAttribs( EditSelection( EditPaM( pNode, pAttr->GetStart() ),
+ EditPaM( pNode, pAttr->GetEnd() ) ), _aTmpSet );
+
+ nLastEnd = pAttr->GetEnd();
+ delete pNew;
+ }
+ }
+ }
+ }
+ UndoActionEnd( EDITUNDO_STRETCH );
+}
+
+const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
+{
+ const SvxNumberFormat *pRes = 0;
+
+ if (pNode)
+ {
+ // get index of paragraph
+ USHORT nPara = GetEditDoc().GetPos( const_cast< ContentNode * >(pNode) );
+ DBG_ASSERT( nPara < USHRT_MAX, "node not found in array" );
+ if (nPara < USHRT_MAX)
+ {
+ // the called function may be overloaded by an OutlinerEditEng object to provide
+ // access to the SvxNumberFormat of the Outliner.
+ // The EditEngine implementation will just return 0.
+ pRes = pEditEngine->GetNumberFormat( nPara );
+ }
+ }
+
+ return pRes;
+}
+
+sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
+ const ContentNode *pNode,
+ sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
+{
+ // nSpaceBefore matches the ODF attribut text:space-before
+ // nMinLabelWidth matches the ODF attribut text:min-label-width
+
+ const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
+
+ // if no number format was found we have no Outliner or the numbering level
+ // within the Outliner is -1 which means no number format should be applied.
+ // Thus the default values to be returned are 0.
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nMinLabelWidth = 0;
+
+ if (pNumFmt)
+ {
+ nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
+ nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
+ DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
+ }
+ if (pnSpaceBefore)
+ *pnSpaceBefore = nSpaceBefore;
+ if (pnMinLabelWidth)
+ *pnMinLabelWidth = nMinLabelWidth;
+
+ return nSpaceBefore + nMinLabelWidth;
+}
+
+const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
+{
+ return (const SvxLRSpaceItem&)pNode->GetContentAttribs().GetItem( aStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
+}
+
+// Either sets the digit mode at the output device or
+// modifies the passed string according to the text numeral setting:
+void ImpEditEngine::ImplInitDigitMode( OutputDevice* pOutDev, String* pString, xub_StrLen nStt, xub_StrLen nLen, LanguageType eCurLang )
+{
+ // #114278# Also setting up digit language from Svt options
+ // (cannot reliably inherit the outdev's setting)
+ if( !pCTLOptions )
+ pCTLOptions = new SvtCTLOptions;
+
+ LanguageType eLang = eCurLang;
+ const SvtCTLOptions::TextNumerals nCTLTextNumerals = pCTLOptions->GetCTLTextNumerals();
+
+ if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
+ eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
+ eLang = LANGUAGE_ENGLISH;
+ else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
+ eLang = (LanguageType) Application::GetSettings().GetLanguage();
+
+ if(pOutDev)
+ {
+ pOutDev->SetDigitLanguage( eLang );
+ }
+ else if (pString)
+ {
+ // see sallayout.cxx in vcl
+ int nOffset;
+ switch( eLang & LANGUAGE_MASK_PRIMARY )
+ {
+ default:
+ nOffset = 0;
+ break;
+ case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0660 - '0'; // arabic-indic digits
+ break;
+ case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
+ case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
+ break;
+ }
+ if (nOffset)
+ {
+ const xub_StrLen nEnd = nStt + nLen;
+ for( xub_StrLen nIdx = nStt; nIdx < nEnd; ++nIdx )
+ {
+ sal_Unicode nChar = pString->GetChar( nIdx );
+ if( (nChar < '0') || ('9' < nChar) )
+ continue;
+ nChar = (sal_Unicode)(nChar + nOffset);
+ pString->SetChar( nIdx, nChar );
+ }
+ }
+ }
+}
+
+void ImpEditEngine::ImplInitLayoutMode( OutputDevice* pOutDev, USHORT nPara, USHORT nIndex )
+{
+ BOOL bCTL = FALSE;
+ BYTE bR2L = FALSE;
+ if ( nIndex == 0xFFFF )
+ {
+ bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
+ bR2L = IsRightToLeft( nPara );
+ }
+ else
+ {
+ ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
+ short nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
+ bCTL = nScriptType == i18n::ScriptType::COMPLEX;
+ bR2L = GetRightToLeft( nPara, nIndex + 1); // this change was discussed in issue 37190
+ // it also works for issue 55927
+ }
+
+ ULONG nLayoutMode = pOutDev->GetLayoutMode();
+
+ // We always use the left postion for DrawText()
+ nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL);
+
+ if ( !bCTL && !bR2L)
+ {
+ // No CTL/Bidi checking neccessary
+ nLayoutMode |= ( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
+ }
+ else
+ {
+ // CTL/Bidi checking neccessary
+ // Don't use BIDI_STRONG, VCL must do some checks.
+ nLayoutMode &= ~( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
+
+ if ( bR2L )
+ nLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
+ }
+
+ pOutDev->SetLayoutMode( nLayoutMode );
+
+ // #114278# Also setting up digit language from Svt options
+ // (cannot reliably inherit the outdev's setting)
+ LanguageType eLang;
+
+ if( !pCTLOptions )
+ pCTLOptions = new SvtCTLOptions;
+
+ if ( SvtCTLOptions::NUMERALS_HINDI == pCTLOptions->GetCTLTextNumerals() )
+ eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == pCTLOptions->GetCTLTextNumerals() )
+ eLang = LANGUAGE_ENGLISH;
+ else
+ eLang = (LanguageType) Application::GetSettings().GetLanguage();
+
+ pOutDev->SetDigitLanguage( eLang );
+}
+
+Reference < i18n::XBreakIterator > ImpEditEngine::ImplGetBreakIterator() const
+{
+ if ( !xBI.is() )
+ {
+ Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
+ xBI.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
+ }
+ return xBI;
+}
+
+Reference < i18n::XExtendedInputSequenceChecker > ImpEditEngine::ImplGetInputSequenceChecker() const
+{
+ if ( !xISC.is() )
+ {
+ Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
+ Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
+ if ( xI.is() )
+ {
+ Any x = xI->queryInterface( ::getCppuType((const Reference< i18n::XExtendedInputSequenceChecker >*)0) );
+ x >>= xISC;
+ }
+ }
+ return xISC;
+}
+
+Color ImpEditEngine::GetAutoColor() const
+{
+ Color aColor = const_cast<ImpEditEngine*>(this)->GetColorConfig().GetColorValue( svtools::FONTCOLOR ).nColor;
+
+ if ( GetBackgroundColor() != COL_AUTO )
+ {
+ if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
+ aColor = COL_WHITE;
+ else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
+ aColor = COL_BLACK;
+ }
+
+ return aColor;
+}
+
+
+BOOL ImpEditEngine::ImplCalcAsianCompression( ContentNode* pNode, TextPortion* pTextPortion, USHORT nStartPos, sal_Int32* pDXArray, USHORT n100thPercentFromMax, BOOL bManipulateDXArray )
+{
+ DBG_ASSERT( GetAsianCompressionMode(), "ImplCalcAsianCompression - Why?" );
+ DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
+
+ // Percent is 1/100 Percent...
+
+ if ( n100thPercentFromMax == 10000 )
+ pTextPortion->SetExtraInfos( NULL );
+
+ BOOL bCompressed = FALSE;
+
+ if ( GetScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
+ {
+ long nNewPortionWidth = pTextPortion->GetSize().Width();
+ USHORT nPortionLen = pTextPortion->GetLen();
+ for ( USHORT n = 0; n < nPortionLen; n++ )
+ {
+ BYTE nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
+
+ BOOL bCompressPunctuation = ( nType == CHAR_PUNCTUATIONLEFT ) || ( nType == CHAR_PUNCTUATIONRIGHT );
+ BOOL bCompressKana = ( nType == CHAR_KANA ) && ( GetAsianCompressionMode() == text::CharacterCompressionType::PUNCTUATION_AND_KANA );
+
+ // create Extra infos only if needed...
+ if ( bCompressPunctuation || bCompressKana )
+ {
+ if ( !pTextPortion->GetExtraInfos() )
+ {
+ ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
+ pTextPortion->SetExtraInfos( pExtraInfos );
+ pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
+ pExtraInfos->nAsianCompressionTypes = CHAR_NORMAL;
+ }
+ pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
+ pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
+// pTextPortion->GetExtraInfos()->nCompressedChars++;
+
+ long nOldCharWidth;
+ if ( (n+1) < nPortionLen )
+ {
+ nOldCharWidth = pDXArray[n];
+ }
+ else
+ {
+ if ( bManipulateDXArray )
+ nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
+ else
+ nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
+ }
+ nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
+
+ long nCompress = 0;
+
+ if ( bCompressPunctuation )
+ {
+ // pTextPortion->GetExtraInfos()->nComressionWeight += 5;
+ nCompress = nOldCharWidth / 2;
+ }
+ else // Kana
+ {
+ // pTextPortion->GetExtraInfos()->nComressionWeight += 1;
+ nCompress = nOldCharWidth / 10;
+ }
+
+ if ( n100thPercentFromMax != 10000 )
+ {
+ nCompress *= n100thPercentFromMax;
+ nCompress /= 10000;
+ }
+
+ if ( nCompress )
+ {
+ bCompressed = TRUE;
+ nNewPortionWidth -= nCompress;
+ pTextPortion->GetExtraInfos()->bCompressed = TRUE;
+
+
+ // Special handling for rightpunctuation: For the 'compression' we must
+ // start th eoutput before the normal char position....
+ if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
+ {
+ if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
+ pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
+
+ if ( nType == CHAR_PUNCTUATIONRIGHT )
+ {
+ // If it's the first char, I must handle it in Paint()...
+ if ( n )
+ {
+ // -1: No entry for the last character
+ for ( USHORT i = n-1; i < (nPortionLen-1); i++ )
+ pDXArray[i] -= nCompress;
+ }
+ else
+ {
+ pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = TRUE;
+ pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
+ }
+ }
+ else
+ {
+ // -1: No entry for the last character
+ for ( USHORT i = n; i < (nPortionLen-1); i++ )
+ pDXArray[i] -= nCompress;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
+ pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
+
+ pTextPortion->GetSize().Width() = nNewPortionWidth;
+
+ if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
+ {
+ // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
+ long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
+ nShrink *= n100thPercentFromMax;
+ nShrink /= 10000;
+ long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
+ if ( nNewWidth < pTextPortion->GetSize().Width() )
+ pTextPortion->GetSize().Width() = nNewWidth;
+ }
+ }
+ return bCompressed;
+}
+
+
+void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, long nRemainingWidth )
+{
+ BOOL bFoundCompressedPortion = FALSE;
+ long nCompressed = 0;
+// long nCompressWeight = 0;
+ TextPortionList aCompressedPortions;
+
+ USHORT nPortion = pLine->GetEndPortion();
+ TextPortion* pTP = pParaPortion->GetTextPortions()[ nPortion ];
+ while ( pTP && ( pTP->GetKind() == PORTIONKIND_TEXT ) )
+ {
+ if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
+ {
+ bFoundCompressedPortion = TRUE;
+ nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
+ aCompressedPortions.Insert( pTP, aCompressedPortions.Count() );
+ }
+ pTP = ( nPortion > pLine->GetStartPortion() ) ? pParaPortion->GetTextPortions()[ --nPortion ] : NULL;
+ }
+
+ if ( bFoundCompressedPortion )
+ {
+ long nCompressPercent = 0;
+ if ( nCompressed > nRemainingWidth )
+ {
+ nCompressPercent = nCompressed - nRemainingWidth;
+ DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
+ nCompressPercent *= 10000;
+ nCompressPercent /= nCompressed;
+ }
+
+ for ( USHORT n = 0; n < aCompressedPortions.Count(); n++ )
+ {
+ pTP = aCompressedPortions[n];
+ pTP->GetExtraInfos()->bCompressed = FALSE;
+ pTP->GetSize().Width() = pTP->GetExtraInfos()->nOrgWidth;
+ if ( nCompressPercent )
+ {
+ USHORT nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
+ USHORT nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
+ DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
+ sal_Int32* pDXArray = const_cast< sal_Int32* >( pLine->GetCharPosArray().GetData()+( nTxtPortionStart-pLine->GetStart() ) );
+ if ( pTP->GetExtraInfos()->pOrgDXArray )
+ memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray, (pTP->GetLen()-1)*sizeof(sal_Int32) );
+ ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, (USHORT)nCompressPercent, TRUE );
+ }
+ }
+ }
+
+ aCompressedPortions.Remove( 0, aCompressedPortions.Count() );
+}
+
+// redesigned to work with TextMarkingVector
+void ImpEditEngine::ImplFillTextMarkingVector(const lang::Locale& rLocale, EEngineData::TextMarkingVector& rTextMarkingVector, const String& rTxt, const USHORT nIdx, const USHORT nLen) const
+{
+ // determine relevant logical text elements for the just-rendered
+ // string of characters.
+ Reference< i18n::XBreakIterator > _xBI(ImplGetBreakIterator());
+
+ if(_xBI.is())
+ {
+ sal_Int32 nDone;
+ sal_Int32 nNextCellBreak(_xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
+ i18n::Boundary nNextWordBoundary(_xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True));
+ sal_Int32 nNextSentenceBreak(_xBI->endOfSentence(rTxt, nIdx, rLocale));
+
+ const sal_Int32 nEndPos(nIdx + nLen);
+ sal_Int32 i;
+
+ for(i = nIdx; i < nEndPos; i++)
+ {
+ // create the entries for the respective break positions
+ if(i == nNextCellBreak)
+ {
+ rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfCaracter, i - nIdx));
+ nNextCellBreak = _xBI->nextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ }
+ if(i == nNextWordBoundary.endPos)
+ {
+ rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfWord, i - nIdx));
+ nNextWordBoundary = _xBI->getWordBoundary(rTxt, i + 1, rLocale, i18n::WordType::ANY_WORD, sal_True);
+ }
+ if(i == nNextSentenceBreak)
+ {
+ rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfSentence, i - nIdx));
+ nNextSentenceBreak = _xBI->endOfSentence(rTxt, i + 1, rLocale);
+ }
+ }
+ }
+}
+
+// eof