| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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( (sal_uInt16)(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(); |
| } |
| |
| // --> OD 2010-07-07 #i112181# |
| rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() ); |
| // <-- |
| } |
| 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 sal_uInt16 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() ) |
| { |
| sal_uInt8 nNxtActual = rInf.GetFont()->GetActual(); |
| sal_uInt8 nLstActual = nNxtActual; |
| sal_uInt16 nLstHeight = (sal_uInt16)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 = (sal_uInt16)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 sal_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( (sal_uInt16)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() ) |
| { |
| sal_uInt16 nDist = (sal_uInt16)(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 sal_uInt8 nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() ); |
| const sal_uInt8 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(); |
| sal_uInt16 nSumWidth = pPor->Width(); |
| while ( pTmpPor ) |
| { |
| nSumWidth = nSumWidth + pTmpPor->Width(); |
| pTmpPor = pTmpPor->GetPortion(); |
| } |
| |
| const sal_uInt16 i = nSumWidth ? |
| ( nSumWidth - 1 ) / nGridWidth + 1 : |
| 0; |
| const SwTwips nTmpWidth = i * nGridWidth; |
| const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth), |
| nRestWidth ); |
| const sal_uInt16 nKernWidth_1 = (sal_uInt16)(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()->IsInputField() ) |
| { |
| pPor = new SwTxtInputFldPortion(); |
| } |
| 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 = (sal_uInt16)(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 |
| 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 sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(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; |
| sal_uInt16 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 ) |
| { |
| sal_uInt16 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; |
| sal_uInt16 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() ); |
| |
| //Bug 120864:For Special case that at the first caculation couldn't get correct height. And need to recaculate for the right height. |
| SwLinePortion* pPorTmp = pCurr->GetPortion(); |
| if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetPortion() && |
| pCurr->Height() > pPorTmp->Height()))) |
| //Bug 120864(End) |
| { |
| 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 sal_uInt16 nGridWidth = pGrid->GetBaseHeight(); |
| const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight(); |
| const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); |
| |
| nLineHeight = nGridWidth + nRubyHeight; |
| sal_uInt16 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() ) |
| { |
| sal_uLong nTmp = pSpace->GetPropLineSpace(); |
| |
| if( nTmp < 100 ) |
| nTmp = 100; |
| |
| nTmp *= nLineHeight; |
| nLineHeight = (sal_uInt16)(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 sal_uInt16 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; |
| sal_uInt16 nCnt = 0; |
| sal_uInt16 nX = 0; |
| sal_uInt16 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( sal_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; |
| } |