xref: /aoo4110/main/sw/source/core/text/portxt.cxx (revision b1cdbd2c)
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 #include <vcl/pdfextoutdevdata.hxx>
53 
54 #if OSL_DEBUG_LEVEL > 1
55 const sal_Char *GetLangName( const MSHORT nLang );
56 #endif
57 
58 using namespace ::sw::mark;
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::i18n::ScriptType;
61 
62 /*************************************************************************
63  *                          lcl_AddSpace
64  * Returns for how many characters an extra space has to be added
65  * (for justified alignment).
66  *************************************************************************/
67 
lcl_AddSpace(const SwTxtSizeInfo & rInf,const XubString * pStr,const SwLinePortion & rPor)68 sal_uInt16 lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr,
69                      const SwLinePortion& rPor )
70 {
71     xub_StrLen nPos, nEnd;
72     const SwScriptInfo* pSI = 0;
73 
74     if ( pStr )
75     {
76         // passing a string means we are inside a field
77         nPos = 0;
78         nEnd = pStr->Len();
79     }
80     else
81     {
82         nPos = rInf.GetIdx();
83         nEnd = rInf.GetIdx() + rPor.GetLen();
84         pStr = &rInf.GetTxt();
85         pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo();
86     }
87 
88     sal_uInt16 nCnt = 0;
89     sal_uInt8 nScript = 0;
90 
91     // If portion consists of Asian characters and language is not
92     // Korean, we add extra space to each character.
93     // first we get the script type
94     if ( pSI )
95         nScript = pSI->ScriptType( nPos );
96     else if ( pBreakIt->GetBreakIter().is() )
97         nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos );
98 
99     // Note: rInf.GetIdx() can differ from nPos,
100     // e.g., when rPor is a field portion. nPos referes to the string passed
101     // to the function, rInf.GetIdx() referes to the original string.
102 
103     // We try to find out which justification mode is required. This is done by
104     // evaluating the script type and the language attribute set for this portion
105 
106     // Asian Justification: Each character get some extra space
107     if ( nEnd > nPos && ASIAN == nScript )
108     {
109         LanguageType aLang =
110             rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
111 
112         if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
113         {
114             const SwLinePortion* pPor = rPor.GetPortion();
115             if ( pPor && ( pPor->IsKernPortion() ||
116                            pPor->IsControlCharPortion() ||
117                            pPor->IsPostItsPortion() ) )
118                 pPor = pPor->GetPortion();
119 
120             nCnt += nEnd - nPos;
121 
122             if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
123                   pPor->IsBreakPortion() )
124                 --nCnt;
125 
126             return nCnt;
127         }
128     }
129 
130     // Kashida Justification: Insert Kashidas
131     if ( nEnd > nPos && pSI && COMPLEX == nScript )
132     {
133         if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
134         {
135             const sal_uInt16 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos );
136             // i60591: need to check result of KashidaJustify
137             // determine if kashida justification is applicable
138             if( nKashRes != STRING_LEN )
139                 return nKashRes;
140         }
141     }
142 
143     // Thai Justification: Each character cell gets some extra space
144     if ( nEnd > nPos && COMPLEX == nScript )
145     {
146         LanguageType aLang =
147             rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
148 
149         if ( LANGUAGE_THAI == aLang )
150         {
151             nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos );
152 
153             const SwLinePortion* pPor = rPor.GetPortion();
154             if ( pPor && ( pPor->IsKernPortion() ||
155                            pPor->IsControlCharPortion() ||
156                            pPor->IsPostItsPortion() ) )
157                 pPor = pPor->GetPortion();
158 
159             if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
160                 --nCnt;
161 
162             return nCnt;
163         }
164     }
165 
166     // Here starts the good old "Look for blanks and add space to them" part.
167     // Note: We do not want to add space to an isolated latin blank in front
168     // of some complex characters in RTL environment
169     const sal_Bool bDoNotAddSpace =
170             LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
171             ( i18n::ScriptType::COMPLEX ==
172               pSI->ScriptType( nPos + 1 ) ) &&
173             rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft();
174 
175     if ( bDoNotAddSpace )
176         return nCnt;
177 
178     for ( ; nPos < nEnd; ++nPos )
179     {
180         if( CH_BLANK == pStr->GetChar( nPos ) )
181             ++nCnt;
182     }
183 
184     // We still have to examine the next character:
185     // If the next character is ASIAN and not KOREAN we have
186     // to add an extra space
187     // nPos referes to the original string, even if a field string has
188     // been passed to this function
189     nPos = rInf.GetIdx() + rPor.GetLen();
190     if ( nPos < rInf.GetTxt().Len() )
191     {
192         sal_uInt8 nNextScript = 0;
193         const SwLinePortion* pPor = rPor.GetPortion();
194         if ( pPor && pPor->IsKernPortion() )
195             pPor = pPor->GetPortion();
196 
197         if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
198             return nCnt;
199 
200         // next character is inside a field?
201         if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
202         {
203             sal_Bool bOldOnWin = rInf.OnWin();
204             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
205 
206             XubString aStr( aEmptyStr );
207             pPor->GetExpTxt( rInf, aStr );
208             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
209 
210             nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
211         }
212         else
213             nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos );
214 
215         if( ASIAN == nNextScript )
216         {
217             LanguageType aLang =
218                 rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript );
219 
220             if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
221                 ++nCnt;
222         }
223     }
224 
225     return nCnt;
226 }
227 
228 /*************************************************************************
229  *                      class SwTxtPortion
230  *************************************************************************/
231 
SwTxtPortion(const SwLinePortion & rPortion)232 SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion )
233   : SwLinePortion( rPortion )
234 {
235     SetWhichPor( POR_TXT );
236 }
237 
238 /*************************************************************************
239  *                      SwTxtPortion::BreakCut()
240  *************************************************************************/
241 
BreakCut(SwTxtFormatInfo & rInf,const SwTxtGuess & rGuess)242 void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess )
243 {
244     // Das Wort/Zeichen ist groesser als die Zeile
245     // Sonderfall Nr.1: Das Wort ist groesser als die Zeile
246     // Wir kappen...
247     const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X());
248     xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx();
249     if( nLen )
250     {
251         // special case: guess does not always provide the correct
252         // width, only in common cases.
253         if ( !rGuess.BreakWidth() )
254         {
255             rInf.SetLen( nLen );
256             SetLen( nLen );
257             CalcTxtSize( rInf );
258 
259             // changing these values requires also changing them in
260             // guess.cxx
261             KSHORT nItalic = 0;
262             if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
263             {
264                 nItalic = Height() / 12;
265             }
266             Width( Width() + nItalic );
267         }
268         else
269         {
270             Width( rGuess.BreakWidth() );
271             SetLen( nLen );
272         }
273     }
274     // special case: first character does not fit to line
275     else if ( rGuess.CutPos() == rInf.GetLineStart() )
276     {
277         SetLen( 1 );
278         Width( nLineWidth );
279     }
280     else
281     {
282         SetLen( 0 );
283         Width( 0 );
284     }
285 }
286 
287 /*************************************************************************
288  *                      SwTxtPortion::BreakUnderflow()
289  *************************************************************************/
290 
BreakUnderflow(SwTxtFormatInfo & rInf)291 void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf )
292 {
293     Truncate();
294     Height( 0 );
295     Width( 0 );
296     SetLen( 0 );
297     SetAscent( 0 );
298     rInf.SetUnderFlow( this );
299 }
300 
301  /*************************************************************************
302  *                      SwTxtPortion::_Format()
303  *************************************************************************/
304 
lcl_HasContent(const SwFldPortion & rFld,SwTxtFormatInfo & rInf)305 sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf )
306 {
307     String aTxt;
308     return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len();
309 }
310 
_Format(SwTxtFormatInfo & rInf)311 sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf )
312 {
313     // 5744: wenn nur der Trennstrich nicht mehr passt,
314     // muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True!
315     if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() )
316     {
317         // soft hyphen portion has triggered an underflow event because
318         // of an alternative spelling position
319         sal_Bool bFull = sal_False;
320         const sal_Bool bHyph = rInf.ChgHyph( sal_True );
321         if( rInf.IsHyphenate() )
322         {
323             SwTxtGuess aGuess;
324             // check for alternative spelling left from the soft hyphen
325             // this should usually be true but
326             aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
327             bFull = CreateHyphen( rInf, aGuess );
328             ASSERT( bFull, "Problem with hyphenation!!!" );
329         }
330         rInf.ChgHyph( bHyph );
331         rInf.SetSoftHyphPos( 0 );
332         return bFull;
333     }
334 
335     SwTxtGuess aGuess;
336     const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() );
337 
338     // these are the possible cases:
339     // A Portion fits to current line
340     // B Portion does not fit to current line but a possible line break
341     //   within the portion has been found by the break iterator, 2 subcases
342     //   B1 break is hyphen
343     //   B2 break is word end
344     // C Portion does not fit to current line and no possible line break
345     //   has been found by break iterator, 2 subcases:
346     //   C1 break iterator found a possible line break in portion before us
347     //      ==> this break is used (underflow)
348     //   C2 break iterator does not found a possible line break at all:
349     //      ==> line break
350 
351     // case A: line not yet full
352     if ( !bFull )
353     {
354         Width( aGuess.BreakWidth() );
355         // Vorsicht !
356         if( !InExpGrp() || InFldGrp() )
357             SetLen( rInf.GetLen() );
358 
359         short nKern = rInf.GetFont()->CheckKerning();
360         if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
361         {
362             nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
363             if( nKern < 0 )
364                 nKern = 0;
365         }
366         if( nKern )
367             new SwKernPortion( *this, nKern );
368     }
369     // special case: hanging portion
370     else if( bFull && aGuess.GetHangingPortion() )
371     {
372         Width( aGuess.BreakWidth() );
373         SetLen( aGuess.BreakPos() - rInf.GetIdx() );
374         Insert( aGuess.GetHangingPortion() );
375         aGuess.GetHangingPortion()->SetAscent( GetAscent() );
376         aGuess.ClearHangingPortion();
377     }
378     // breakPos >= index
379     else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN )
380     {
381         // case B1
382         if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
383             && ( aGuess.BreakPos() > rInf.GetIdx() ||
384                ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
385         {
386             CreateHyphen( rInf, aGuess );
387             if ( rInf.GetFly() )
388                 rInf.GetRoot()->SetMidHyph( sal_True );
389             else
390                 rInf.GetRoot()->SetEndHyph( sal_True );
391         }
392         // case C1
393         // - Footnote portions with fake line start (i.e., not at beginning of line)
394         //   should keep together with the text portion. (Note: no keep together
395         //   with only footnote portions.
396         // - TabPortions not at beginning of line should keep together with the
397         //   text portion, if they are not followed by a blank
398         //   (work around different definition of tab stop character - breaking or
399         //   non breaking character - in compatibility mode)
400         else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() &&
401                     // --> OD 2010-01-29 #b6921213#
402                     rInf.IsOtherThanFtnInside() ) ||
403                     // <--
404                   ( rInf.GetLast() &&
405                     rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) &&
406                     rInf.GetLast()->InTabGrp() &&
407                     rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
408                     aGuess.BreakPos() == rInf.GetIdx()  &&
409                     CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
410                     0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
411             BreakUnderflow( rInf );
412         // case B2
413         else if( rInf.GetIdx() > rInf.GetLineStart() ||
414                  aGuess.BreakPos() > rInf.GetIdx() ||
415                  // this is weird: during formatting the follow of a field
416                  // the values rInf.GetIdx and rInf.GetLineStart are replaced
417                  // IsFakeLineStart indicates GetIdx > GetLineStart
418                  rInf.IsFakeLineStart() ||
419                  rInf.GetFly() ||
420                  rInf.IsFirstMulti() ||
421                  ( rInf.GetLast() &&
422                     ( rInf.GetLast()->IsFlyPortion() ||
423                         ( rInf.GetLast()->InFldGrp() &&
424                           ! rInf.GetLast()->InNumberGrp() &&
425                           ! rInf.GetLast()->IsErgoSumPortion() &&
426                           lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) )
427         {
428             if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
429                 Width( aGuess.BreakWidth() );
430             else
431                 // this actually should not happen
432                 Width( KSHORT(rInf.Width() - rInf.X()) );
433 
434             SetLen( aGuess.BreakPos() - rInf.GetIdx() );
435 
436             ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(),
437                     "Trouble with expanded field portions during line break" );
438             const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
439             if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
440             {
441                 SwHolePortion *pNew = new SwHolePortion( *this );
442                 pNew->SetLen( nRealStart - aGuess.BreakPos() );
443                 Insert( pNew );
444             }
445         }
446         else    // case C2, last exit
447             BreakCut( rInf, aGuess );
448     }
449     // breakPos < index or no breakpos at all
450     else
451     {
452         sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
453         if( aGuess.BreakPos() != STRING_LEN &&
454             aGuess.BreakPos() != rInf.GetLineStart() &&
455             ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
456               rInf.IsFirstMulti() ) &&
457             ( !rInf.GetLast()->IsBlankPortion() ||  ((SwBlankPortion*)
458               rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True )))
459         {       // case C1 (former BreakUnderflow())
460             BreakUnderflow( rInf );
461         }
462         else
463              // case C2, last exit
464             BreakCut( rInf, aGuess );
465     }
466 
467     return bFull;
468 }
469 
470 /*************************************************************************
471  *                 virtual SwTxtPortion::Format()
472  *************************************************************************/
473 
474 
475 
Format(SwTxtFormatInfo & rInf)476 sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf )
477 {
478 #if OSL_DEBUG_LEVEL > 1
479     const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) );
480 #endif
481 
482     if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
483     {
484         Height( 0 );
485         Width( 0 );
486         SetLen( 0 );
487         SetAscent( 0 );
488         SetPortion( NULL );  // ????
489         return sal_True;
490     }
491 
492     ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()),
493         "SwTxtPortion::Format: missing real width" );
494     ASSERT( Height(), "SwTxtPortion::Format: missing height" );
495 
496     return _Format( rInf );
497 }
498 
499 /*************************************************************************
500  *                 virtual SwTxtPortion::FormatEOL()
501  *************************************************************************/
502 
503 // Format end of line
504 // 5083: Es kann schon manchmal unguenstige Faelle geben...
505 // "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz
506 // zu "vom" und " ", wobei der Glue expandiert wird, statt in die
507 // MarginPortion aufzugehen.
508 // rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte
509 // Buchstabe der Portion.
510 
511 
512 
FormatEOL(SwTxtFormatInfo & rInf)513 void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf )
514 {
515     if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
516         !GetPortion()->GetPortion() ) ) && GetLen() &&
517         rInf.GetIdx() < rInf.GetTxt().Len() &&
518         1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
519         && !rInf.GetLast()->IsHolePortion() )
520     {
521         // calculate number of blanks
522         xub_StrLen nX = rInf.GetIdx() - 1;
523         sal_uInt16 nHoleLen = 1;
524         while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
525             nHoleLen++;
526 
527         // Erst uns einstellen und dann Inserten, weil wir ja auch ein
528         // SwLineLayout sein koennten.
529         KSHORT nBlankSize;
530         if( nHoleLen == GetLen() )
531             nBlankSize = Width();
532         else
533             nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width();
534         Width( Width() - nBlankSize );
535         rInf.X( rInf.X() - nBlankSize );
536         SetLen( GetLen() - nHoleLen );
537         SwLinePortion *pHole = new SwHolePortion( *this );
538         ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize );
539         ( (SwHolePortion *)pHole )->SetLen( nHoleLen );
540         Insert( pHole );
541     }
542 }
543 
544 /*************************************************************************
545  *               virtual SwTxtPortion::GetCrsrOfst()
546  *************************************************************************/
547 
548 
549 
GetCrsrOfst(const KSHORT nOfst) const550 xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const
551 {
552     ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" );
553     return SwLinePortion::GetCrsrOfst( nOfst );
554 }
555 
556 /*************************************************************************
557  *                virtual SwTxtPortion::GetTxtSize()
558  *************************************************************************/
559 // Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist
560 
GetTxtSize(const SwTxtSizeInfo & rInf) const561 SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
562 {
563     return rInf.GetTxtSize();
564 }
565 
566 /*************************************************************************
567  *               virtual SwTxtPortion::Paint()
568  *************************************************************************/
569 
570 
571 
Paint(const SwTxtPaintInfo & rInf) const572 void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const
573 {
574     if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx()))
575     {
576         rInf.DrawBackBrush( *this );
577         const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND);
578         rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
579     }
580     else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx()))
581     {
582         rInf.DrawBackBrush( *this );
583         const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART);
584         rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
585     }
586     else if( GetLen() )
587     {
588         rInf.DrawBackBrush( *this );
589 
590         // do we have to repaint a post it portion?
591         if( rInf.OnWin() && pPortion && !pPortion->Width() )
592             pPortion->PrePaint( rInf, this );
593 
594         const SwWrongList *pWrongList = rInf.GetpWrongList();
595         const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
596         // SMARTTAGS
597         const SwWrongList *pSmarttags = rInf.GetSmartTags();
598 
599         const bool bWrong = 0 != pWrongList;
600         const bool bGrammarCheck = 0 != pGrammarCheckList;
601         const bool bSmartTags = 0 != pSmarttags;
602 
603         if ( bWrong || bSmartTags || bGrammarCheck )
604             rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck );
605         else
606             rInf.DrawText( *this, rInf.GetLen(), sal_False );
607     }
608 }
609 
610 /*************************************************************************
611  *              virtual SwTxtPortion::GetExpTxt()
612  *************************************************************************/
613 
614 
615 
GetExpTxt(const SwTxtSizeInfo &,XubString &) const616 sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const
617 {
618     return sal_False;
619 }
620 
621 /*************************************************************************
622  *        xub_StrLen SwTxtPortion::GetSpaceCnt()
623  *              long SwTxtPortion::CalcSpacing()
624  * sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks
625  * und den daraus resultierenden zusaetzlichen Zwischenraum
626  *************************************************************************/
627 
GetSpaceCnt(const SwTxtSizeInfo & rInf,xub_StrLen & rCharCnt) const628 xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf,
629                                       xub_StrLen& rCharCnt ) const
630 {
631     xub_StrLen nCnt = 0;
632     xub_StrLen nPos = 0;
633     if ( InExpGrp() )
634     {
635         if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
636         {
637             // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
638             // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
639             sal_Bool bOldOnWin = rInf.OnWin();
640             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
641 
642             XubString aStr( aEmptyStr );
643             GetExpTxt( rInf, aStr );
644             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
645 
646             nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
647             nPos = aStr.Len();
648         }
649     }
650     else if( !IsDropPortion() )
651     {
652         nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
653         nPos = GetLen();
654     }
655     rCharCnt = rCharCnt + nPos;
656     return nCnt;
657 }
658 
CalcSpacing(long nSpaceAdd,const SwTxtSizeInfo & rInf) const659 long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const
660 {
661     xub_StrLen nCnt = 0;
662 
663     if ( InExpGrp() )
664     {
665         if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
666         {
667             // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
668             // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
669             sal_Bool bOldOnWin = rInf.OnWin();
670             ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
671 
672             XubString aStr( aEmptyStr );
673             GetExpTxt( rInf, aStr );
674             ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
675             if( nSpaceAdd > 0 )
676                 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
677             else
678             {
679                 nSpaceAdd = -nSpaceAdd;
680                 nCnt = aStr.Len();
681             }
682         }
683     }
684     else if( !IsDropPortion() )
685     {
686         if( nSpaceAdd > 0 )
687             nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
688         else
689         {
690             nSpaceAdd = -nSpaceAdd;
691             nCnt = GetLen();
692             SwLinePortion* pPor = GetPortion();
693 
694             // we do not want an extra space in front of margin portions
695             if ( nCnt )
696             {
697                 while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
698                     pPor = pPor->GetPortion();
699 
700                 if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
701                     --nCnt;
702             }
703         }
704     }
705 
706     return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
707 }
708 
709 /*************************************************************************
710  *              virtual SwTxtPortion::HandlePortion()
711  *************************************************************************/
712 
HandlePortion(SwPortionHandler & rPH) const713 void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const
714 {
715     rPH.Text( GetLen(), GetWhichPor() );
716 }
717 
718 
SwTxtInputFldPortion()719 SwTxtInputFldPortion::SwTxtInputFldPortion()
720     : SwTxtPortion()
721     , mbContainsInputFieldStart( false )
722     , mbContainsInputFieldEnd( false )
723 {
724     SetWhichPor( POR_INPUTFLD );
725 }
726 
727 
Format(SwTxtFormatInfo & rInf)728 sal_Bool SwTxtInputFldPortion::Format( SwTxtFormatInfo &rInf )
729 {
730     mbContainsInputFieldStart =
731         rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART;
732     mbContainsInputFieldEnd =
733         rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND;
734 
735     sal_Bool bRet = sal_False;
736     if ( rInf.GetLen() == 1
737          && ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) )
738     {
739         Width( 0 );
740     }
741     else
742     {
743         SwTxtSlot aFormatTxt( &rInf, this, true, true, 0 );
744         if ( rInf.GetLen() == 0 )
745         {
746             Width( 0 );
747         }
748         else
749         {
750             const xub_StrLen nFormerLineStart = rInf.GetLineStart();
751             if ( !mbContainsInputFieldStart )
752             {
753                 rInf.SetLineStart( 0 );
754             }
755 
756             bRet = SwTxtPortion::Format( rInf );
757 
758             if ( mbContainsInputFieldEnd )
759             {
760                 // adjust portion length accordingly, if complete text fits into the portion
761                 if ( GetLen() == rInf.GetLen() )
762                 {
763                     SetLen( GetLen() + 1 );
764                 }
765             }
766 
767             if ( mbContainsInputFieldStart )
768             {
769                 // adjust portion length accordingly
770                 SetLen( GetLen() + 1 );
771             }
772             else
773             {
774                 rInf.SetLineStart( nFormerLineStart );
775             }
776         }
777     }
778 
779     return bRet;
780 }
781 
Paint(const SwTxtPaintInfo & rInf) const782 void SwTxtInputFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
783 {
784     if ( Width() )
785     {
786         rInf.DrawViewOpt( *this, POR_INPUTFLD );
787         static sal_Char sSpace = ' ';
788         SwTxtSlot aPaintTxt( &rInf, this, true, true,
789                             ContainsOnlyDummyChars() ? &sSpace : 0 );
790         SwTxtPortion::Paint( rInf );
791     }
792 }
793 
GetExpTxt(const SwTxtSizeInfo & rInf,XubString & rTxt) const794 sal_Bool SwTxtInputFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
795 {
796     xub_StrLen nIdx = rInf.GetIdx();
797     xub_StrLen nLen = rInf.GetLen();
798     if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
799     {
800         ++nIdx;
801         --nLen;
802     }
803     if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND )
804     {
805         --nLen;
806     }
807     rTxt = rInf.GetTxt().Copy( nIdx, nLen );
808 
809     return sal_True;
810 }
811 
812 
GetTxtSize(const SwTxtSizeInfo & rInf) const813 SwPosSize SwTxtInputFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
814 {
815     SwTxtSlot aFormatTxt( &rInf, this, true, false, 0 );
816     if ( rInf.GetLen() == 0 )
817     {
818         return SwPosSize( 0, 0 );
819     }
820 
821     return rInf.GetTxtSize();
822 }
823 
824 
GetViewWidth(const SwTxtSizeInfo & rInf) const825 KSHORT SwTxtInputFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
826 {
827     if( !Width()
828         && ContainsOnlyDummyChars()
829         && !rInf.GetOpt().IsPagePreview()
830         && !rInf.GetOpt().IsReadonly()
831         && SwViewOption::IsFieldShadings() )
832     {
833         return rInf.GetTxtSize( ' ' ).Width();
834     }
835 
836     return SwTxtPortion::GetViewWidth( rInf );
837 }
838 
ContainsOnlyDummyChars() const839 bool SwTxtInputFldPortion::ContainsOnlyDummyChars() const
840 {
841     return GetLen() <= 2
842            && mbContainsInputFieldStart
843            && mbContainsInputFieldEnd;
844 }
845 
846 /*************************************************************************
847  *                      class SwHolePortion
848  *************************************************************************/
849 
850 
851 
SwHolePortion(const SwTxtPortion & rPor)852 SwHolePortion::SwHolePortion( const SwTxtPortion &rPor )
853     : nBlankWidth( 0 )
854 {
855     SetLen( 1 );
856     Height( rPor.Height() );
857     SetAscent( rPor.GetAscent() );
858     SetWhichPor( POR_HOLE );
859 }
860 
Compress()861 SwLinePortion *SwHolePortion::Compress() { return this; }
862 
863 /*************************************************************************
864  *               virtual SwHolePortion::Paint()
865  *************************************************************************/
866 
867 
868 
Paint(const SwTxtPaintInfo & rInf) const869 void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const
870 {
871     if( !rInf.GetOut() )
872         return;
873 
874     // #i16816# export stuff only needed for tagged pdf support
875     const vcl::PDFExtOutDevData* pPDFExt = dynamic_cast<const vcl::PDFExtOutDevData*>( rInf.GetOut()->GetExtOutDevData() );
876     if( !pPDFExt || !pPDFExt->GetIsExportTaggedPDF())
877         return;
878 
879     // #i68503# the hole must have no decoration for a consistent visual appearance
880     const SwFont* pOrigFont = rInf.GetFont();
881     SwFont* pHoleFont = NULL;
882     SwFontSave* pFontSave = NULL;
883     if( pOrigFont->GetUnderline() != UNDERLINE_NONE
884     ||  pOrigFont->GetOverline() != UNDERLINE_NONE
885     ||  pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
886     {
887         pHoleFont = new SwFont( *pOrigFont );
888         pHoleFont->SetUnderline( UNDERLINE_NONE );
889         pHoleFont->SetOverline( UNDERLINE_NONE );
890         pHoleFont->SetStrikeout( STRIKEOUT_NONE );
891         pFontSave = new SwFontSave( rInf, pHoleFont );
892     }
893 
894     const XubString aTxt( ' ' );
895     rInf.DrawText( aTxt, *this, 0, 1, false );
896 
897     delete pFontSave;
898     delete pHoleFont;
899 }
900 
901 /*************************************************************************
902  *                 virtual SwHolePortion::Format()
903  *************************************************************************/
904 
905 
906 
Format(SwTxtFormatInfo & rInf)907 sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf )
908 {
909     return rInf.IsFull() || rInf.X() >= rInf.Width();
910 }
911 
912 /*************************************************************************
913  *              virtual SwHolePortion::HandlePortion()
914  *************************************************************************/
915 
HandlePortion(SwPortionHandler & rPH) const916 void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
917 {
918     rPH.Text( GetLen(), GetWhichPor() );
919 }
920 
Paint(const SwTxtPaintInfo &) const921 void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const
922 {
923     // These shouldn't be painted!
924     //SwTxtPortion::Paint(rInf);
925 }
926 
Format(SwTxtFormatInfo &)927 sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & )
928 {
929     sal_Bool ret=0;
930     Width(0);
931     return ret;
932 }
933 
934 namespace {
getCurrentListIndex(IFieldmark * pBM,::rtl::OUString * io_pCurrentText=NULL)935     static sal_Int32 getCurrentListIndex( IFieldmark* pBM,
936             ::rtl::OUString* io_pCurrentText = NULL )
937     {
938         const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
939         sal_Int32 nCurrentIdx = 0;
940         const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_RESULT));
941         if(pResult != pParameters->end())
942             pResult->second >>= nCurrentIdx;
943         if(io_pCurrentText)
944         {
945             const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_LISTENTRY));
946             if(pListEntries != pParameters->end())
947             {
948                 uno::Sequence< ::rtl::OUString > vListEntries;
949                 pListEntries->second >>= vListEntries;
950                 if(nCurrentIdx < vListEntries.getLength())
951                     *io_pCurrentText = vListEntries[nCurrentIdx];
952             }
953         }
954         return nCurrentIdx;
955     }
956 }
957 
958 //FIXME Fieldbk
Paint(const SwTxtPaintInfo & rInf) const959 void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const
960 {
961     SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode());
962     const SwDoc *doc=pNd->GetDoc();
963     SwIndex aIndex( pNd, rInf.GetIdx() );
964     SwPosition aPosition(*pNd, aIndex);
965 
966     IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
967 
968     OSL_ENSURE( pBM,
969         "SwFieldFormPortion::Paint(..)"
970         " - Where is my form field bookmark???");
971 
972     if ( pBM != NULL )
973     {
974         if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
975         { // a checkbox...
976             ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
977             bool checked = pCheckboxFm->IsChecked();
978             rInf.DrawCheckBox(*this, checked);
979         }
980         else if ( pBM->GetFieldname( ).equalsAscii(  ODF_FORMDROPDOWN ) )
981         { // a list...
982             rtl::OUString aTxt;
983             rInf.DrawViewOpt( *this, POR_FLD );
984             rInf.DrawText( aTxt, *this, 0, 0/*aTxt.getLength()*/, false );
985         }
986         else
987         {
988             assert(0); // unknown type...
989         }
990     }
991 }
992 
Format(SwTxtFormatInfo & rInf)993 sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo & rInf )
994 {
995     sal_Bool ret = 0;
996     SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm(  )->GetTxtNode(  ) );
997     const SwDoc *doc = pNd->GetDoc(  );
998     SwIndex aIndex( pNd, rInf.GetIdx(  ) );
999     SwPosition aPosition( *pNd, aIndex );
1000     IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
1001     ASSERT( pBM != NULL, "Where is my form field bookmark???" );
1002     if ( pBM != NULL )
1003     {
1004         if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
1005         {
1006             Width( rInf.GetTxtHeight(  ) );
1007             Height( rInf.GetTxtHeight(  ) );
1008             SetAscent( rInf.GetAscent(  ) );
1009         }
1010         else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
1011         {
1012             ::rtl::OUString aTxt;
1013             getCurrentListIndex( pBM, &aTxt );
1014             SwPosSize aPosSize = rInf.GetTxtSize( aTxt );
1015             Width( aPosSize.Width(  ) );
1016             Height( aPosSize.Height(  ) );
1017             SetAscent( rInf.GetAscent(  ) );
1018         }
1019         else
1020         {
1021             assert( 0 );        // unknown type...
1022         }
1023     }
1024     return ret;
1025 }
1026 
1027 
1028