Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <ctype.h>
21 :
22 : #include <com/sun/star/i18n/ScriptType.hpp>
23 : #include <i18nlangtag/mslangid.hxx>
24 : #include <hintids.hxx>
25 : #include <EnhancedPDFExportHelper.hxx>
26 : #include <SwPortionHandler.hxx>
27 : #include <porlay.hxx>
28 : #include <inftxt.hxx>
29 : #include <guess.hxx>
30 : #include <porglue.hxx>
31 : #include <portab.hxx>
32 : #include <porfld.hxx>
33 : #include <wrong.hxx>
34 : #include <viewsh.hxx>
35 : #include <IDocumentSettingAccess.hxx>
36 : #include <viewopt.hxx>
37 : #include <editeng/borderline.hxx>
38 :
39 : #include <IMark.hxx>
40 : #include <pam.hxx>
41 : #include <doc.hxx>
42 : #include <xmloff/odffields.hxx>
43 :
44 : using namespace ::sw::mark;
45 : using namespace ::com::sun::star;
46 : using namespace ::com::sun::star::i18n::ScriptType;
47 :
48 : // Returns for how many characters an extra space has to be added
49 : // (for justified alignment).
50 9392 : static sal_Int32 lcl_AddSpace( const SwTxtSizeInfo &rInf, const OUString* pStr,
51 : const SwLinePortion& rPor )
52 : {
53 : sal_Int32 nPos, nEnd;
54 9392 : const SwScriptInfo* pSI = 0;
55 :
56 9392 : if ( pStr )
57 : {
58 : // passing a string means we are inside a field
59 0 : nPos = 0;
60 0 : nEnd = pStr->getLength();
61 : }
62 : else
63 : {
64 9392 : nPos = rInf.GetIdx();
65 9392 : nEnd = rInf.GetIdx() + rPor.GetLen();
66 9392 : pStr = &rInf.GetTxt();
67 9392 : pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo();
68 : }
69 :
70 9392 : sal_Int32 nCnt = 0;
71 9392 : sal_uInt8 nScript = 0;
72 :
73 : // If portion consists of Asian characters and language is not
74 : // Korean, we add extra space to each character.
75 : // first we get the script type
76 9392 : if ( pSI )
77 9392 : nScript = pSI->ScriptType( nPos );
78 0 : else if ( g_pBreakIt->GetBreakIter().is() )
79 0 : nScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos );
80 :
81 : // Note: rInf.GetIdx() can differ from nPos,
82 : // e.g., when rPor is a field portion. nPos referes to the string passed
83 : // to the function, rInf.GetIdx() referes to the original string.
84 :
85 : // We try to find out which justification mode is required. This is done by
86 : // evaluating the script type and the language attribute set for this portion
87 :
88 : // Asian Justification: Each character get some extra space
89 9392 : if ( nEnd > nPos && ASIAN == nScript )
90 : {
91 : LanguageType aLang =
92 0 : rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
93 :
94 0 : if (!MsLangId::isKorean(aLang))
95 : {
96 0 : const SwLinePortion* pPor = rPor.GetPortion();
97 0 : if ( pPor && ( pPor->IsKernPortion() ||
98 0 : pPor->IsControlCharPortion() ||
99 0 : pPor->IsPostItsPortion() ) )
100 0 : pPor = pPor->GetPortion();
101 :
102 0 : nCnt += nEnd - nPos;
103 :
104 0 : if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
105 0 : pPor->IsBreakPortion() )
106 0 : --nCnt;
107 :
108 0 : return nCnt;
109 : }
110 : }
111 :
112 : // Kashida Justification: Insert Kashidas
113 9392 : if ( nEnd > nPos && pSI && COMPLEX == nScript )
114 : {
115 6 : if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
116 : {
117 0 : const sal_Int32 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos );
118 : // i60591: need to check result of KashidaJustify
119 : // determine if kashida justification is applicable
120 0 : if (nKashRes != -1)
121 0 : return nKashRes;
122 : }
123 : }
124 :
125 : // Thai Justification: Each character cell gets some extra space
126 9392 : if ( nEnd > nPos && COMPLEX == nScript )
127 : {
128 : LanguageType aLang =
129 6 : rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
130 :
131 6 : if ( LANGUAGE_THAI == aLang )
132 : {
133 0 : nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos );
134 :
135 0 : const SwLinePortion* pPor = rPor.GetPortion();
136 0 : if ( pPor && ( pPor->IsKernPortion() ||
137 0 : pPor->IsControlCharPortion() ||
138 0 : pPor->IsPostItsPortion() ) )
139 0 : pPor = pPor->GetPortion();
140 :
141 0 : if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
142 0 : --nCnt;
143 :
144 0 : return nCnt;
145 : }
146 : }
147 :
148 : // Here starts the good old "Look for blanks and add space to them" part.
149 : // Note: We do not want to add space to an isolated latin blank in front
150 : // of some complex characters in RTL environment
151 : const bool bDoNotAddSpace =
152 9788 : LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
153 : ( i18n::ScriptType::COMPLEX ==
154 402 : pSI->ScriptType( nPos + 1 ) ) &&
155 9392 : rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft();
156 :
157 9392 : if ( bDoNotAddSpace )
158 0 : return nCnt;
159 :
160 9392 : sal_Int32 nTxtEnd = std::min(nEnd, pStr->getLength());
161 556014 : for ( ; nPos < nTxtEnd; ++nPos )
162 : {
163 546622 : if( CH_BLANK == (*pStr)[ nPos ] )
164 81552 : ++nCnt;
165 : }
166 :
167 : // We still have to examine the next character:
168 : // If the next character is ASIAN and not KOREAN we have
169 : // to add an extra space
170 : // nPos referes to the original string, even if a field string has
171 : // been passed to this function
172 9392 : nPos = rInf.GetIdx() + rPor.GetLen();
173 9392 : if ( nPos < rInf.GetTxt().getLength() )
174 : {
175 9328 : sal_uInt8 nNextScript = 0;
176 9328 : const SwLinePortion* pPor = rPor.GetPortion();
177 9328 : if ( pPor && pPor->IsKernPortion() )
178 0 : pPor = pPor->GetPortion();
179 :
180 9328 : if ( ! g_pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
181 234 : return nCnt;
182 :
183 : // next character is inside a field?
184 9094 : if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
185 : {
186 0 : bool bOldOnWin = rInf.OnWin();
187 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( false );
188 :
189 0 : OUString aStr;
190 0 : pPor->GetExpTxt( rInf, aStr );
191 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
192 :
193 0 : nNextScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
194 : }
195 : else
196 9094 : nNextScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos );
197 :
198 9094 : if( ASIAN == nNextScript )
199 : {
200 : LanguageType aLang =
201 0 : rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript );
202 :
203 0 : if (!MsLangId::isKorean(aLang))
204 0 : ++nCnt;
205 : }
206 : }
207 :
208 9158 : return nCnt;
209 : }
210 :
211 25080 : SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion )
212 25080 : : SwLinePortion( rPortion )
213 : {
214 25080 : SetWhichPor( POR_TXT );
215 25080 : }
216 :
217 5016 : void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess )
218 : {
219 : // The word/char is larger than the line
220 : // Special case 1: The word is larger than the line
221 : // We truncate ...
222 5016 : const sal_uInt16 nLineWidth = (sal_uInt16)(rInf.Width() - rInf.X());
223 5016 : sal_Int32 nLen = rGuess.CutPos() - rInf.GetIdx();
224 5016 : if( nLen )
225 : {
226 : // special case: guess does not always provide the correct
227 : // width, only in common cases.
228 3264 : if ( !rGuess.BreakWidth() )
229 : {
230 2476 : rInf.SetLen( nLen );
231 2476 : SetLen( nLen );
232 2476 : CalcTxtSize( rInf );
233 :
234 : // changing these values requires also changing them in
235 : // guess.cxx
236 2476 : sal_uInt16 nItalic = 0;
237 2476 : if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
238 : {
239 0 : nItalic = Height() / 12;
240 : }
241 2476 : Width( Width() + nItalic );
242 : }
243 : else
244 : {
245 788 : Width( rGuess.BreakWidth() );
246 788 : SetLen( nLen );
247 : }
248 : }
249 : // special case: first character does not fit to line
250 1752 : else if ( rGuess.CutPos() == rInf.GetLineStart() )
251 : {
252 1742 : SetLen( 1 );
253 1742 : Width( nLineWidth );
254 : }
255 : else
256 : {
257 10 : SetLen( 0 );
258 10 : Width( 0 );
259 : }
260 5016 : }
261 :
262 224 : void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf )
263 : {
264 224 : Truncate();
265 224 : Height( 0 );
266 224 : Width( 0 );
267 224 : SetLen( 0 );
268 224 : SetAscent( 0 );
269 224 : rInf.SetUnderflow( this );
270 224 : }
271 :
272 0 : static bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf )
273 : {
274 0 : OUString aTxt;
275 0 : return rFld.GetExpTxt( rInf, aTxt ) && !aTxt.isEmpty();
276 : }
277 :
278 161843 : bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf )
279 : {
280 : // 5744: If only the hypen does not fit anymore, we still need to wrap
281 : // the word, or else return true!
282 161843 : if( rInf.IsUnderflow() && rInf.GetSoftHyphPos() )
283 : {
284 : // soft hyphen portion has triggered an underflow event because
285 : // of an alternative spelling position
286 0 : bool bFull = false;
287 0 : const bool bHyph = rInf.ChgHyph( true );
288 0 : if( rInf.IsHyphenate() )
289 : {
290 0 : SwTxtGuess aGuess;
291 : // check for alternative spelling left from the soft hyphen
292 : // this should usually be true but
293 0 : aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
294 0 : bFull = CreateHyphen( rInf, aGuess );
295 0 : OSL_ENSURE( bFull, "Problem with hyphenation!!!" );
296 : }
297 0 : rInf.ChgHyph( bHyph );
298 0 : rInf.SetSoftHyphPos( 0 );
299 0 : return bFull;
300 : }
301 :
302 161843 : SwTxtGuess aGuess;
303 161843 : const bool bFull = !aGuess.Guess( *this, rInf, Height() );
304 :
305 : // these are the possible cases:
306 : // A Portion fits to current line
307 : // B Portion does not fit to current line but a possible line break
308 : // within the portion has been found by the break iterator, 2 subcases
309 : // B1 break is hyphen
310 : // B2 break is word end
311 : // C Portion does not fit to current line and no possible line break
312 : // has been found by break iterator, 2 subcases:
313 : // C1 break iterator found a possible line break in portion before us
314 : // ==> this break is used (underflow)
315 : // C2 break iterator does not found a possible line break at all:
316 : // ==> line break
317 :
318 : // case A: line not yet full
319 161843 : if ( !bFull )
320 : {
321 105395 : Width( aGuess.BreakWidth() );
322 : // Vorsicht !
323 105395 : if( !InExpGrp() || InFldGrp() )
324 104027 : SetLen( rInf.GetLen() );
325 :
326 105395 : short nKern = rInf.GetFont()->CheckKerning();
327 105395 : if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
328 : {
329 0 : nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
330 0 : if( nKern < 0 )
331 0 : nKern = 0;
332 : }
333 105395 : if( nKern )
334 656 : new SwKernPortion( *this, nKern );
335 : }
336 : // special case: hanging portion
337 56448 : else if( bFull && aGuess.GetHangingPortion() )
338 : {
339 0 : Width( aGuess.BreakWidth() );
340 0 : SetLen( aGuess.BreakPos() - rInf.GetIdx() );
341 0 : Insert( aGuess.GetHangingPortion() );
342 0 : aGuess.GetHangingPortion()->SetAscent( GetAscent() );
343 0 : aGuess.ClearHangingPortion();
344 : }
345 : // breakPos >= index
346 56448 : else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != COMPLETE_STRING )
347 : {
348 : // case B1
349 162192 : if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
350 162192 : && ( aGuess.BreakPos() > rInf.GetIdx() ||
351 0 : ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
352 : {
353 0 : CreateHyphen( rInf, aGuess );
354 0 : if ( rInf.GetFly() )
355 0 : rInf.GetRoot()->SetMidHyph( true );
356 : else
357 0 : rInf.GetRoot()->SetEndHyph( true );
358 : }
359 : // case C1
360 : // - Footnote portions with fake line start (i.e., not at beginning of line)
361 : // should keep together with the text portion. (Note: no keep together
362 : // with only footnote portions.
363 : // - TabPortions not at beginning of line should keep together with the
364 : // text portion, if they are not followed by a blank
365 : // (work around different definition of tab stop character - breaking or
366 : // non breaking character - in compatibility mode)
367 108128 : else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() &&
368 :
369 108134 : rInf.IsOtherThanFtnInside() ) ||
370 108128 : ( rInf.GetLast() &&
371 108128 : rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) &&
372 54090 : rInf.GetLast()->InTabGrp() &&
373 40 : rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
374 20 : aGuess.BreakPos() == rInf.GetIdx() &&
375 12 : CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
376 6 : 0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
377 6 : BreakUnderflow( rInf );
378 : // case B2
379 155238 : else if( rInf.GetIdx() > rInf.GetLineStart() ||
380 50132 : aGuess.BreakPos() > rInf.GetIdx() ||
381 : // this is weird: during formatting the follow of a field
382 : // the values rInf.GetIdx and rInf.GetLineStart are replaced
383 : // IsFakeLineStart indicates GetIdx > GetLineStart
384 6020 : rInf.IsFakeLineStart() ||
385 6020 : rInf.GetFly() ||
386 111126 : rInf.IsFirstMulti() ||
387 5700 : ( rInf.GetLast() &&
388 5700 : ( rInf.GetLast()->IsFlyPortion() ||
389 2850 : ( rInf.GetLast()->InFldGrp() &&
390 0 : ! rInf.GetLast()->InNumberGrp() &&
391 0 : ! rInf.GetLast()->IsErgoSumPortion() &&
392 0 : lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) )
393 : {
394 51208 : if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
395 51208 : Width( aGuess.BreakWidth() );
396 : else
397 : // this actually should not happen
398 0 : Width( sal_uInt16(rInf.Width() - rInf.X()) );
399 :
400 51208 : SetLen( aGuess.BreakPos() - rInf.GetIdx() );
401 :
402 : OSL_ENSURE( aGuess.BreakStart() >= aGuess.FieldDiff(),
403 : "Trouble with expanded field portions during line break" );
404 51208 : const sal_Int32 nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
405 51208 : if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
406 : {
407 7025 : SwHolePortion *pNew = new SwHolePortion( *this );
408 7025 : pNew->SetLen( nRealStart - aGuess.BreakPos() );
409 7025 : Insert( pNew );
410 : }
411 : }
412 : else // case C2, last exit
413 2850 : BreakCut( rInf, aGuess );
414 : }
415 : // breakPos < index or no breakpos at all
416 : else
417 : {
418 2384 : bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
419 5024 : if( aGuess.BreakPos() != COMPLETE_STRING &&
420 474 : aGuess.BreakPos() != rInf.GetLineStart() &&
421 218 : ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
422 2820 : rInf.IsFirstMulti() ) &&
423 248 : ( !rInf.GetLast()->IsBlankPortion() || ((SwBlankPortion*)
424 30 : rInf.GetLast())->MayUnderflow( rInf, rInf.GetIdx()-1, true )))
425 : { // case C1 (former BreakUnderflow())
426 218 : BreakUnderflow( rInf );
427 : }
428 : else
429 : // case C2, last exit
430 2166 : BreakCut( rInf, aGuess );
431 : }
432 :
433 161843 : return bFull;
434 : }
435 :
436 161843 : bool SwTxtPortion::Format( SwTxtFormatInfo &rInf )
437 : {
438 161843 : if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
439 : {
440 0 : Height( 0 );
441 0 : Width( 0 );
442 0 : SetLen( 0 );
443 0 : SetAscent( 0 );
444 0 : SetPortion( NULL ); // ????
445 0 : return true;
446 : }
447 :
448 : OSL_ENSURE( rInf.RealWidth() || (rInf.X() == rInf.Width()),
449 : "SwTxtPortion::Format: missing real width" );
450 : OSL_ENSURE( Height(), "SwTxtPortion::Format: missing height" );
451 :
452 161843 : return _Format( rInf );
453 : }
454 :
455 : // Format end of line
456 : // 5083: We can have awkward cases e.g.:
457 : // "from {Santa}"
458 : // Santa wraps, "from " turns into "from" and " " in a justified
459 : // paragraph, in which the glue gets expanded instead of merged
460 : // with the MarginPortion.
461 :
462 : // rInf.nIdx points to the next word, nIdx-1 is the portion's last char
463 1340 : void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf )
464 : {
465 3704 : if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
466 2540 : !GetPortion()->GetPortion() ) ) && GetLen() &&
467 968 : rInf.GetIdx() < rInf.GetTxt().getLength() &&
468 536 : 1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
469 1574 : && !rInf.GetLast()->IsHolePortion() )
470 : {
471 : // calculate number of blanks
472 234 : sal_Int32 nX = rInf.GetIdx() - 1;
473 234 : sal_Int32 nHoleLen = 1;
474 468 : while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
475 0 : nHoleLen++;
476 :
477 : // First set ourselves and the insert, because there could be
478 : // a SwLineLayout
479 : sal_uInt16 nBlankSize;
480 234 : if( nHoleLen == GetLen() )
481 2 : nBlankSize = Width();
482 : else
483 232 : nBlankSize = nHoleLen * rInf.GetTxtSize(OUString(' ')).Width();
484 234 : Width( Width() - nBlankSize );
485 234 : rInf.X( rInf.X() - nBlankSize );
486 234 : SetLen( GetLen() - nHoleLen );
487 234 : SwLinePortion *pHole = new SwHolePortion( *this );
488 234 : ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize );
489 234 : ( (SwHolePortion *)pHole )->SetLen( nHoleLen );
490 234 : Insert( pHole );
491 : }
492 1340 : }
493 :
494 0 : sal_Int32 SwTxtPortion::GetCrsrOfst( const sal_uInt16 nOfst ) const
495 : {
496 : OSL_ENSURE( false, "SwTxtPortion::GetCrsrOfst: don't use this method!" );
497 0 : return SwLinePortion::GetCrsrOfst( nOfst );
498 : }
499 :
500 : // The GetTxtSize() assumes that the own length is correct
501 6383 : SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
502 : {
503 6383 : SwPosSize aSize = rInf.GetTxtSize();
504 6383 : if( !GetJoinBorderWithPrev() )
505 6383 : aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace() );
506 6383 : if( !GetJoinBorderWithNext() )
507 6383 : aSize.Width(aSize.Width() + rInf.GetFont()->GetRightBorderSpace() );
508 :
509 6383 : aSize.Height(aSize.Height() +
510 6383 : rInf.GetFont()->GetTopBorderSpace() +
511 6383 : rInf.GetFont()->GetBottomBorderSpace() );
512 :
513 6383 : return aSize;
514 : }
515 :
516 43422 : void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const
517 : {
518 43422 : if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt()[rInf.GetIdx()])
519 : {
520 : assert(false); // this is some debugging only code
521 0 : rInf.DrawBackBrush( *this );
522 0 : const OUString aTxt(CH_TXT_ATR_SUBST_FIELDEND);
523 0 : rInf.DrawText( aTxt, *this, 0, aTxt.getLength(), false );
524 : }
525 43422 : else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt()[rInf.GetIdx()])
526 : {
527 : assert(false); // this is some debugging only code
528 0 : rInf.DrawBackBrush( *this );
529 0 : const OUString aTxt(CH_TXT_ATR_SUBST_FIELDSTART);
530 0 : rInf.DrawText( aTxt, *this, 0, aTxt.getLength(), false );
531 : }
532 43422 : else if( GetLen() )
533 : {
534 40937 : rInf.DrawBackBrush( *this );
535 40937 : rInf.DrawBorder( *this );
536 :
537 : // do we have to repaint a post it portion?
538 40937 : if( rInf.OnWin() && pPortion && !pPortion->Width() )
539 3074 : pPortion->PrePaint( rInf, this );
540 :
541 40937 : const SwWrongList *pWrongList = rInf.GetpWrongList();
542 40937 : const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
543 40937 : const SwWrongList *pSmarttags = rInf.GetSmartTags();
544 :
545 40937 : const bool bWrong = 0 != pWrongList;
546 40937 : const bool bGrammarCheck = 0 != pGrammarCheckList;
547 40937 : const bool bSmartTags = 0 != pSmarttags;
548 :
549 40937 : if ( bWrong || bSmartTags || bGrammarCheck )
550 7838 : rInf.DrawMarkedText( *this, rInf.GetLen(), false, bWrong, bSmartTags, bGrammarCheck );
551 : else
552 33099 : rInf.DrawText( *this, rInf.GetLen(), false );
553 : }
554 43422 : }
555 :
556 4 : bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, OUString & ) const
557 : {
558 4 : return false;
559 : }
560 :
561 : // Responsible for the justified paragraph. They calculate the blank
562 : // count and the resulting added space.
563 2780 : sal_Int32 SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf,
564 : sal_Int32& rCharCnt ) const
565 : {
566 2780 : sal_Int32 nCnt = 0;
567 2780 : sal_Int32 nPos = 0;
568 2780 : if ( InExpGrp() )
569 : {
570 20 : if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
571 : {
572 : // OnWin() likes to return a blank instead of an empty string from
573 : // time to time. We cannot use that here at all, however.
574 0 : bool bOldOnWin = rInf.OnWin();
575 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( false );
576 :
577 0 : OUString aStr;
578 0 : GetExpTxt( rInf, aStr );
579 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
580 :
581 0 : nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
582 0 : nPos = aStr.getLength();
583 : }
584 : }
585 2760 : else if( !IsDropPortion() )
586 : {
587 2760 : nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
588 2760 : nPos = GetLen();
589 : }
590 2780 : rCharCnt = rCharCnt + nPos;
591 2780 : return nCnt;
592 : }
593 :
594 6662 : long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const
595 : {
596 6662 : sal_Int32 nCnt = 0;
597 :
598 6662 : if ( InExpGrp() )
599 : {
600 30 : if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
601 : {
602 : // OnWin() likes to return a blank instead of an empty string from
603 : // time to time. We cannot use that here at all, however.
604 0 : bool bOldOnWin = rInf.OnWin();
605 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( false );
606 :
607 0 : OUString aStr;
608 0 : GetExpTxt( rInf, aStr );
609 0 : ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
610 0 : if( nSpaceAdd > 0 )
611 0 : nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
612 : else
613 : {
614 0 : nSpaceAdd = -nSpaceAdd;
615 0 : nCnt = aStr.getLength();
616 0 : }
617 : }
618 : }
619 6632 : else if( !IsDropPortion() )
620 : {
621 6632 : if( nSpaceAdd > 0 )
622 6632 : nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
623 : else
624 : {
625 0 : nSpaceAdd = -nSpaceAdd;
626 0 : nCnt = GetLen();
627 0 : SwLinePortion* pPor = GetPortion();
628 :
629 : // we do not want an extra space in front of margin portions
630 0 : if ( nCnt )
631 : {
632 0 : while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
633 0 : pPor = pPor->GetPortion();
634 :
635 0 : if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
636 0 : --nCnt;
637 : }
638 : }
639 : }
640 :
641 6662 : return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
642 : }
643 :
644 1082 : void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const
645 : {
646 1082 : rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
647 1082 : }
648 :
649 32 : SwTxtInputFldPortion::SwTxtInputFldPortion()
650 : : SwTxtPortion()
651 : , mbContainsInputFieldStart( false )
652 32 : , mbContainsInputFieldEnd( false )
653 : {
654 32 : SetWhichPor( POR_INPUTFLD );
655 32 : }
656 :
657 32 : bool SwTxtInputFldPortion::Format( SwTxtFormatInfo &rInf )
658 : {
659 : mbContainsInputFieldStart =
660 32 : rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART;
661 : mbContainsInputFieldEnd =
662 32 : rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND;
663 :
664 32 : bool bRet = false;
665 64 : if ( rInf.GetLen() == 1
666 32 : && ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) )
667 : {
668 0 : Width( 0 );
669 : }
670 : else
671 : {
672 32 : SwTxtSlot aFormatTxt( &rInf, this, true, true );
673 32 : if ( rInf.GetLen() == 0 )
674 : {
675 6 : Width( 0 );
676 : }
677 : else
678 : {
679 26 : const sal_Int32 nFormerLineStart = rInf.GetLineStart();
680 26 : if ( !mbContainsInputFieldStart )
681 : {
682 0 : rInf.SetLineStart( 0 );
683 : }
684 :
685 26 : bRet = SwTxtPortion::Format( rInf );
686 :
687 26 : if ( mbContainsInputFieldEnd )
688 : {
689 : // adjust portion length accordingly, if complete text fits into the portion
690 26 : if ( GetLen() == rInf.GetLen() )
691 : {
692 26 : SetLen( GetLen() + 1 );
693 : }
694 : }
695 :
696 26 : if ( mbContainsInputFieldStart )
697 : {
698 : // adjust portion length accordingly
699 26 : SetLen( GetLen() + 1 );
700 : }
701 : else
702 : {
703 0 : rInf.SetLineStart( nFormerLineStart );
704 : }
705 32 : }
706 : }
707 :
708 32 : return bRet;
709 : }
710 :
711 28 : void SwTxtInputFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
712 : {
713 28 : if ( Width() )
714 : {
715 24 : rInf.DrawViewOpt( *this, POR_INPUTFLD );
716 : SwTxtSlot aPaintTxt( &rInf, this, true, true,
717 24 : ContainsOnlyDummyChars() ? OUString(" ") : OUString() );
718 24 : SwTxtPortion::Paint( rInf );
719 : }
720 28 : }
721 :
722 52 : bool SwTxtInputFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, OUString &rTxt ) const
723 : {
724 52 : sal_Int32 nIdx = rInf.GetIdx();
725 52 : sal_Int32 nLen = rInf.GetLen();
726 52 : if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
727 : {
728 52 : ++nIdx;
729 52 : --nLen;
730 : }
731 52 : if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND )
732 : {
733 52 : --nLen;
734 : }
735 52 : rTxt = rInf.GetTxt().copy( nIdx, std::min( nLen, rInf.GetTxt().getLength() - nIdx ) );
736 :
737 52 : return true;
738 : }
739 :
740 0 : SwPosSize SwTxtInputFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
741 : {
742 0 : SwTxtSlot aFormatTxt( &rInf, this, true, false );
743 0 : if ( rInf.GetLen() == 0 )
744 : {
745 0 : return SwPosSize( 0, 0 );
746 : }
747 :
748 0 : return rInf.GetTxtSize();
749 : }
750 :
751 4 : sal_uInt16 SwTxtInputFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
752 : {
753 8 : if( !Width()
754 4 : && ContainsOnlyDummyChars()
755 4 : && !rInf.GetOpt().IsPagePreview()
756 4 : && !rInf.GetOpt().IsReadonly()
757 8 : && SwViewOption::IsFieldShadings() )
758 : {
759 4 : return rInf.GetTxtSize( " " ).Width();
760 : }
761 :
762 0 : return SwTxtPortion::GetViewWidth( rInf );
763 : }
764 :
765 28 : bool SwTxtInputFldPortion::ContainsOnlyDummyChars() const
766 : {
767 28 : return GetLen() <= 2
768 8 : && mbContainsInputFieldStart
769 36 : && mbContainsInputFieldEnd;
770 : }
771 :
772 7259 : SwHolePortion::SwHolePortion( const SwTxtPortion &rPor )
773 7259 : : nBlankWidth( 0 )
774 : {
775 7259 : SetLen( 1 );
776 7259 : Height( rPor.Height() );
777 7259 : SetAscent( rPor.GetAscent() );
778 7259 : SetWhichPor( POR_HOLE );
779 7259 : }
780 :
781 7345 : SwLinePortion *SwHolePortion::Compress() { return this; }
782 :
783 2216 : void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const
784 : {
785 2216 : if( !rInf.GetOut() )
786 2216 : return;
787 :
788 : // #i16816# export stuff only needed for tagged pdf support
789 2216 : if (!SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut()) )
790 2216 : return;
791 :
792 : // #i68503# the hole must have no decoration for a consistent visual appearance
793 0 : const SwFont* pOrigFont = rInf.GetFont();
794 0 : SwFont* pHoleFont = NULL;
795 0 : SwFontSave* pFontSave = NULL;
796 0 : if( pOrigFont->GetUnderline() != UNDERLINE_NONE
797 0 : || pOrigFont->GetOverline() != UNDERLINE_NONE
798 0 : || pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
799 : {
800 0 : pHoleFont = new SwFont( *pOrigFont );
801 0 : pHoleFont->SetUnderline( UNDERLINE_NONE );
802 0 : pHoleFont->SetOverline( UNDERLINE_NONE );
803 0 : pHoleFont->SetStrikeout( STRIKEOUT_NONE );
804 0 : pFontSave = new SwFontSave( rInf, pHoleFont );
805 : }
806 :
807 0 : const OUString aTxt( ' ' );
808 0 : rInf.DrawText( aTxt, *this, 0, 1, false );
809 :
810 0 : delete pFontSave;
811 0 : delete pHoleFont;
812 : }
813 :
814 0 : bool SwHolePortion::Format( SwTxtFormatInfo &rInf )
815 : {
816 0 : return rInf.IsFull() || rInf.X() >= rInf.Width();
817 : }
818 :
819 6 : void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
820 : {
821 6 : rPH.Text( GetLen(), GetWhichPor() );
822 6 : }
823 :
824 28 : void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const
825 : {
826 : // These shouldn't be painted!
827 : //SwTxtPortion::Paint(rInf);
828 28 : }
829 :
830 412 : bool SwFieldMarkPortion::Format( SwTxtFormatInfo & )
831 : {
832 412 : Width(0);
833 412 : return false;
834 : }
835 :
836 4 : void SwFieldFormCheckboxPortion::Paint( const SwTxtPaintInfo& rInf ) const
837 : {
838 4 : SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode());
839 4 : const SwDoc *doc=pNd->GetDoc();
840 4 : SwIndex aIndex( pNd, rInf.GetIdx() );
841 8 : SwPosition aPosition(*pNd, aIndex);
842 :
843 4 : IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
844 :
845 : OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX,
846 : "Where is my form field bookmark???");
847 :
848 4 : if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
849 : {
850 4 : const ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
851 4 : bool bChecked = pCheckboxFm && pCheckboxFm->IsChecked();
852 4 : rInf.DrawCheckBox(*this, bChecked);
853 4 : }
854 4 : }
855 :
856 18 : bool SwFieldFormCheckboxPortion::Format( SwTxtFormatInfo & rInf )
857 : {
858 18 : SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm( )->GetTxtNode( ) );
859 18 : const SwDoc *doc = pNd->GetDoc( );
860 18 : SwIndex aIndex( pNd, rInf.GetIdx( ) );
861 36 : SwPosition aPosition( *pNd, aIndex );
862 18 : IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
863 : OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX, "Where is my form field bookmark???");
864 18 : if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
865 : {
866 18 : Width( rInf.GetTxtHeight( ) );
867 18 : Height( rInf.GetTxtHeight( ) );
868 18 : SetAscent( rInf.GetAscent( ) );
869 : }
870 36 : return false;
871 270 : }
872 :
873 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|