xref: /AOO41X/main/sw/source/core/text/portxt.cxx (revision e1d5bd03a6ea7ac2b26b792c9e2a94e9f347a43b)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include <ctype.h>
29 
30 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
31 #include <com/sun/star/i18n/ScriptType.hdl>
32 #endif
33 #include <hintids.hxx>     // CH_TXTATR
34 #include <errhdl.hxx>   // ASSERT
35 #include <SwPortionHandler.hxx>
36 #include <txtcfg.hxx>
37 #include <porlay.hxx>
38 #include <inftxt.hxx>
39 #include <guess.hxx>    // SwTxtGuess, Zeilenumbruch
40 #include <porglue.hxx>
41 #include <portab.hxx>       // pLastTab->
42 #include <porfld.hxx>       // SwFldPortion
43 #include <wrong.hxx>
44 #include <viewsh.hxx>
45 #include <IDocumentSettingAccess.hxx>
46 #include <viewopt.hxx>  // SwViewOptions
47 
48 #include <IMark.hxx>
49 #include <pam.hxx>
50 #include <doc.hxx>
51 #include <xmloff/odffields.hxx>
52 
53 #if OSL_DEBUG_LEVEL > 1
54 const sal_Char *GetLangName( const MSHORT nLang );
55 #endif
56 
57 using namespace ::sw::mark;
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::i18n::ScriptType;
60 
61 /*************************************************************************
62  *                          lcl_AddSpace
63  * Returns for how many characters an extra space has to be added
64  * (for justified alignment).
65  *************************************************************************/
66 
67 sal_uInt16 lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr,
68                      const SwLinePortion& rPor )
69 {
70     xub_StrLen nPos, nEnd;
71     const SwScriptInfo* pSI = 0;
72 
73     if ( pStr )
74     {
75         // passing a string means we are inside a field
76         nPos = 0;
77         nEnd = pStr->Len();
78     }
79     else
80     {
81         nPos = rInf.GetIdx();
82         nEnd = rInf.GetIdx() + rPor.GetLen();
83         pStr = &rInf.GetTxt();
84         pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo();
85     }
86 
87     sal_uInt16 nCnt = 0;
88     sal_uInt8 nScript = 0;
89 
90     // If portion consists of Asian characters and language is not
91     // Korean, we add extra space to each character.
92     // first we get the script type
93     if ( pSI )
94         nScript = pSI->ScriptType( nPos );
95     else if ( pBreakIt->GetBreakIter().is() )
96         nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos );
97 
98     // Note: rInf.GetIdx() can differ from nPos,
99     // e.g., when rPor is a field portion. nPos referes to the string passed
100     // to the function, rInf.GetIdx() referes to the original string.
101 
102     // We try to find out which justification mode is required. This is done by
103     // evaluating the script type and the language attribute set for this portion
104 
105     // Asian Justification: Each character get some extra space
106     if ( nEnd > nPos && ASIAN == nScript )
107     {
108         LanguageType aLang =
109             rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
110 
111         if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
112         {
113             const SwLinePortion* pPor = rPor.GetPortion();
114             if ( pPor && ( pPor->IsKernPortion() ||
115                            pPor->IsControlCharPortion() ||
116                            pPor->IsPostItsPortion() ) )
117                 pPor = pPor->GetPortion();
118 
119             nCnt += nEnd - nPos;
120 
121             if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
122                   pPor->IsBreakPortion() )
123                 --nCnt;
124 
125             return nCnt;
126         }
127     }
128 
129     // Kashida Justification: Insert Kashidas
130     if ( nEnd > nPos && pSI && COMPLEX == nScript )
131     {
132         if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
133         {
134             const sal_uInt16 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos );
135             // i60591: need to check result of KashidaJustify
136             // determine if kashida justification is applicable
137             if( nKashRes != STRING_LEN )
138                 return nKashRes;
139         }
140     }
141 
142     // Thai Justification: Each character cell gets some extra space
143     if ( nEnd > nPos && COMPLEX == nScript )
144     {
145         LanguageType aLang =
146             rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
147 
148         if ( LANGUAGE_THAI == aLang )
149         {
150             nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos );
151 
152             const SwLinePortion* pPor = rPor.GetPortion();
153             if ( pPor && ( pPor->IsKernPortion() ||
154                            pPor->IsControlCharPortion() ||
155                            pPor->IsPostItsPortion() ) )
156                 pPor = pPor->GetPortion();
157 
158             if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
159                 --nCnt;
160 
161             return nCnt;
162         }
163     }
164 
165     // Here starts the good old "Look for blanks and add space to them" part.
166     // Note: We do not want to add space to an isolated latin blank in front
167     // of some complex characters in RTL environment
168     const sal_Bool bDoNotAddSpace =
169             LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
170             ( i18n::ScriptType::COMPLEX ==
171               pSI->ScriptType( nPos + 1 ) ) &&
172             rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft();
173 
174     if ( bDoNotAddSpace )
175         return nCnt;
176 
177     for ( ; nPos < nEnd; ++nPos )
178     {
179         if( CH_BLANK == pStr->GetChar( nPos ) )
180             ++nCnt;
181     }
182 
183     // We still have to examine the next character:
184     // If the next character is ASIAN and not KOREAN we have
185     // to add an extra space
186     // nPos referes to the original string, even if a field string has
187     // been passed to this function
188     nPos = rInf.GetIdx() + rPor.GetLen();
189     if ( nPos < rInf.GetTxt().Len() )
190     {
191         sal_uInt8 nNextScript = 0;
192         const SwLinePortion* pPor = rPor.GetPortion();
193         if ( pPor && pPor->IsKernPortion() )
194             pPor = pPor->GetPortion();
195 
196         if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
197             return nCnt;
198 
199         // next character is inside a field?
200         if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
201         {
202             sal_Bool bOldOnWin = rInf.OnWin();
203             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
204 
205             XubString aStr( aEmptyStr );
206             pPor->GetExpTxt( rInf, aStr );
207             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
208 
209             nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
210         }
211         else
212             nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos );
213 
214         if( ASIAN == nNextScript )
215         {
216             LanguageType aLang =
217                 rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript );
218 
219             if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
220                 ++nCnt;
221         }
222     }
223 
224     return nCnt;
225 }
226 
227 /*************************************************************************
228  *                      class SwTxtPortion
229  *************************************************************************/
230 
231 SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion )
232   : SwLinePortion( rPortion )
233 {
234     SetWhichPor( POR_TXT );
235 }
236 
237 /*************************************************************************
238  *                      SwTxtPortion::BreakCut()
239  *************************************************************************/
240 
241 void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess )
242 {
243     // Das Wort/Zeichen ist groesser als die Zeile
244     // Sonderfall Nr.1: Das Wort ist groesser als die Zeile
245     // Wir kappen...
246     const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X());
247     xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx();
248     if( nLen )
249     {
250         // special case: guess does not always provide the correct
251         // width, only in common cases.
252         if ( !rGuess.BreakWidth() )
253         {
254             rInf.SetLen( nLen );
255             SetLen( nLen );
256             CalcTxtSize( rInf );
257 
258             // changing these values requires also changing them in
259             // guess.cxx
260             KSHORT nItalic = 0;
261             if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
262             {
263                 nItalic = Height() / 12;
264             }
265             Width( Width() + nItalic );
266         }
267         else
268         {
269             Width( rGuess.BreakWidth() );
270             SetLen( nLen );
271         }
272     }
273     // special case: first character does not fit to line
274     else if ( rGuess.CutPos() == rInf.GetLineStart() )
275     {
276         SetLen( 1 );
277         Width( nLineWidth );
278     }
279     else
280     {
281         SetLen( 0 );
282         Width( 0 );
283     }
284 }
285 
286 /*************************************************************************
287  *                      SwTxtPortion::BreakUnderflow()
288  *************************************************************************/
289 
290 void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf )
291 {
292     Truncate();
293     Height( 0 );
294     Width( 0 );
295     SetLen( 0 );
296     SetAscent( 0 );
297     rInf.SetUnderFlow( this );
298 }
299 
300  /*************************************************************************
301  *                      SwTxtPortion::_Format()
302  *************************************************************************/
303 
304 sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf )
305 {
306     String aTxt;
307     return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len();
308 }
309 
310 sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf )
311 {
312     // 5744: wenn nur der Trennstrich nicht mehr passt,
313     // muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True!
314     if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() )
315     {
316         // soft hyphen portion has triggered an underflow event because
317         // of an alternative spelling position
318         sal_Bool bFull = sal_False;
319         const sal_Bool bHyph = rInf.ChgHyph( sal_True );
320         if( rInf.IsHyphenate() )
321         {
322             SwTxtGuess aGuess;
323             // check for alternative spelling left from the soft hyphen
324             // this should usually be true but
325             aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
326             bFull = CreateHyphen( rInf, aGuess );
327             ASSERT( bFull, "Problem with hyphenation!!!" );
328         }
329         rInf.ChgHyph( bHyph );
330         rInf.SetSoftHyphPos( 0 );
331         return bFull;
332     }
333 
334     SwTxtGuess aGuess;
335     const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() );
336 
337     // these are the possible cases:
338     // A Portion fits to current line
339     // B Portion does not fit to current line but a possible line break
340     //   within the portion has been found by the break iterator, 2 subcases
341     //   B1 break is hyphen
342     //   B2 break is word end
343     // C Portion does not fit to current line and no possible line break
344     //   has been found by break iterator, 2 subcases:
345     //   C1 break iterator found a possible line break in portion before us
346     //      ==> this break is used (underflow)
347     //   C2 break iterator does not found a possible line break at all:
348     //      ==> line break
349 
350     // case A: line not yet full
351     if ( !bFull )
352     {
353         Width( aGuess.BreakWidth() );
354         // Vorsicht !
355         if( !InExpGrp() || InFldGrp() )
356             SetLen( rInf.GetLen() );
357 
358         short nKern = rInf.GetFont()->CheckKerning();
359         if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
360         {
361             nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
362             if( nKern < 0 )
363                 nKern = 0;
364         }
365         if( nKern )
366             new SwKernPortion( *this, nKern );
367     }
368     // special case: hanging portion
369     else if( bFull && aGuess.GetHangingPortion() )
370     {
371         Width( aGuess.BreakWidth() );
372         SetLen( aGuess.BreakPos() - rInf.GetIdx() );
373         Insert( aGuess.GetHangingPortion() );
374         aGuess.GetHangingPortion()->SetAscent( GetAscent() );
375         aGuess.ClearHangingPortion();
376     }
377     // breakPos >= index
378     else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN )
379     {
380         // case B1
381         if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
382             && ( aGuess.BreakPos() > rInf.GetIdx() ||
383                ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
384         {
385             CreateHyphen( rInf, aGuess );
386             if ( rInf.GetFly() )
387                 rInf.GetRoot()->SetMidHyph( sal_True );
388             else
389                 rInf.GetRoot()->SetEndHyph( sal_True );
390         }
391         // case C1
392         // - Footnote portions with fake line start (i.e., not at beginning of line)
393         //   should keep together with the text portion. (Note: no keep together
394         //   with only footnote portions.
395         // - TabPortions not at beginning of line should keep together with the
396         //   text portion, if they are not followed by a blank
397         //   (work around different definition of tab stop character - breaking or
398         //   non breaking character - in compatibility mode)
399         else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() &&
400                     // --> OD 2010-01-29 #b6921213#
401                     rInf.IsOtherThanFtnInside() ) ||
402                     // <--
403                   ( rInf.GetLast() &&
404                     rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) &&
405                     rInf.GetLast()->InTabGrp() &&
406                     rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
407                     aGuess.BreakPos() == rInf.GetIdx()  &&
408                     CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
409                     0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
410             BreakUnderflow( rInf );
411         // case B2
412         else if( rInf.GetIdx() > rInf.GetLineStart() ||
413                  aGuess.BreakPos() > rInf.GetIdx() ||
414                  // this is weird: during formatting the follow of a field
415                  // the values rInf.GetIdx and rInf.GetLineStart are replaced
416                  // IsFakeLineStart indicates GetIdx > GetLineStart
417                  rInf.IsFakeLineStart() ||
418                  rInf.GetFly() ||
419                  rInf.IsFirstMulti() ||
420                  ( rInf.GetLast() &&
421                     ( rInf.GetLast()->IsFlyPortion() ||
422                         ( rInf.GetLast()->InFldGrp() &&
423                           ! rInf.GetLast()->InNumberGrp() &&
424                           ! rInf.GetLast()->IsErgoSumPortion() &&
425                           lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) )
426         {
427             if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
428                 Width( aGuess.BreakWidth() );
429             else
430                 // this actually should not happen
431                 Width( KSHORT(rInf.Width() - rInf.X()) );
432 
433             SetLen( aGuess.BreakPos() - rInf.GetIdx() );
434 
435             ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(),
436                     "Trouble with expanded field portions during line break" );
437             const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
438             if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
439             {
440                 SwHolePortion *pNew = new SwHolePortion( *this );
441                 pNew->SetLen( nRealStart - aGuess.BreakPos() );
442                 Insert( pNew );
443             }
444         }
445         else    // case C2, last exit
446             BreakCut( rInf, aGuess );
447     }
448     // breakPos < index or no breakpos at all
449     else
450     {
451         sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
452         if( aGuess.BreakPos() != STRING_LEN &&
453             aGuess.BreakPos() != rInf.GetLineStart() &&
454             ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
455               rInf.IsFirstMulti() ) &&
456             ( !rInf.GetLast()->IsBlankPortion() ||  ((SwBlankPortion*)
457               rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True )))
458         {       // case C1 (former BreakUnderflow())
459             BreakUnderflow( rInf );
460         }
461         else
462              // case C2, last exit
463             BreakCut( rInf, aGuess );
464     }
465 
466     return bFull;
467 }
468 
469 /*************************************************************************
470  *                 virtual SwTxtPortion::Format()
471  *************************************************************************/
472 
473 
474 
475 sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf )
476 {
477 #if OSL_DEBUG_LEVEL > 1
478     const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) );
479 #endif
480 
481     if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
482     {
483         Height( 0 );
484         Width( 0 );
485         SetLen( 0 );
486         SetAscent( 0 );
487         SetPortion( NULL );  // ????
488         return sal_True;
489     }
490 
491     ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()),
492         "SwTxtPortion::Format: missing real width" );
493     ASSERT( Height(), "SwTxtPortion::Format: missing height" );
494 
495     return _Format( rInf );
496 }
497 
498 /*************************************************************************
499  *                 virtual SwTxtPortion::FormatEOL()
500  *************************************************************************/
501 
502 // Format end of line
503 // 5083: Es kann schon manchmal unguenstige Faelle geben...
504 // "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz
505 // zu "vom" und " ", wobei der Glue expandiert wird, statt in die
506 // MarginPortion aufzugehen.
507 // rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte
508 // Buchstabe der Portion.
509 
510 
511 
512 void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf )
513 {
514     if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
515         !GetPortion()->GetPortion() ) ) && GetLen() &&
516         rInf.GetIdx() < rInf.GetTxt().Len() &&
517         1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
518         && !rInf.GetLast()->IsHolePortion() )
519     {
520         // calculate number of blanks
521         xub_StrLen nX = rInf.GetIdx() - 1;
522         sal_uInt16 nHoleLen = 1;
523         while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
524             nHoleLen++;
525 
526         // Erst uns einstellen und dann Inserten, weil wir ja auch ein
527         // SwLineLayout sein koennten.
528         KSHORT nBlankSize;
529         if( nHoleLen == GetLen() )
530             nBlankSize = Width();
531         else
532             nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width();
533         Width( Width() - nBlankSize );
534         rInf.X( rInf.X() - nBlankSize );
535         SetLen( GetLen() - nHoleLen );
536         SwLinePortion *pHole = new SwHolePortion( *this );
537         ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize );
538         ( (SwHolePortion *)pHole )->SetLen( nHoleLen );
539         Insert( pHole );
540     }
541 }
542 
543 /*************************************************************************
544  *               virtual SwTxtPortion::GetCrsrOfst()
545  *************************************************************************/
546 
547 
548 
549 xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const
550 {
551     ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" );
552     return SwLinePortion::GetCrsrOfst( nOfst );
553 }
554 
555 /*************************************************************************
556  *                virtual SwTxtPortion::GetTxtSize()
557  *************************************************************************/
558 // Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist
559 
560 SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
561 {
562     return rInf.GetTxtSize();
563 }
564 
565 /*************************************************************************
566  *               virtual SwTxtPortion::Paint()
567  *************************************************************************/
568 
569 
570 
571 void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const
572 {
573     if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx()))
574     {
575         rInf.DrawBackBrush( *this );
576         const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND);
577         rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
578     }
579     else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx()))
580     {
581         rInf.DrawBackBrush( *this );
582         const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART);
583         rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
584     }
585     else if( GetLen() )
586     {
587         rInf.DrawBackBrush( *this );
588 
589         // do we have to repaint a post it portion?
590         if( rInf.OnWin() && pPortion && !pPortion->Width() )
591             pPortion->PrePaint( rInf, this );
592 
593         const SwWrongList *pWrongList = rInf.GetpWrongList();
594         const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
595         // SMARTTAGS
596         const SwWrongList *pSmarttags = rInf.GetSmartTags();
597 
598         const bool bWrong = 0 != pWrongList;
599         const bool bGrammarCheck = 0 != pGrammarCheckList;
600         const bool bSmartTags = 0 != pSmarttags;
601 
602         if ( bWrong || bSmartTags || bGrammarCheck )
603             rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck );
604         else
605             rInf.DrawText( *this, rInf.GetLen(), sal_False );
606     }
607 }
608 
609 /*************************************************************************
610  *              virtual SwTxtPortion::GetExpTxt()
611  *************************************************************************/
612 
613 
614 
615 sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const
616 {
617     return sal_False;
618 }
619 
620 /*************************************************************************
621  *        xub_StrLen SwTxtPortion::GetSpaceCnt()
622  *              long SwTxtPortion::CalcSpacing()
623  * sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks
624  * und den daraus resultierenden zusaetzlichen Zwischenraum
625  *************************************************************************/
626 
627 xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf,
628                                       xub_StrLen& rCharCnt ) const
629 {
630     xub_StrLen nCnt = 0;
631     xub_StrLen nPos = 0;
632     if ( InExpGrp() )
633     {
634         if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
635         {
636             // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
637             // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
638             sal_Bool bOldOnWin = rInf.OnWin();
639             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
640 
641             XubString aStr( aEmptyStr );
642             GetExpTxt( rInf, aStr );
643             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
644 
645             nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
646             nPos = aStr.Len();
647         }
648     }
649     else if( !IsDropPortion() )
650     {
651         nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
652         nPos = GetLen();
653     }
654     rCharCnt = rCharCnt + nPos;
655     return nCnt;
656 }
657 
658 long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const
659 {
660     xub_StrLen nCnt = 0;
661 
662     if ( InExpGrp() )
663     {
664         if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
665         {
666             // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
667             // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
668             sal_Bool bOldOnWin = rInf.OnWin();
669             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
670 
671             XubString aStr( aEmptyStr );
672             GetExpTxt( rInf, aStr );
673             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
674             if( nSpaceAdd > 0 )
675                 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
676             else
677             {
678                 nSpaceAdd = -nSpaceAdd;
679                 nCnt = aStr.Len();
680             }
681         }
682     }
683     else if( !IsDropPortion() )
684     {
685         if( nSpaceAdd > 0 )
686             nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
687         else
688         {
689             nSpaceAdd = -nSpaceAdd;
690             nCnt = GetLen();
691             SwLinePortion* pPor = GetPortion();
692 
693             // we do not want an extra space in front of margin portions
694             if ( nCnt )
695             {
696                 while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
697                     pPor = pPor->GetPortion();
698 
699                 if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
700                     --nCnt;
701             }
702         }
703     }
704 
705     return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
706 }
707 
708 /*************************************************************************
709  *              virtual SwTxtPortion::HandlePortion()
710  *************************************************************************/
711 
712 void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const
713 {
714     rPH.Text( GetLen(), GetWhichPor() );
715 }
716 
717 /*************************************************************************
718  *                      class SwHolePortion
719  *************************************************************************/
720 
721 
722 
723 SwHolePortion::SwHolePortion( const SwTxtPortion &rPor )
724     : nBlankWidth( 0 )
725 {
726     SetLen( 1 );
727     Height( rPor.Height() );
728     SetAscent( rPor.GetAscent() );
729     SetWhichPor( POR_HOLE );
730 }
731 
732 SwLinePortion *SwHolePortion::Compress() { return this; }
733 
734 /*************************************************************************
735  *               virtual SwHolePortion::Paint()
736  *************************************************************************/
737 
738 
739 
740 void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const
741 {
742     // --> FME 2004-06-24 #i16816# tagged pdf support
743     if( rInf.GetVsh() && rInf.GetVsh()->GetViewOptions()->IsPDFExport() )
744     {
745         const XubString aTxt( ' ' );
746         rInf.DrawText( aTxt, *this, 0, 1, false );
747     }
748     // <--
749 }
750 
751 /*************************************************************************
752  *                 virtual SwHolePortion::Format()
753  *************************************************************************/
754 
755 
756 
757 sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf )
758 {
759     return rInf.IsFull() || rInf.X() >= rInf.Width();
760 }
761 
762 /*************************************************************************
763  *              virtual SwHolePortion::HandlePortion()
764  *************************************************************************/
765 
766 void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
767 {
768     rPH.Text( GetLen(), GetWhichPor() );
769 }
770 
771 void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const
772 {
773     // These shouldn't be painted!
774     // SwTxtPortion::Paint(rInf);
775 }
776 
777 sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & )
778 {
779     sal_Bool ret=0;
780     Width(0);
781     return ret;
782 }
783 
784 namespace {
785     static sal_Int32 getCurrentListIndex( IFieldmark* pBM,
786             ::rtl::OUString* io_pCurrentText = NULL )
787     {
788         const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
789         sal_Int32 nCurrentIdx = 0;
790         const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_RESULT));
791         if(pResult != pParameters->end())
792             pResult->second >>= nCurrentIdx;
793         if(io_pCurrentText)
794         {
795             const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_LISTENTRY));
796             if(pListEntries != pParameters->end())
797             {
798                 uno::Sequence< ::rtl::OUString > vListEntries;
799                 pListEntries->second >>= vListEntries;
800                 if(nCurrentIdx < vListEntries.getLength())
801                     *io_pCurrentText = vListEntries[nCurrentIdx];
802             }
803         }
804         return nCurrentIdx;
805     }
806 }
807 
808 //FIXME Fieldbk
809 void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const
810 {
811     SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode());
812     const SwDoc *doc=pNd->GetDoc();
813     SwIndex aIndex( pNd, rInf.GetIdx() );
814     SwPosition aPosition(*pNd, aIndex);
815 
816     IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
817 
818     OSL_ENSURE( pBM,
819         "SwFieldFormPortion::Paint(..)"
820         " - Where is my form field bookmark???");
821 
822     if ( pBM != NULL )
823     {
824         if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
825         { // a checkbox...
826             ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
827             bool checked = pCheckboxFm->IsChecked();
828             rInf.DrawCheckBox(*this, checked);
829         }
830         else if ( pBM->GetFieldname( ).equalsAscii(  ODF_FORMDROPDOWN ) )
831         { // a list...
832             rtl::OUString aTxt;
833             rInf.DrawViewOpt( *this, POR_FLD );
834             rInf.DrawText( aTxt, *this, 0, 0/*aTxt.getLength()*/, false );
835         }
836         else
837         {
838             assert(0); // unknown type...
839         }
840     }
841 }
842 
843 sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo & rInf )
844 {
845     sal_Bool ret = 0;
846     SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm(  )->GetTxtNode(  ) );
847     const SwDoc *doc = pNd->GetDoc(  );
848     SwIndex aIndex( pNd, rInf.GetIdx(  ) );
849     SwPosition aPosition( *pNd, aIndex );
850     IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
851     ASSERT( pBM != NULL, "Where is my form field bookmark???" );
852     if ( pBM != NULL )
853     {
854         if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
855         {
856             Width( rInf.GetTxtHeight(  ) );
857             Height( rInf.GetTxtHeight(  ) );
858             SetAscent( rInf.GetAscent(  ) );
859         }
860         else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
861         {
862             ::rtl::OUString aTxt;
863             getCurrentListIndex( pBM, &aTxt );
864             SwPosSize aPosSize = rInf.GetTxtSize( aTxt );
865             Width( aPosSize.Width(  ) );
866             Height( aPosSize.Height(  ) );
867             SetAscent( rInf.GetAscent(  ) );
868         }
869         else
870         {
871             assert( 0 );        // unknown type...
872         }
873     }
874     return ret;
875 }
876 
877 
878