diff options
Diffstat (limited to 'sw/source/core/text/itrform2.cxx')
-rw-r--r-- | sw/source/core/text/itrform2.cxx | 2127 |
1 files changed, 2127 insertions, 0 deletions
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx new file mode 100644 index 000000000000..c7527b372554 --- /dev/null +++ b/sw/source/core/text/itrform2.cxx @@ -0,0 +1,2127 @@ +/************************************************************************* + * + * 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_sw.hxx" + +#include "hintids.hxx" + +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <editeng/lspcitem.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <ftninfo.hxx> +#include <charfmt.hxx> +#include <editeng/charrotateitem.hxx> +#include <layfrm.hxx> // GetFrmRstHeight, etc +#include <viewsh.hxx> +#include <viewopt.hxx> // SwViewOptions +#include <paratr.hxx> // SwFmtDrop +#include <txtcfg.hxx> +#include <itrform2.hxx> +#include <porrst.hxx> +#include <portab.hxx> // pLastTab-> +#include <porfly.hxx> // CalcFlyWidth +#include <portox.hxx> // WhichTxtPortion +#include <porref.hxx> // WhichTxtPortion +#include <porfld.hxx> // SwNumberPortion fuer CalcAscent() +#include <porftn.hxx> // SwFtnPortion +#include <porhyph.hxx> +#include <guess.hxx> +#include <blink.hxx> // pBlink +#include <ftnfrm.hxx> // WhichFirstPortion() -> mal Verlagern. +#include <redlnitr.hxx> // SwRedlineItr +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <doc.hxx> // SwDoc +#include <pormulti.hxx> // SwMultiPortion +#define _SVSTDARR_LONGS +#include <svl/svstdarr.hxx> +#include <unotools/charclass.hxx> + +#if OSL_DEBUG_LEVEL > 1 +#include <ndtxt.hxx> // pSwpHints, Ausgabeoperator +#endif + +using namespace ::com::sun::star; + +extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); +bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ); + +#define MAX_TXTPORLEN 300 + +inline void ClearFly( SwTxtFormatInfo &rInf ) +{ + if( rInf.GetFly() ) + { + delete rInf.GetFly(); + rInf.SetFly(0); + } +} + +/************************************************************************* + * SwTxtFormatter::CtorInitTxtFormatter() + *************************************************************************/ + +void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf ) +{ + CtorInitTxtPainter( pNewFrm, pNewInf ); + pInf = pNewInf; + pDropFmt = GetInfo().GetDropFmt(); + pMulti = NULL; + + bOnceMore = sal_False; + bFlyInCntBase = sal_False; + bChanges = sal_False; + bTruncLines = sal_False; + nCntEndHyph = 0; + nCntMidHyph = 0; + nLeftScanIdx = STRING_LEN; + nRightScanIdx = 0; + m_nHintEndIndex = 0; + + if( nStart > GetInfo().GetTxt().Len() ) + { + ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" ); + nStart = GetInfo().GetTxt().Len(); + } + +} + +/************************************************************************* + * SwTxtFormatter::DTOR + *************************************************************************/ + +SwTxtFormatter::~SwTxtFormatter() +{ + // Auesserst unwahrscheinlich aber denkbar. + // z.B.: Feld spaltet sich auf, Widows schlagen zu + if( GetInfo().GetRest() ) + { + delete GetInfo().GetRest(); + GetInfo().SetRest(0); + } +} + +/************************************************************************* + * SwTxtFormatter::Insert() + *************************************************************************/ + +void SwTxtFormatter::Insert( SwLineLayout *pLay ) +{ + // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element. + if ( pCurr ) + { + pLay->SetNext( pCurr->GetNext() ); + pCurr->SetNext( pLay ); + } + else + pCurr = pLay; +} + +/************************************************************************* + * SwTxtFormatter::GetFrmRstHeight() + *************************************************************************/ + +KSHORT SwTxtFormatter::GetFrmRstHeight() const +{ + // 8725: Uns interessiert die Resthoehe bezogen auf die Seite. + // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht + // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn + // gerufen. + // Falsch: const SwFrm *pUpper = pFrm->GetUpper(); + const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm(); + const SwTwips nHeight = pPage->Frm().Top() + + pPage->Prt().Top() + + pPage->Prt().Height() - Y(); + if( 0 > nHeight ) + return pCurr->Height(); + else + return KSHORT( nHeight ); +} + +/************************************************************************* + * SwTxtFormatter::UnderFlow() + *************************************************************************/ + +SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf ) +{ + // Werte sichern und rInf initialisieren. + SwLinePortion *pUnderFlow = rInf.GetUnderFlow(); + if( !pUnderFlow ) + return 0; + + // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der + // naechsten Zeile durchaus noch einmal drankommen koennen. + // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt. + + const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos(); + const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos(); + + // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF + // 3983: Nicht ClearFly(rInf) ! + SwFlyPortion *pFly = rInf.GetFly(); + rInf.SetFly( 0 ); + + FeedInf( rInf ); + rInf.SetLast( pCurr ); + // pUnderFlow braucht nicht deletet werden, weil es im folgenden + // Truncate() untergehen wird. + rInf.SetUnderFlow(0); + rInf.SetSoftHyphPos( nSoftHyphPos ); + rInf.SetUnderScorePos( nUnderScorePos ); + rInf.SetPaintOfst( GetLeftMargin() ); + + // Wir suchen die Portion mit der Unterlaufposition + SwLinePortion *pPor = pCurr->GetFirstPortion(); + if( pPor != pUnderFlow ) + { + // pPrev wird die letzte Portion vor pUnderFlow, + // die noch eine echte Breite hat. + // Ausnahme: SoftHyphPortions duerfen dabei natuerlich + // nicht vergessen werden, obwohl sie keine Breite haben. + SwLinePortion *pTmpPrev = pPor; + while( pPor && pPor != pUnderFlow ) + { + DBG_LOOP; + if( !pPor->IsKernPortion() && + ( pPor->Width() || pPor->IsSoftHyphPortion() ) ) + { + while( pTmpPrev != pPor ) + { + pTmpPrev->Move( rInf ); + rInf.SetLast( pTmpPrev ); + pTmpPrev = pTmpPrev->GetPortion(); + ASSERT( pTmpPrev, "UnderFlow: Loosing control!" ); + }; + } + pPor = pPor->GetPortion(); + } + pPor = pTmpPrev; + if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen + ( pPor->IsFlyPortion() || pPor->IsDropPortion() || + pPor->IsFlyCntPortion() ) ) + { + pPor->Move( rInf ); + rInf.SetLast( pPor ); + rInf.SetStopUnderFlow( sal_True ); + pPor = pUnderFlow; + } + } + + // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ? + ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" ); + + // OD 2004-05-26 #i29529# - correction: no delete of footnotes +// if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() ) +// { +// SwLinePortion *pTmp = pPor->GetPortion(); +// while( pTmp ) +// { +// if( pTmp->IsFtnPortion() ) +// ((SwFtnPortion*)pTmp)->ClearFtn(); +// pTmp = pTmp->GetPortion(); +// } +// } + + /*-----------------14.12.94 09:45------------------- + * 9849: Schnellschuss + * --------------------------------------------------*/ + if ( pPor==rInf.GetLast() ) + { + // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich + // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber + // mehrere Zeilen geht und in der zweiten Zeile in einen Fly + // hineinlaeuft! + rInf.SetFly( pFly ); // wg. 28300 + pPor->Truncate(); + return pPor; // Reicht das? + } + /*--------------------------------------------------- + * Ende des Schnellschusses wg. 9849 + * --------------------------------------------------*/ + + // 4656: X + Width == 0 bei SoftHyph > Zeile ?! + if( !pPor || !(rInf.X() + pPor->Width()) ) + { + delete pFly; + return 0; + } + + // Vorbereitungen auf's Format() + // Wir muessen die Kette hinter pLast abknipsen, weil + // nach dem Format() ein Insert erfolgt. + SeekAndChg( rInf ); + + // line width is adjusted, so that pPor does not fit to current + // line anymore + rInf.Width( (USHORT)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) ); + rInf.SetLen( pPor->GetLen() ); + rInf.SetFull( sal_False ); + if( pFly ) + { + // Aus folgendem Grund muss die FlyPortion neu berechnet werden: + // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie + // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht, + // so hat die FlyPortion eine falsche Groesse/Fixsize. + rInf.SetFly( pFly ); + CalcFlyWidth( rInf ); + } + rInf.GetLast()->SetPortion(0); + + // Eine Ausnahme bildet das SwLineLayout, dass sich beim + // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg: + if( rInf.GetLast() == pCurr ) + { + if( pPor->InTxtGrp() && !pPor->InExpGrp() ) + { + MSHORT nOldWhich = pCurr->GetWhichPor(); + *(SwLinePortion*)pCurr = *pPor; + pCurr->SetPortion( pPor->GetPortion() ); + pCurr->SetWhichPor( nOldWhich ); + pPor->SetPortion( 0 ); + delete pPor; + pPor = pCurr; + } + } + pPor->Truncate(); + SwLinePortion *const pRest( rInf.GetRest() ); + if (pRest && pRest->InFldGrp() && + static_cast<SwFldPortion*>(pRest)->IsNoLength()) + { + // HACK: decrement again, so we pick up the suffix in next line! + --m_nHintEndIndex; + } + delete pRest; + rInf.SetRest(0); + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::InsertPortion() + *************************************************************************/ + +void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf, + SwLinePortion *pPor ) const +{ + // Die neue Portion wird eingefuegt, + // bei dem LineLayout ist allerdings alles anders... + if( pPor == pCurr ) + { + if( pCurr->GetPortion() ) + pPor = pCurr->GetPortion(); + } + else + { + SwLinePortion *pLast = rInf.GetLast(); + if( pLast->GetPortion() ) + { + while( pLast->GetPortion() ) + pLast = pLast->GetPortion(); + rInf.SetLast( pLast ); + } + pLast->Insert( pPor ); + + rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() ); + + // Maxima anpassen: + if( pCurr->Height() < pPor->Height() ) + pCurr->Height( pPor->Height() ); + if( pCurr->GetAscent() < pPor->GetAscent() ) + pCurr->SetAscent( pPor->GetAscent() ); + } + + // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate) + rInf.SetLast( pPor ); + while( pPor ) + { + DBG_LOOP; + pPor->Move( rInf ); + rInf.SetLast( pPor ); + pPor = pPor->GetPortion(); + } +} + +/************************************************************************* + * SwTxtFormatter::BuildPortion() + *************************************************************************/ + +void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf ) +{ + ASSERT( rInf.GetTxt().Len() < STRING_LEN, + "SwTxtFormatter::BuildPortions: bad text length in info" ); + + rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); + + // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet. + // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt + // wird. In CalcAscent geschieht dies automatisch. + rInf.SetLast( pCurr ); + rInf.ForcedLeftMargin( 0 ); + + ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" ); + + if( !pCurr->GetAscent() && !pCurr->Height() ) + CalcAscent( rInf, pCurr ); + + SeekAndChg( rInf ); + + // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. + ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" ); + CalcFlyWidth( rInf ); + SwFlyPortion *pFly = rInf.GetFly(); + if( pFly ) + { + if ( 0 < pFly->Fix() ) + ClearFly( rInf ); + else + rInf.SetFull(sal_True); + } + + SwLinePortion *pPor = NewPortion( rInf ); + + // Asian grid stuff + GETGRID( pFrm->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() && + GRID_LINES_CHARS == pGrid->GetGridType(); + + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + const USHORT nGridWidth = bHasGrid ? + GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor + + // used for grid mode only: + // the pointer is stored, because after formatting of non-asian text, + // the width of the kerning portion has to be adjusted + SwKernPortion* pGridKernPortion = 0; + + sal_Bool bFull; + SwTwips nUnderLineStart = 0; + rInf.Y( Y() ); + + while( pPor && !rInf.IsStop() ) + { + ASSERT( rInf.GetLen() < STRING_LEN && + rInf.GetIdx() <= rInf.GetTxt().Len(), + "SwTxtFormatter::BuildPortions: bad length in info" ); + DBG_LOOP; + + // We have to check the script for fields in order to set the + // correct nActual value for the font. + if( pPor->InFldGrp() ) + ((SwFldPortion*)pPor)->CheckScript( rInf ); + + if( ! bHasGrid && rInf.HasScriptSpace() && + rInf.GetLast() && rInf.GetLast()->InTxtGrp() && + rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() ) + { + BYTE nNxtActual = rInf.GetFont()->GetActual(); + BYTE nLstActual = nNxtActual; + USHORT nLstHeight = (USHORT)rInf.GetFont()->GetHeight(); + sal_Bool bAllowBefore = sal_False; + sal_Bool bAllowBehind = sal_False; + const CharClass& rCC = GetAppCharClass(); + + // are there any punctuation characters on both sides + // of the kerning portion? + if ( pPor->InFldGrp() ) + { + XubString aAltTxt; + if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) && + aAltTxt.Len() ) + { + bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 ); + + const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont(); + if ( pTmpFnt ) + nNxtActual = pTmpFnt->GetActual(); + } + } + else + bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() ); + + const SwLinePortion* pLast = rInf.GetLast(); + if ( bAllowBehind && pLast ) + { + if ( pLast->InFldGrp() ) + { + XubString aAltTxt; + if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) && + aAltTxt.Len() ) + { + bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 ); + + const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont(); + if ( pTmpFnt ) + { + nLstActual = pTmpFnt->GetActual(); + nLstHeight = (USHORT)pTmpFnt->GetHeight(); + } + } + } + else if ( rInf.GetIdx() ) + { + bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 ); + // Note: ScriptType returns values in [1,4] + if ( bAllowBefore ) + nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1; + } + + nLstHeight /= 5; + // does the kerning portion still fit into the line? + if( bAllowBefore && ( nLstActual != nNxtActual ) && + nLstHeight && rInf.X() + nLstHeight <= rInf.Width() ) + { + SwKernPortion* pKrn = + new SwKernPortion( *rInf.GetLast(), nLstHeight, + pLast->InFldGrp() && pPor->InFldGrp() ); + rInf.GetLast()->SetPortion( NULL ); + InsertPortion( rInf, pKrn ); + } + } + } + else if ( bHasGrid && ! pGridKernPortion && ! pMulti ) + { + // insert a grid kerning portion + if ( ! pGridKernPortion ) + pGridKernPortion = pPor->IsKernPortion() ? + (SwKernPortion*)pPor : + new SwKernPortion( *pCurr ); + + // if we have a new GridKernPortion, we initially calculate + // its size so that its ends on the grid + const SwPageFrm* pPageFrm = pFrm->FindPageFrm(); + const SwLayoutFrm* pBody = pPageFrm->FindBodyCont(); + SWRECTFN( pPageFrm ) + + const long nGridOrigin = pBody ? + (pBody->*fnRect->fnGetPrtLeft)() : + (pPageFrm->*fnRect->fnGetPrtLeft)(); + + SwTwips nStartX = rInf.X() + GetLeftMargin(); + if ( bVert ) + { + Point aPoint( nStartX, 0 ); + pFrm->SwitchHorizontalToVertical( aPoint ); + nStartX = aPoint.Y(); + } + + const SwTwips nOfst = nStartX - nGridOrigin; + if ( nOfst ) + { + const ULONG i = ( nOfst > 0 ) ? + ( ( nOfst - 1 ) / nGridWidth + 1 ) : + 0; + const SwTwips nKernWidth = i * nGridWidth - nOfst; + const SwTwips nRestWidth = rInf.Width() - rInf.X(); + + if ( nKernWidth <= nRestWidth ) + pGridKernPortion->Width( (USHORT)nKernWidth ); + } + + if ( pGridKernPortion != pPor ) + InsertPortion( rInf, pGridKernPortion ); + } + + // the multi-portion has it's own format function + if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) ) + bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) ); + else + bFull = pPor->Format( rInf ); + + if( rInf.IsRuby() && !rInf.GetRest() ) + bFull = sal_True; + + // if we are underlined, we store the beginning of this underlined + // segment for repaint optimization + if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart ) + nUnderLineStart = GetLeftMargin() + rInf.X(); + + if ( pPor->IsFlyPortion() ) + pCurr->SetFly( sal_True ); + // some special cases, where we have to take care for the repaint + // offset: + // 1. Underlined portions due to special underline feature + // 2. Right Tab + // 3. BidiPortions + // 4. other Multiportions + // 5. DropCaps + // 6. Grid Mode + else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) && + // 1. Underlined portions + nUnderLineStart && + // reformat is at end of an underlined portion and next portion + // is not underlined + ( ( rInf.GetReformatStart() == rInf.GetIdx() && + UNDERLINE_NONE == pFnt->GetUnderline() + ) || + // reformat is inside portion and portion is underlined + ( rInf.GetReformatStart() >= rInf.GetIdx() && + rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() && + UNDERLINE_NONE != pFnt->GetUnderline() ) ) ) + rInf.SetPaintOfst( nUnderLineStart ); + else if ( ! rInf.GetPaintOfst() && + // 2. Right Tab + ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) || + // 3. BidiPortions + ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) || + // 4. Multi Portion and 5. Drop Caps + ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) && + rInf.GetReformatStart() >= rInf.GetIdx() && + rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() ) + // 6. Grid Mode + || ( bHasGrid && SW_CJK != pFnt->GetActual() ) + ) + ) + // we store the beginning of the critical portion as our + // paint offset + rInf.SetPaintOfst( GetLeftMargin() + rInf.X() ); + + // under one of these conditions we are allowed to delete the + // start of the underline portion + if ( IsUnderlineBreak( *pPor, *pFnt ) ) + nUnderLineStart = 0; + + if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) ) + SetFlyInCntBase(); + // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim + // naechsten Softhyphen wieder umgebrochen! + if ( !bFull ) + { + rInf.ClrUnderFlow(); + if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() && + pPor->GetLen() && !pPor->InFldGrp() ) + { + // The distance between two different scripts is set + // to 20% of the fontheight. + xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); + if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) && + nTmp != rInf.GetTxt().Len() ) + { + USHORT nDist = (USHORT)(rInf.GetFont()->GetHeight()/5); + + if( nDist ) + { + // we do not want a kerning portion if any end + // would be a punctuation character + const CharClass& rCC = GetAppCharClass(); + if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) && + rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) ) + { + // does the kerning portion still fit into the line? + if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() ) + new SwKernPortion( *pPor, nDist ); + else + bFull = sal_True; + } + } + } + } + } + + if ( bHasGrid && pPor != pGridKernPortion && ! pMulti ) + { + xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); + const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width(); + + const BYTE nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() ); + const BYTE nNextScript = nTmp >= rInf.GetTxt().Len() ? + SW_CJK : + SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo ); + + // snap non-asian text to grid if next portion is ASIAN or + // there are no more portions in this line + // be careful when handling an underflow event: the gridkernportion + // could have been deleted + if ( nRestWidth > 0 && SW_CJK != nCurrScript && + ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) ) + { + ASSERT( pGridKernPortion, "No GridKernPortion available" ) + + // calculate size + SwLinePortion* pTmpPor = pGridKernPortion->GetPortion(); + USHORT nSumWidth = pPor->Width(); + while ( pTmpPor ) + { + nSumWidth = nSumWidth + pTmpPor->Width(); + pTmpPor = pTmpPor->GetPortion(); + } + + const USHORT i = nSumWidth ? + ( nSumWidth - 1 ) / nGridWidth + 1 : + 0; + const SwTwips nTmpWidth = i * nGridWidth; + const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth), + nRestWidth ); + const USHORT nKernWidth_1 = (USHORT)(nKernWidth / 2); + + ASSERT( nKernWidth <= nRestWidth, + "Not enough space left for adjusting non-asian text in grid mode" ) + + pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 ); + rInf.X( rInf.X() + nKernWidth_1 ); + + if ( ! bFull ) + new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1), + sal_False, sal_True ); + + pGridKernPortion = 0; + } + else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() || + pPor->IsFlyCntPortion() || pPor->InNumberGrp() || + pPor->InFldGrp() || nCurrScript != nNextScript ) + // next portion should snap to grid + pGridKernPortion = 0; + } + + rInf.SetFull( bFull ); + + // Restportions von mehrzeiligen Feldern haben bisher noch + // nicht den richtigen Ascent. + if ( !pPor->GetLen() && !pPor->IsFlyPortion() + && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp() + && !pPor->IsMultiPortion() ) + CalcAscent( rInf, pPor ); + + InsertPortion( rInf, pPor ); + pPor = NewPortion( rInf ); + } + + if( !rInf.IsStop() ) + { + // der letzte rechte, zentrierte, dezimale Tab + SwTabPortion *pLastTab = rInf.GetLastTab(); + if( pLastTab ) + pLastTab->FormatEOL( rInf ); + else if( rInf.GetLast() && rInf.LastKernPortion() ) + rInf.GetLast()->FormatEOL( rInf ); + } + if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp() + && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() ) + rInf.SetNumDone( sal_False ); + + // 3260, 3860: Fly auf jeden Fall loeschen! + ClearFly( rInf ); +} + +/************************************************************************* + * SwTxtFormatter::CalcAdjustLine() + *************************************************************************/ + +void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent ) +{ + if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti) + { + pCurrent->SetFormatAdj(sal_True); + if( IsFlyInCntBase() ) + { + CalcAdjLine( pCurrent ); + // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint + // auf jeden Fall umsetzen, deshalb bAllWays = sal_True + UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True ); + } + } +} + +/************************************************************************* + * SwTxtFormatter::CalcAscent() + *************************************************************************/ + +void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor ) +{ + if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() ) + { + // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten, + // dann ist ihre Groesse unabhaengig von harten Attributierungen. + SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt; + SwFontSave aSave( rInf, pFldFnt ); + ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) ); + ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) ); + } + // --> OD 2008-06-05 #i89179# + // tab portion representing the list tab of a list label gets the + // same height and ascent as the corresponding number portion + else if ( pPor->InTabGrp() && pPor->GetLen() == 0 && + rInf.GetLast() && rInf.GetLast()->InNumberGrp() && + static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() ) + { + const SwLinePortion* pLast = rInf.GetLast(); + pPor->Height( pLast->Height() ); + pPor->SetAscent( pLast->GetAscent() ); + } + // <-- + else + { + const SwLinePortion *pLast = rInf.GetLast(); + sal_Bool bChg; + + // Fallunterscheidung: in leeren Zeilen werden die Attribute + // per SeekStart angeschaltet. + const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); + if ( pPor->IsQuoVadisPortion() ) + bChg = SeekStartAndChg( rInf, sal_True ); + else + { + if( bFirstPor ) + { + if( rInf.GetTxt().Len() ) + { + if ( pPor->GetLen() || !rInf.GetIdx() + || ( pCurr != pLast && !pLast->IsFlyPortion() ) + || !pCurr->IsRest() ) // statt !rInf.GetRest() + bChg = SeekAndChg( rInf ); + else + bChg = SeekAndChgBefore( rInf ); + } + else if ( pMulti ) + // do not open attributes starting at 0 in empty multi + // portions (rotated numbering followed by a footnote + // can cause trouble, because the footnote attribute + // starts at 0, but if we open it, the attribute handler + // cannot handle it. + bChg = sal_False; + else + bChg = SeekStartAndChg( rInf ); + } + else + bChg = SeekAndChg( rInf ); + } + if( bChg || bFirstPor || !pPor->GetAscent() + || !rInf.GetLast()->InTxtGrp() ) + { + pPor->SetAscent( rInf.GetAscent() ); + pPor->Height( rInf.GetTxtHeight() ); + } + else + { + pPor->Height( pLast->Height() ); + pPor->SetAscent( pLast->GetAscent() ); + } + } +} + +/************************************************************************* + * class SwMetaPortion + *************************************************************************/ + +class SwMetaPortion : public SwTxtPortion +{ +public: + inline SwMetaPortion() { SetWhichPor( POR_META ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; +// OUTPUT_OPERATOR +}; + +//CLASSIO( SwMetaPortion ) + +/************************************************************************* + * virtual SwMetaPortion::Paint() + *************************************************************************/ + +void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if ( Width() ) + { + rInf.DrawViewOpt( *this, POR_META ); + SwTxtPortion::Paint( rInf ); + } +} + + +/************************************************************************* + * SwTxtFormatter::WhichTxtPor() + *************************************************************************/ + +SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const +{ + SwTxtPortion *pPor = 0; + if( GetFnt()->IsTox() ) + pPor = new SwToxPortion; + else + { + if( GetFnt()->IsRef() ) + pPor = new SwRefPortion; + else if (GetFnt()->IsMeta()) + { + pPor = new SwMetaPortion; + } + else + { + // Erst zum Schluss ! + // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben, + // z.B. bei nicht darstellbaren Zeichen. + if( rInf.GetLen() > 0 ) + { + if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART ) + pPor = new SwFieldMarkPortion(); + else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND ) + pPor = new SwFieldMarkPortion(); + else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT ) + pPor = new SwFieldFormPortion(); + } + if( !pPor ) + { + if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() ) + pPor = pCurr; + else + { + pPor = new SwTxtPortion; + if( GetFnt()->IsURL() ) + pPor->SetWhichPor( POR_URL ); + } + } + } + } + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::NewTxtPortion() + *************************************************************************/ +// Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert: +// 1) Tabs +// 2) Linebreaks +// 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD +// 4) naechster Attributwechsel + +SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf ) +{ + // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr + // Wenn pCurr nicht von SwTxtPortion abgeleitet ist, + // muessen wir duplizieren ... + Seek( rInf.GetIdx() ); + SwTxtPortion *pPor = WhichTxtPor( rInf ); + + // until next attribute change: + const xub_StrLen nNextAttr = GetNextAttr(); + xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() ); + + // end of script type: + const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() ); + nNextChg = Min( nNextChg, nNextScript ); + + // end of direction: + const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() ); + nNextChg = Min( nNextChg, nNextDir ); + + // 7515, 7516, 3470, 6441 : Turbo-Boost + // Es wird unterstellt, dass die Buchstaben eines Fonts nicht + // groesser als doppelt so breit wie hoch sind. + // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen. + // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe + // ergibt sich erst im CalcAscent! + // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times + // New Roman besitzt einen Ascent von 182, eine Hoehe von 200 + // und eine Breite von 53! Daraus folgt, dass eine Zeile mit + // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von + // Faktor 2 auf 8 (wg. negativen Kernings). + + pPor->SetLen(1); + CalcAscent( rInf, pPor ); + + const SwFont* pTmpFnt = rInf.GetFont(); + KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ), + KSHORT( pPor->GetAscent() ) ) / 8; + if ( !nExpect ) + nExpect = 1; + nExpect = (USHORT)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect)); + if( nExpect > rInf.GetIdx() && nNextChg > nExpect ) + nNextChg = Min( nExpect, rInf.GetTxt().Len() ); + + // we keep an invariant during method calls: + // there are no portion ending characters like hard spaces + // or tabs in [ nLeftScanIdx, nRightScanIdx ] + if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx ) + { + if ( nNextChg > nRightScanIdx ) + nNextChg = nRightScanIdx = + rInf.ScanPortionEnd( nRightScanIdx, nNextChg ); + } + else + { + nLeftScanIdx = rInf.GetIdx(); + nNextChg = nRightScanIdx = + rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg ); + } + + pPor->SetLen( nNextChg - rInf.GetIdx() ); + rInf.SetLen( pPor->GetLen() ); + return pPor; +} + + +/************************************************************************* + * SwTxtFormatter::WhichFirstPortion() + *************************************************************************/ + +SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf) +{ + SwLinePortion *pPor = 0; + + if( rInf.GetRest() ) + { + // 5010: Tabs und Felder + if( '\0' != rInf.GetHookChar() ) + return 0; + + pPor = rInf.GetRest(); + if( pPor->IsErgoSumPortion() ) + rInf.SetErgoDone(sal_True); + else + if( pPor->IsFtnNumPortion() ) + rInf.SetFtnDone(sal_True); + else + if( pPor->InNumberGrp() ) + rInf.SetNumDone(sal_True); + if( pPor ) + { + rInf.SetRest(0); + pCurr->SetRest( sal_True ); + return pPor; + } + } + + // ???? und ????: im Follow duerfen wir schon stehen, + // entscheidend ist, ob pFrm->GetOfst() == 0 ist! + if( rInf.GetIdx() ) + { + // Nun koennen auch FtnPortions und ErgoSumPortions + // verlaengert werden. + + // 1) Die ErgoSumTexte + if( !rInf.IsErgoDone() ) + { + if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) + pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); + rInf.SetErgoDone( sal_True ); + } + + // 2) Arrow portions + if( !pPor && !rInf.IsArrowDone() ) + { + if( pFrm->GetOfst() && !pFrm->IsFollow() && + rInf.GetIdx() == pFrm->GetOfst() ) + pPor = new SwArrowPortion( *pCurr ); + rInf.SetArrowDone( sal_True ); + } + + // 3) Kerning portions at beginning of line in grid mode + if ( ! pPor && ! pCurr->GetPortion() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + pPor = new SwKernPortion( *pCurr ); + } + + // 4) Die Zeilenreste (mehrzeilige Felder) + if( !pPor ) + { + pPor = rInf.GetRest(); + // 6922: Nur bei pPor natuerlich. + if( pPor ) + { + pCurr->SetRest( sal_True ); + rInf.SetRest(0); + } + } + } + else + { + // 5) Die Fussnotenzahlen + if( !rInf.IsFtnDone() ) + { + ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), + "Rotated number portion trouble" ) + + sal_Bool bFtnNum = pFrm->IsFtnNumFrm(); + rInf.GetParaPortion()->SetFtnNum( bFtnNum ); + if( bFtnNum ) + pPor = (SwLinePortion*)NewFtnNumPortion( rInf ); + rInf.SetFtnDone( sal_True ); + } + + // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster, + // entscheidend ist, ob der SwFtnFrm ein Follow ist. + if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() ) + { + if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) + pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); + rInf.SetErgoDone( sal_True ); + } + + // 7) Die Numerierungen + if( !rInf.IsNumDone() && !pPor ) + { + ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), + "Rotated number portion trouble" ) + + // Wenn wir im Follow stehen, dann natuerlich nicht. + if( GetTxtFrm()->GetTxtNode()->GetNumRule() ) + pPor = (SwLinePortion*)NewNumberPortion( rInf ); + rInf.SetNumDone( sal_True ); + } + // 8) Die DropCaps + if( !pPor && GetDropFmt() && ! rInf.IsMulti() ) + pPor = (SwLinePortion*)NewDropPortion( rInf ); + + // 9) Kerning portions at beginning of line in grid mode + if ( !pPor && !pCurr->GetPortion() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + pPor = new SwKernPortion( *pCurr ); + } + } + + // 10) Decimal tab portion at the beginning of each line in table cells + if ( !pPor && !pCurr->GetPortion() && + GetTxtFrm()->IsInTab() && + GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) ) + { + pPor = NewTabPortion( rInf, true ); + } + + // 11) suffix of meta-field + if (!pPor) + { + pPor = TryNewNoLengthPortion(rInf); + } + + return pPor; +} + +sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr ) +{ + if( !pCurr->GetNext() ) + return sal_False; + const SwLinePortion *pPor = pCurr->GetNext()->GetPortion(); + sal_Bool bRet = sal_False; + while( pPor && !bRet ) + { + bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) || + (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld()); + if( !pPor->GetLen() ) + break; + pPor = pPor->GetPortion(); + } + return bRet; +} + +/************************************************************************* + * SwTxtFormatter::NewPortion() + *************************************************************************/ + +/* NewPortion stellt rInf.nLen ein. + * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr, + * attrwechsel. + * Drei Faelle koennen eintreten: + * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert + * -> return 0; + * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert + * -> Breite neu einstellen und return new FlyPortion + * 3) Es muss eine neue Portion gebaut werden. + * -> CalcFlyWidth emuliert ggf. die Breite und return Portion + */ + +SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf ) +{ + // Underflow hat Vorrang + rInf.SetStopUnderFlow( sal_False ); + if( rInf.GetUnderFlow() ) + { + ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" ); + return UnderFlow( rInf ); + } + + // Wenn die Zeile voll ist, koennten noch Flys oder + // UnderFlow-LinePortions warten ... + if( rInf.IsFull() ) + { + // ????: LineBreaks und Flys (bug05.sdw) + // 8450: IsDummy() + if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) ) + return 0; + + // Wenn der Text an den Fly gestossen ist, oder wenn + // der Fly als erstes drankommt, weil er ueber dem linken + // Rand haengt, wird GetFly() returnt. + // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's + // naturgemaesz eine 0. + if( rInf.GetFly() ) + { + if( rInf.GetLast()->IsBreakPortion() ) + { + delete rInf.GetFly(); + rInf.SetFly( 0 ); + } + + return rInf.GetFly(); + } + // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den + // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest + // bekanntgeben, damit SwTxtFrm::Format nicht abbricht + // (die Textmasse wurde ja durchformatiert). + if( rInf.GetRest() ) + rInf.SetNewLine( sal_True ); + else + { + // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt, + // jetzt aber kein Rest mehr anliegt, + // muss sie auf jeden Fall neu formatiert werden! + if( lcl_OldFieldRest( GetCurr() ) ) + rInf.SetNewLine( sal_True ); + else + { + SwLinePortion *pFirst = WhichFirstPortion( rInf ); + if( pFirst ) + { + rInf.SetNewLine( sal_True ); + if( pFirst->InNumberGrp() ) + rInf.SetNumDone( sal_False) ; + delete pFirst; + } + } + } + + return 0; + } + + SwLinePortion *pPor = WhichFirstPortion( rInf ); + + // Check for Hidden Portion: + if ( !pPor ) + { + xub_StrLen nEnd = rInf.GetIdx(); + if ( lcl_BuildHiddenPortion( rInf, nEnd ) ) + pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() ); + } + + if( !pPor ) + { + if( ( !pMulti || pMulti->IsBidi() ) && + // --> FME 2005-02-14 #i42734# + // No multi portion if there is a hook character waiting: + ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) ) + // <-- + { + // We open a multiportion part, if we enter a multi-line part + // of the paragraph. + xub_StrLen nEnd = rInf.GetIdx(); + SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti ); + if( pCreate ) + { + SwMultiPortion* pTmp = NULL; + + if ( SW_MC_BIDI == pCreate->nId ) + pTmp = new SwBidiPortion( nEnd, pCreate->nLevel ); + else if ( SW_MC_RUBY == pCreate->nId ) + { + Seek( rInf.GetIdx() ); + sal_Bool bRubyTop; + sal_Bool* pRubyPos = 0; + + if ( rInf.SnapToGrid() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + { + bRubyTop = ! pGrid->GetRubyTextBelow(); + pRubyPos = &bRubyTop; + } + } + + pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(), + *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(), + nEnd, 0, pRubyPos ); + } + else if( SW_MC_ROTATE == pCreate->nId ) + pTmp = new SwRotatedPortion( *pCreate, nEnd, + GetTxtFrm()->IsRightToLeft() ); + else + pTmp = new SwDoubleLinePortion( *pCreate, nEnd ); + + delete pCreate; + CalcFlyWidth( rInf ); + + return pTmp; + } + } + // 5010: Tabs und Felder + xub_Unicode cChar = rInf.GetHookChar(); + + if( cChar ) + { + /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das + * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile + * gewandert ist ( so geschehen hinter Rahmen ). + * Wenn allerdings eine FldPortion im Rest wartet, muessen wir + * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei + * DezimalTabs und Feldern (22615) + */ + if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() ) + cChar = rInf.GetChar( rInf.GetIdx() ); + rInf.ClearHookChar(); + } + else + { + if( rInf.GetIdx() >= rInf.GetTxt().Len() ) + { + rInf.SetFull(sal_True); + CalcFlyWidth( rInf ); + return pPor; + } + cChar = rInf.GetChar( rInf.GetIdx() ); + } + + switch( cChar ) + { + case CH_TAB: + pPor = NewTabPortion( rInf, false ); break; + + case CH_BREAK: + pPor = new SwBreakPortion( *rInf.GetLast() ); break; + + case CHAR_SOFTHYPHEN: // soft hyphen + pPor = new SwSoftHyphPortion; break; + + case CHAR_HARDBLANK: // no-break space + pPor = new SwBlankPortion( ' ' ); break; + + case CHAR_HARDHYPHEN: // non-breaking hyphen + pPor = new SwBlankPortion( '-' ); break; + + case CHAR_ZWSP: // zero width space + case CHAR_ZWNBSP : // word joiner +// case CHAR_RLM : // right to left mark +// case CHAR_LRM : // left to right mark + pPor = new SwControlCharPortion( cChar ); break; + + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + if( rInf.HasHint( rInf.GetIdx() ) ) + { + pPor = NewExtraPortion( rInf ); + break; + } + // No break + default : + { + SwTabPortion* pLastTabPortion = rInf.GetLastTab(); + if ( pLastTabPortion && cChar == rInf.GetTabDecimal() ) + { + // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full: + // We have a decimal tab portion in the line and the next character has to be + // aligned at the tab stop position. We store the width from the beginning of + // the tab stop portion up to the portion containint the decimal separator: + if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ && + POR_TABDECIMAL == pLastTabPortion->GetWhichPor() ) + { + ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" ) + const USHORT nWidthOfPortionsUpToDecimalPosition = (USHORT)(rInf.X() - pLastTabPortion->Fix() ); + static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition ); + rInf.SetTabDecimal( 0 ); + } + // <-- + else + rInf.SetFull( rInf.GetLastTab()->Format( rInf ) ); + } + + if( rInf.GetRest() ) + { + if( rInf.IsFull() ) + { + rInf.SetNewLine(sal_True); + return 0; + } + pPor = rInf.GetRest(); + rInf.SetRest(0); + } + else + { + if( rInf.IsFull() ) + return 0; + pPor = NewTxtPortion( rInf ); + } + break; + } + } + + // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht, + // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet + // hat, weil z.B. ein Tab enthalten ist. + if( pPor && rInf.GetRest() ) + pPor->SetLen( 0 ); + + // robust: + if( !pPor || rInf.IsStop() ) + { + delete pPor; + return 0; + } + } + + // Special portions containing numbers (footnote anchor, footnote number, + // numbering) can be contained in a rotated portion, if the user + // choose a rotated character attribute. + if ( pPor && ! pMulti ) + { + if ( pPor->IsFtnPortion() ) + { + const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn(); + + if ( pTxtFtn ) + { + SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn(); + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + const SwEndNoteInfo* pInfo; + if( rFtn.IsEndNote() ) + pInfo = &pDoc->GetEndNoteInfo(); + else + pInfo = &pDoc->GetFtnInfo(); + const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet(); + + const SfxPoolItem* pItem; + USHORT nDir = 0; + if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE, + sal_True, &pItem )) + nDir = ((SvxCharRotateItem*)pItem)->GetValue(); + + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ? + DIR_BOTTOM2TOP : + DIR_TOP2BOTTOM ); + } + } + } + else if ( pPor->InNumberGrp() ) + { + const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont(); + + if ( pNumFnt ) + { + USHORT nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion( 0, 900 == nDir ? + DIR_BOTTOM2TOP : + DIR_TOP2BOTTOM ); + + rInf.SetNumDone( sal_False ); + rInf.SetFtnDone( sal_False ); + } + } + } + } + + // Der Font wird im Outputdevice eingestellt, + // der Ascent und die Hoehe werden berechnet. + if( !pPor->GetAscent() && !pPor->Height() ) + CalcAscent( rInf, pPor ); + rInf.SetLen( pPor->GetLen() ); + + // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. + CalcFlyWidth( rInf ); + + // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige + // Werte bereithalten muss: + if( !pCurr->Height() ) + { + ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" ); + pCurr->Height( pPor->Height() ); + pCurr->SetAscent( pPor->GetAscent() ); + } + + ASSERT( !pPor || pPor->Height(), + "SwTxtFormatter::NewPortion: something went wrong"); + if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() ) + { + delete pPor; + pPor = rInf.GetFly(); + } + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::FormatLine() + *************************************************************************/ + +xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos ) +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" ); + + // For the formatting routines, we set pOut to the reference device. + SwHookOut aHook( GetInfo() ); + if( GetInfo().GetLen() < GetInfo().GetTxt().Len() ) + GetInfo().SetLen( GetInfo().GetTxt().Len() ); + + sal_Bool bBuild = sal_True; + SetFlyInCntBase( sal_False ); + GetInfo().SetLineHeight( 0 ); + GetInfo().SetLineNettoHeight( 0 ); + + // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden + // und auch bei geaendertem Ascent (Absenken der Grundlinie). + const KSHORT nOldHeight = pCurr->Height(); + const KSHORT nOldAscent = pCurr->GetAscent(); + + pCurr->SetEndHyph( sal_False ); + pCurr->SetMidHyph( sal_False ); + + // fly positioning can make it necessary format a line several times + // for this, we have to keep a copy of our rest portion + SwLinePortion* pFld = GetInfo().GetRest(); + SwFldPortion* pSaveFld = 0; + + if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() ) + pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) ); + + // for an optimal repaint rectangle, we want to compare fly portions + // before and after the BuildPortions call + const sal_Bool bOptimizeRepaint = AllowRepaintOpt(); + const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen(); + SvLongs* pFlyStart = 0; + + // these are the conditions for a fly position comparison + if ( bOptimizeRepaint && pCurr->IsFly() ) + { + pFlyStart = new SvLongs; + SwLinePortion* pPor = pCurr->GetFirstPortion(); + long nPOfst = 0; + USHORT nCnt = 0; + + while ( pPor ) + { + if ( pPor->IsFlyPortion() ) + // insert start value of fly portion + pFlyStart->Insert( nPOfst, nCnt++ ); + + nPOfst += pPor->Width(); + pPor = pPor->GetPortion(); + } + } + + // Hier folgt bald die Unterlaufpruefung. + while( bBuild ) + { + GetInfo().SetFtnInside( sal_False ); + GetInfo().SetOtherThanFtnInside( sal_False ); + + // These values must not be reset by FormatReset(); + sal_Bool bOldNumDone = GetInfo().IsNumDone(); + sal_Bool bOldArrowDone = GetInfo().IsArrowDone(); + sal_Bool bOldErgoDone = GetInfo().IsErgoDone(); + + // besides other things, this sets the repaint offset to 0 + FormatReset( GetInfo() ); + + GetInfo().SetNumDone( bOldNumDone ); + GetInfo().SetArrowDone( bOldArrowDone ); + GetInfo().SetErgoDone( bOldErgoDone ); + + // build new portions for this line + BuildPortions( GetInfo() ); + + if( GetInfo().IsStop() ) + { + pCurr->SetLen( 0 ); + pCurr->Height( GetFrmRstHeight() + 1 ); + pCurr->SetRealHeight( GetFrmRstHeight() + 1 ); + pCurr->Width(0); + pCurr->Truncate(); + return nStartPos; + } + else if( GetInfo().IsDropInit() ) + { + DropInit(); + GetInfo().SetDropInit( sal_False ); + } + + pCurr->CalcLine( *this, GetInfo() ); + CalcRealHeight( GetInfo().IsNewLine() ); + + if ( IsFlyInCntBase() && !IsQuick() ) + { + KSHORT nTmpAscent, nTmpHeight; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + AlignFlyInCntBase( Y() + long( nTmpAscent ) ); + pCurr->CalcLine( *this, GetInfo() ); + CalcRealHeight(); + } + + // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird + if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() ) + { + pCurr->SetRealHeight( GetInfo().GetLineHeight() ); + bBuild = sal_False; + } + else + { + bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow( GetInfo() ) + || GetInfo().CheckFtnPortion( pCurr ) ); + if( bBuild ) + { + GetInfo().SetNumDone( bOldNumDone ); + GetInfo().ResetMaxWidthDiff(); + + // delete old rest + if ( GetInfo().GetRest() ) + { + delete GetInfo().GetRest(); + GetInfo().SetRest( 0 ); + } + + // set original rest portion + if ( pSaveFld ) + GetInfo().SetRest( new SwFldPortion( *pSaveFld ) ); + + pCurr->SetLen( 0 ); + pCurr->Width(0); + pCurr->Truncate(); + } + } + } + + // calculate optimal repaint rectangle + if ( bOptimizeRepaint ) + { + GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) ); + if ( pFlyStart ) + delete pFlyStart; + } + else + // Special case: We do not allow an optimitation of the repaint + // area, but during formatting the repaint offset is set to indicate + // a maximum value for the offset. This value has to be reset: + GetInfo().SetPaintOfst( 0 ); + + // This corrects the start of the reformat range if something has + // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt + // will give us a wrong result if we have to reformat another line + GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() ); + + // delete master copy of rest portion + if ( pSaveFld ) + delete pSaveFld; + + xub_StrLen nNewStart = nStartPos + pCurr->GetLen(); + + // adjust text if kana compression is enabled + if ( GetInfo().CompressLine() ) + { + SwTwips nRepaintOfst = CalcKanaAdj( pCurr ); + + // adjust repaint offset + if ( nRepaintOfst < GetInfo().GetPaintOfst() ) + GetInfo().SetPaintOfst( nRepaintOfst ); + } + + CalcAdjustLine( pCurr ); + + if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() ) + { + SetFlyInCntBase(); + GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling + // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind + // auch formatiert werden. + GetInfo().SetShift( sal_True ); + } + + if ( IsFlyInCntBase() && !IsQuick() ) + UpdatePos( pCurr, GetTopLeft(), GetStart() ); + + return nNewStart; +} + +/************************************************************************* + * SwTxtFormatter::RecalcRealHeight() + *************************************************************************/ + +void SwTxtFormatter::RecalcRealHeight() +{ + sal_Bool bMore = sal_True; + while(bMore) + { + DBG_LOOP; + CalcRealHeight(); + bMore = Next() != 0; + } +} + +/************************************************************************* + * SwTxtFormatter::CalcRealHeight() + *************************************************************************/ + +void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine ) +{ + KSHORT nLineHeight = pCurr->Height(); + pCurr->SetClipping( sal_False ); + + GETGRID( pFrm->FindPageFrm() ) + if ( pGrid && GetInfo().SnapToGrid() ) + { + const USHORT nGridWidth = pGrid->GetBaseHeight(); + const USHORT nRubyHeight = pGrid->GetRubyHeight(); + const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); + + nLineHeight = nGridWidth + nRubyHeight; + USHORT nLineDist = nLineHeight; + + while ( pCurr->Height() > nLineHeight ) + nLineHeight = nLineHeight + nLineDist; + + KSHORT nAsc = pCurr->GetAscent() + + ( bRubyTop ? + ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 : + ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 ); + + pCurr->Height( nLineHeight ); + pCurr->SetAscent( nAsc ); + pInf->GetParaPortion()->SetFixLineHeight(); + + // we ignore any line spacing options except from ... + const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing(); + if ( ! IsParaLine() && pSpace && + SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() ) + { + ULONG nTmp = pSpace->GetPropLineSpace(); + + if( nTmp < 100 ) + nTmp = 100; + + nTmp *= nLineHeight; + nLineHeight = (USHORT)(nTmp / 100); + } + + pCurr->SetRealHeight( nLineHeight ); + return; + } + + // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese + // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere + // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem + // Shift-Return), die das Register durchaus beachten soll. + if( !pCurr->IsDummy() || ( !pCurr->GetNext() && + GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) ) + { + const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing(); + if( pSpace ) + { + switch( pSpace->GetLineSpaceRule() ) + { + case SVX_LINE_SPACE_AUTO: + break; + case SVX_LINE_SPACE_MIN: + { + if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) ) + nLineHeight = pSpace->GetLineHeight(); + break; + } + case SVX_LINE_SPACE_FIX: + { + nLineHeight = pSpace->GetLineHeight(); + KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80% + if( nAsc < pCurr->GetAscent() || + nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() ) + pCurr->SetClipping( sal_True ); + pCurr->Height( nLineHeight ); + pCurr->SetAscent( nAsc ); + pInf->GetParaPortion()->SetFixLineHeight(); + } + break; + default: ASSERT( sal_False, ": unknown LineSpaceRule" ); + } + if( !IsParaLine() ) + switch( pSpace->GetInterLineSpaceRule() ) + { + case SVX_INTER_LINE_SPACE_OFF: + break; + case SVX_INTER_LINE_SPACE_PROP: + { + long nTmp = pSpace->GetPropLineSpace(); + // 50% ist das Minimum, bei 0% schalten wir auf + // den Defaultwert 100% um ... + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + + nTmp *= nLineHeight; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + nLineHeight = (KSHORT)nTmp; + break; + } + case SVX_INTER_LINE_SPACE_FIX: + { + nLineHeight = nLineHeight + pSpace->GetInterLineSpace(); + break; + } + default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); + } + } +#if OSL_DEBUG_LEVEL > 1 + KSHORT nDummy = nLineHeight + 1; + (void)nDummy; +#endif + + if( IsRegisterOn() ) + { + SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height(); + SWRECTFN( pFrm ) + if ( bVert ) + nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY ); + nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() ); + KSHORT nDiff = KSHORT( nTmpY % RegDiff() ); + if( nDiff ) + nLineHeight += RegDiff() - nDiff; + } + } + pCurr->SetRealHeight( nLineHeight ); +} + +/************************************************************************* + * SwTxtFormatter::FeedInf() + *************************************************************************/ + +void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const +{ + // 3260, 3860: Fly auf jeden Fall loeschen! + ClearFly( rInf ); + rInf.Init(); + + rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); + rInf.SetRoot( pCurr ); + rInf.SetLineStart( nStart ); + rInf.SetIdx( nStart ); + + // Handle overflows: + // --> FME 2004-11-25 #i34348# Changed type from USHORT to SwTwips + SwTwips nTmpLeft = Left(); + SwTwips nTmpRight = Right(); + SwTwips nTmpFirst = FirstLeft(); + // <-- + + if ( nTmpLeft > USHRT_MAX || + nTmpRight > USHRT_MAX || + nTmpFirst > USHRT_MAX ) + { + SWRECTFN( rInf.GetTxtFrm() ) + nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)(); + nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)(); + nTmpFirst = nTmpLeft; + } + + rInf.Left( nTmpLeft ); + rInf.Right( nTmpRight ); + rInf.First( nTmpFirst ); + + rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) ); + rInf.Width( rInf.RealWidth() ); + if( ((SwTxtFormatter*)this)->GetRedln() ) + { + ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() ); + ((SwTxtFormatter*)this)->GetRedln()->Reset(); + } +} + +/************************************************************************* + * SwTxtFormatter::FormatReset() + *************************************************************************/ + +void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf ) +{ + pCurr->Truncate(); + pCurr->Init(); + if( pBlink && pCurr->IsBlinking() ) + pBlink->Delete( pCurr ); + + // delete pSpaceAdd und pKanaComp + pCurr->FinishSpaceAdd(); + pCurr->FinishKanaComp(); + pCurr->ResetFlags(); + FeedInf( rInf ); +} + +/************************************************************************* + * SwTxtFormatter::CalcOnceMore() + *************************************************************************/ + +sal_Bool SwTxtFormatter::CalcOnceMore() +{ + if( pDropFmt ) + { + const KSHORT nOldDrop = GetDropHeight(); + CalcDropHeight( pDropFmt->GetLines() ); + bOnceMore = nOldDrop != GetDropHeight(); + } + else + bOnceMore = sal_False; + return bOnceMore; +} + +/************************************************************************* + * SwTxtFormatter::CalcBottomLine() + *************************************************************************/ + +SwTwips SwTxtFormatter::CalcBottomLine() const +{ + SwTwips nRet = Y() + GetLineHeight(); + SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom(); + if( nMin && ++nMin > nRet ) + { + SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height() + - pFrm->Prt().Top(); + if( nRet + nDist < nMin ) + { + sal_Bool bRepaint = HasTruncLines() && + GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1; + nRet = nMin - nDist; + if( bRepaint ) + { + ((SwRepaint*)GetInfo().GetParaPortion() + ->GetRepaint())->Bottom( nRet-1 ); + ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 ); + } + } + } + return nRet; +} + +/************************************************************************* + * SwTxtFormatter::_CalcFitToContent() + * + * FME/OD: This routine does a limited text formatting. + *************************************************************************/ + +SwTwips SwTxtFormatter::_CalcFitToContent() +{ + FormatReset( GetInfo() ); + BuildPortions( GetInfo() ); + pCurr->CalcLine( *this, GetInfo() ); + return pCurr->Width(); +} + +/************************************************************************* + * SwTxtFormatter::AllowRepaintOpt() + * + * determines if the calculation of a repaint offset is allowed + * otherwise each line is painted from 0 (this is a copy of the beginning + * of the former SwTxtFormatter::Recycle() function + *************************************************************************/ +sal_Bool SwTxtFormatter::AllowRepaintOpt() const +{ + // reformat position in front of current line? Only in this case + // we want to set the repaint offset + sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() && + pCurr->GetLen(); + + // a special case is the last line of a block adjusted paragraph: + if ( bOptimizeRepaint ) + { + switch( GetAdjust() ) + { + case SVX_ADJUST_BLOCK: + { + if( IsLastBlock() || IsLastCenter() ) + bOptimizeRepaint = sal_False; + else + { + // ????: Blank in der letzten Masterzeile (blocksat.sdw) + bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow(); + if ( bOptimizeRepaint ) + { + SwLinePortion *pPos = pCurr->GetFirstPortion(); + while ( pPos && !pPos->IsFlyPortion() ) + pPos = pPos->GetPortion(); + bOptimizeRepaint = !pPos; + } + } + break; + } + case SVX_ADJUST_CENTER: + case SVX_ADJUST_RIGHT: + bOptimizeRepaint = sal_False; + break; + default: ; + } + } + + // Schon wieder ein Sonderfall: unsichtbare SoftHyphs + const xub_StrLen nReformat = GetInfo().GetReformatStart(); + if( bOptimizeRepaint && STRING_LEN != nReformat ) + { + const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat ); + bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh ) + || ! GetInfo().HasHint( nReformat ); + } + + return bOptimizeRepaint; +} + +/************************************************************************* + * SwTxtFormatter::CalcOptRepaint() + * + * calculates an optimal repaint offset for the current line + *************************************************************************/ +long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd, + const SvLongs* pFlyStart ) +{ + if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() ) + // the reformat position is behind our new line, that means + // something of our text has moved to the next line + return 0; + + xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd ); + + // in case we do not have any fly in our line, our repaint position + // is the changed position - 1 + if ( ! pFlyStart && ! pCurr->IsFly() ) + { + // this is the maximum repaint offset determined during formatting + // for example: the beginning of the first right tab stop + // if this value is 0, this means that we do not have an upper + // limit for the repaint offset + const long nFormatRepaint = GetInfo().GetPaintOfst(); + + if ( nReformat < GetInfo().GetLineStart() + 3 ) + return 0; + + // step back two positions for smoother repaint + nReformat -= 2; + +#ifndef QUARTZ +#ifndef ENABLE_GRAPHITE + // --> FME 2004-09-27 #i28795#, #i34607#, #i38388# + // step back six(!) more characters for complex scripts + // this is required e.g., for Khmer (thank you, Javier!) + const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); + xub_StrLen nMaxContext = 0; + if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) ) + nMaxContext = 6; +#else + // Some Graphite fonts need context for scripts not marked as complex + static const xub_StrLen nMaxContext = 10; +#endif +#else + // some fonts like Quartz's Zapfino need more context + // TODO: query FontInfo for maximum unicode context + static const xub_StrLen nMaxContext = 8; +#endif + if( nMaxContext > 0 ) + { + if ( nReformat > GetInfo().GetLineStart() + nMaxContext ) + nReformat = nReformat - nMaxContext; + else + nReformat = GetInfo().GetLineStart(); + } + // <-- + + // Weird situation: Our line used to end with a hole portion + // and we delete some characters at the end of our line. We have + // to take care for repainting the blanks which are not anymore + // covered by the hole portion + while ( nReformat > GetInfo().GetLineStart() && + CH_BLANK == GetInfo().GetChar( nReformat ) ) + --nReformat; + + ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" ); + SwRect aRect; + + // Note: GetChareRect is not const. It definitely changes the + // bMulti flag. We have to save and resore the old value. + sal_Bool bOldMulti = GetInfo().IsMulti(); + GetCharRect( &aRect, nReformat ); + GetInfo().SetMulti( bOldMulti ); + + return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) : + aRect.Left(); + } + else + { + // nReformat may be wrong, if something around flys has changed: + // we compare the former and the new fly positions in this line + // if anything has changed, we carefully have to adjust the right + // repaint position + long nPOfst = 0; + USHORT nCnt = 0; + USHORT nX = 0; + USHORT nIdx = GetInfo().GetLineStart(); + SwLinePortion* pPor = pCurr->GetFirstPortion(); + + while ( pPor ) + { + if ( pPor->IsFlyPortion() ) + { + // compare start of fly with former start of fly + if ( pFlyStart && + nCnt < pFlyStart->Count() && + nX == (*pFlyStart)[ nCnt ] && + nIdx < nReformat + ) + // found fix position, nothing has changed left from nX + nPOfst = nX + pPor->Width(); + else + break; + + nCnt++; + } + nX = nX + pPor->Width(); + nIdx = nIdx + pPor->GetLen(); + pPor = pPor->GetPortion(); + } + + return nPOfst + GetLeftMargin(); + } +} + +bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ) +{ + // Only if hidden text should not be shown: +// if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() ) + const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar(); + const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( TRUE ) && rInf.GetOpt().IsPrinting(); + if (bShowInDocView || bShowForPrinting) + return false; + + const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo(); + xub_StrLen nHiddenStart; + xub_StrLen nHiddenEnd; + rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd ); + if ( nHiddenEnd ) + { + rPos = nHiddenEnd; + return true; + } + + return false; +} |