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 5627 : static sal_Int32 lcl_AddSpace( const SwTextSizeInfo &rInf, const OUString* pStr,
51 : const SwLinePortion& rPor )
52 : {
53 : sal_Int32 nPos, nEnd;
54 5627 : const SwScriptInfo* pSI = 0;
55 :
56 5627 : 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 5627 : nPos = rInf.GetIdx();
65 5627 : nEnd = rInf.GetIdx() + rPor.GetLen();
66 5627 : pStr = &rInf.GetText();
67 5627 : pSI = &const_cast<SwParaPortion*>(rInf.GetParaPortion())->GetScriptInfo();
68 : }
69 :
70 5627 : sal_Int32 nCnt = 0;
71 5627 : 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 5627 : if ( pSI )
77 5627 : 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 5627 : if ( nEnd > nPos && ASIAN == nScript )
90 : {
91 : LanguageType aLang =
92 0 : rInf.GetTextFrm()->GetTextNode()->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 5627 : if ( nEnd > nPos && pSI && COMPLEX == nScript )
114 : {
115 3 : 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 5627 : if ( nEnd > nPos && COMPLEX == nScript )
127 : {
128 : LanguageType aLang =
129 3 : rInf.GetTextFrm()->GetTextNode()->GetLang( rInf.GetIdx(), 1, nScript );
130 :
131 3 : 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 5825 : LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
153 : ( i18n::ScriptType::COMPLEX ==
154 201 : pSI->ScriptType( nPos + 1 ) ) &&
155 5627 : rInf.GetTextFrm() && rInf.GetTextFrm()->IsRightToLeft();
156 :
157 5627 : if ( bDoNotAddSpace )
158 0 : return nCnt;
159 :
160 5627 : sal_Int32 nTextEnd = std::min(nEnd, pStr->getLength());
161 358579 : for ( ; nPos < nTextEnd; ++nPos )
162 : {
163 352952 : if( CH_BLANK == (*pStr)[ nPos ] )
164 52416 : ++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 5627 : nPos = rInf.GetIdx() + rPor.GetLen();
173 5627 : if ( nPos < rInf.GetText().getLength() )
174 : {
175 5605 : sal_uInt8 nNextScript = 0;
176 5605 : const SwLinePortion* pPor = rPor.GetPortion();
177 5605 : if ( pPor && pPor->IsKernPortion() )
178 0 : pPor = pPor->GetPortion();
179 :
180 5605 : if ( ! g_pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
181 121 : return nCnt;
182 :
183 : // next character is inside a field?
184 5484 : if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
185 : {
186 0 : bool bOldOnWin = rInf.OnWin();
187 0 : ((SwTextSizeInfo &)rInf).SetOnWin( false );
188 :
189 0 : OUString aStr;
190 0 : pPor->GetExpText( rInf, aStr );
191 0 : ((SwTextSizeInfo &)rInf).SetOnWin( bOldOnWin );
192 :
193 0 : nNextScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
194 : }
195 : else
196 5484 : nNextScript = (sal_uInt8)g_pBreakIt->GetBreakIter()->getScriptType( rInf.GetText(), nPos );
197 :
198 5484 : if( ASIAN == nNextScript )
199 : {
200 : LanguageType aLang =
201 0 : rInf.GetTextFrm()->GetTextNode()->GetLang( nPos, 1, nNextScript );
202 :
203 0 : if (!MsLangId::isKorean(aLang))
204 0 : ++nCnt;
205 : }
206 : }
207 :
208 5506 : return nCnt;
209 : }
210 :
211 13647 : SwTextPortion * SwTextPortion::CopyLinePortion(const SwLinePortion &rPortion)
212 : {
213 13647 : SwTextPortion *const pNew(new SwTextPortion);
214 13647 : static_cast<SwLinePortion&>(*pNew) = rPortion;
215 13647 : pNew->SetWhichPor( POR_TXT ); // overwrite that!
216 13647 : return pNew;
217 : }
218 :
219 2688 : void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess )
220 : {
221 : // The word/char is larger than the line
222 : // Special case 1: The word is larger than the line
223 : // We truncate ...
224 2688 : const sal_uInt16 nLineWidth = (sal_uInt16)(rInf.Width() - rInf.X());
225 2688 : sal_Int32 nLen = rGuess.CutPos() - rInf.GetIdx();
226 2688 : if (nLen > 0)
227 : {
228 : // special case: guess does not always provide the correct
229 : // width, only in common cases.
230 1970 : if ( !rGuess.BreakWidth() )
231 : {
232 1626 : rInf.SetLen( nLen );
233 1626 : SetLen( nLen );
234 1626 : CalcTextSize( rInf );
235 :
236 : // changing these values requires also changing them in
237 : // guess.cxx
238 1626 : sal_uInt16 nItalic = 0;
239 1626 : if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
240 : {
241 0 : nItalic = Height() / 12;
242 : }
243 1626 : Width( Width() + nItalic );
244 : }
245 : else
246 : {
247 344 : Width( rGuess.BreakWidth() );
248 344 : SetLen( nLen );
249 : }
250 : }
251 : // special case: first character does not fit to line
252 718 : else if ( rGuess.CutPos() == rInf.GetLineStart() )
253 : {
254 706 : SetLen( 1 );
255 706 : Width( nLineWidth );
256 : }
257 : else
258 : {
259 12 : SetLen( 0 );
260 12 : Width( 0 );
261 : }
262 2688 : }
263 :
264 127 : void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
265 : {
266 127 : Truncate();
267 127 : Height( 0 );
268 127 : Width( 0 );
269 127 : SetLen( 0 );
270 127 : SetAscent( 0 );
271 127 : rInf.SetUnderflow( this );
272 127 : }
273 :
274 0 : static bool lcl_HasContent( const SwFieldPortion& rField, SwTextFormatInfo &rInf )
275 : {
276 0 : OUString aText;
277 0 : return rField.GetExpText( rInf, aText ) && !aText.isEmpty();
278 : }
279 :
280 102021 : bool SwTextPortion::_Format( SwTextFormatInfo &rInf )
281 : {
282 : // 5744: If only the hypen does not fit anymore, we still need to wrap
283 : // the word, or else return true!
284 102021 : if( rInf.IsUnderflow() && rInf.GetSoftHyphPos() )
285 : {
286 : // soft hyphen portion has triggered an underflow event because
287 : // of an alternative spelling position
288 0 : bool bFull = false;
289 0 : const bool bHyph = rInf.ChgHyph( true );
290 0 : if( rInf.IsHyphenate() )
291 : {
292 0 : SwTextGuess aGuess;
293 : // check for alternative spelling left from the soft hyphen
294 : // this should usually be true but
295 0 : aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
296 0 : bFull = CreateHyphen( rInf, aGuess );
297 0 : OSL_ENSURE( bFull, "Problem with hyphenation!!!" );
298 : }
299 0 : rInf.ChgHyph( bHyph );
300 0 : rInf.SetSoftHyphPos( 0 );
301 0 : return bFull;
302 : }
303 :
304 102021 : SwTextGuess aGuess;
305 102021 : const bool bFull = !aGuess.Guess( *this, rInf, Height() );
306 :
307 : // these are the possible cases:
308 : // A Portion fits to current line
309 : // B Portion does not fit to current line but a possible line break
310 : // within the portion has been found by the break iterator, 2 subcases
311 : // B1 break is hyphen
312 : // B2 break is word end
313 : // C Portion does not fit to current line and no possible line break
314 : // has been found by break iterator, 2 subcases:
315 : // C1 break iterator found a possible line break in portion before us
316 : // ==> this break is used (underflow)
317 : // C2 break iterator does not found a possible line break at all:
318 : // ==> line break
319 :
320 : // case A: line not yet full
321 102021 : if ( !bFull )
322 : {
323 66817 : Width( aGuess.BreakWidth() );
324 : // Vorsicht !
325 66817 : if( !InExpGrp() || InFieldGrp() )
326 66067 : SetLen( rInf.GetLen() );
327 :
328 66817 : short nKern = rInf.GetFont()->CheckKerning();
329 66817 : if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
330 : {
331 0 : nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
332 0 : if( nKern < 0 )
333 0 : nKern = 0;
334 : }
335 66817 : if( nKern )
336 297 : new SwKernPortion( *this, nKern );
337 : }
338 : // special case: hanging portion
339 35204 : else if( bFull && aGuess.GetHangingPortion() )
340 : {
341 0 : Width( aGuess.BreakWidth() );
342 0 : SetLen( aGuess.BreakPos() - rInf.GetIdx() );
343 0 : Insert( aGuess.GetHangingPortion() );
344 0 : aGuess.GetHangingPortion()->SetAscent( GetAscent() );
345 0 : aGuess.ClearHangingPortion();
346 : }
347 : // breakPos >= index
348 35204 : else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != COMPLETE_STRING )
349 : {
350 : // case B1
351 102606 : if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
352 102606 : && ( aGuess.BreakPos() > rInf.GetIdx() ||
353 0 : ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
354 : {
355 0 : CreateHyphen( rInf, aGuess );
356 0 : if ( rInf.GetFly() )
357 0 : rInf.GetRoot()->SetMidHyph( true );
358 : else
359 0 : rInf.GetRoot()->SetEndHyph( true );
360 : }
361 : // case C1
362 : // - Footnote portions with fake line start (i.e., not at beginning of line)
363 : // should keep together with the text portion. (Note: no keep together
364 : // with only footnote portions.
365 : // - TabPortions not at beginning of line should keep together with the
366 : // text portion, if they are not followed by a blank
367 : // (work around different definition of tab stop character - breaking or
368 : // non breaking character - in compatibility mode)
369 68404 : else if ( ( IsFootnotePortion() && rInf.IsFakeLineStart() &&
370 :
371 68413 : rInf.IsOtherThanFootnoteInside() ) ||
372 68404 : ( rInf.GetLast() &&
373 68180 : rInf.GetTextFrm()->GetTextNode()->getIDocumentSettingAccess()->get(DocumentSettingId::TAB_COMPAT) &&
374 34021 : rInf.GetLast()->InTabGrp() &&
375 75 : rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
376 41 : aGuess.BreakPos() == rInf.GetIdx() &&
377 18 : CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
378 9 : 0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
379 9 : BreakUnderflow( rInf );
380 : // case B2
381 99033 : else if( rInf.GetIdx() > rInf.GetLineStart() ||
382 32495 : aGuess.BreakPos() > rInf.GetIdx() ||
383 : // this is weird: during formatting the follow of a field
384 : // the values rInf.GetIdx and rInf.GetLineStart are replaced
385 : // IsFakeLineStart indicates GetIdx > GetLineStart
386 3696 : rInf.IsFakeLineStart() ||
387 3692 : rInf.GetFly() ||
388 70230 : rInf.IsFirstMulti() ||
389 3608 : ( rInf.GetLast() &&
390 3608 : ( rInf.GetLast()->IsFlyPortion() ||
391 1804 : ( rInf.GetLast()->InFieldGrp() &&
392 0 : ! rInf.GetLast()->InNumberGrp() &&
393 0 : ! rInf.GetLast()->IsErgoSumPortion() &&
394 0 : lcl_HasContent(*static_cast<SwFieldPortion*>(rInf.GetLast()),rInf ) ) ) ) )
395 : {
396 32389 : if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
397 32389 : Width( aGuess.BreakWidth() );
398 : else
399 : // this actually should not happen
400 0 : Width( sal_uInt16(rInf.Width() - rInf.X()) );
401 :
402 32389 : SetLen( aGuess.BreakPos() - rInf.GetIdx() );
403 :
404 : OSL_ENSURE( aGuess.BreakStart() >= aGuess.FieldDiff(),
405 : "Trouble with expanded field portions during line break" );
406 32389 : const sal_Int32 nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
407 32389 : if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
408 : {
409 3696 : SwHolePortion *pNew = new SwHolePortion( *this );
410 3696 : pNew->SetLen( nRealStart - aGuess.BreakPos() );
411 3696 : Insert( pNew );
412 : }
413 : }
414 : else // case C2, last exit
415 1804 : BreakCut( rInf, aGuess );
416 : }
417 : // breakPos < index or no breakpos at all
418 : else
419 : {
420 1002 : bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
421 2148 : if( aGuess.BreakPos() != COMPLETE_STRING &&
422 262 : aGuess.BreakPos() != rInf.GetLineStart() &&
423 118 : ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
424 1238 : rInf.IsFirstMulti() ) &&
425 133 : ( !rInf.GetLast()->IsBlankPortion() || SwBlankPortion::MayUnderflow( rInf, rInf.GetIdx()-1, true )))
426 : { // case C1 (former BreakUnderflow())
427 118 : BreakUnderflow( rInf );
428 : }
429 : else
430 : // case C2, last exit
431 884 : BreakCut( rInf, aGuess );
432 : }
433 :
434 102021 : return bFull;
435 : }
436 :
437 102021 : bool SwTextPortion::Format( SwTextFormatInfo &rInf )
438 : {
439 102021 : if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
440 : {
441 0 : Height( 0 );
442 0 : Width( 0 );
443 0 : SetLen( 0 );
444 0 : SetAscent( 0 );
445 0 : SetPortion( NULL ); // ????
446 0 : return true;
447 : }
448 :
449 : OSL_ENSURE( rInf.RealWidth() || (rInf.X() == rInf.Width()),
450 : "SwTextPortion::Format: missing real width" );
451 : OSL_ENSURE( Height(), "SwTextPortion::Format: missing height" );
452 :
453 102021 : return _Format( rInf );
454 : }
455 :
456 : // Format end of line
457 : // 5083: We can have awkward cases e.g.:
458 : // "from {Santa}"
459 : // Santa wraps, "from " turns into "from" and " " in a justified
460 : // paragraph, in which the glue gets expanded instead of merged
461 : // with the MarginPortion.
462 :
463 : // rInf.nIdx points to the next word, nIdx-1 is the portion's last char
464 695 : void SwTextPortion::FormatEOL( SwTextFormatInfo &rInf )
465 : {
466 1834 : if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
467 1208 : !GetPortion()->GetPortion() ) ) && GetLen() &&
468 395 : rInf.GetIdx() < rInf.GetText().getLength() &&
469 196 : 1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
470 779 : && !rInf.GetLast()->IsHolePortion() )
471 : {
472 : // calculate number of blanks
473 84 : sal_Int32 nX = rInf.GetIdx() - 1;
474 84 : sal_Int32 nHoleLen = 1;
475 168 : while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
476 0 : nHoleLen++;
477 :
478 : // First set ourselves and the insert, because there could be
479 : // a SwLineLayout
480 : sal_uInt16 nBlankSize;
481 84 : if( nHoleLen == GetLen() )
482 1 : nBlankSize = Width();
483 : else
484 83 : nBlankSize = nHoleLen * rInf.GetTextSize(OUString(' ')).Width();
485 84 : Width( Width() - nBlankSize );
486 84 : rInf.X( rInf.X() - nBlankSize );
487 84 : SetLen( GetLen() - nHoleLen );
488 84 : SwLinePortion *pHole = new SwHolePortion( *this );
489 84 : static_cast<SwHolePortion *>( pHole )->SetBlankWidth( nBlankSize );
490 84 : static_cast<SwHolePortion *>( pHole )->SetLen( nHoleLen );
491 84 : Insert( pHole );
492 : }
493 695 : }
494 :
495 0 : sal_Int32 SwTextPortion::GetCrsrOfst( const sal_uInt16 nOfst ) const
496 : {
497 : OSL_ENSURE( false, "SwTextPortion::GetCrsrOfst: don't use this method!" );
498 0 : return SwLinePortion::GetCrsrOfst( nOfst );
499 : }
500 :
501 : // The GetTextSize() assumes that the own length is correct
502 3950 : SwPosSize SwTextPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
503 : {
504 3950 : SwPosSize aSize = rInf.GetTextSize();
505 3950 : if( !GetJoinBorderWithPrev() )
506 3950 : aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace() );
507 3950 : if( !GetJoinBorderWithNext() )
508 3950 : aSize.Width(aSize.Width() + rInf.GetFont()->GetRightBorderSpace() );
509 :
510 3950 : aSize.Height(aSize.Height() +
511 3950 : rInf.GetFont()->GetTopBorderSpace() +
512 3950 : rInf.GetFont()->GetBottomBorderSpace() );
513 :
514 3950 : return aSize;
515 : }
516 :
517 19674 : void SwTextPortion::Paint( const SwTextPaintInfo &rInf ) const
518 : {
519 19674 : if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetText()[rInf.GetIdx()])
520 : {
521 : assert(false); // this is some debugging only code
522 0 : rInf.DrawBackBrush( *this );
523 0 : const OUString aText(CH_TXT_ATR_SUBST_FIELDEND);
524 0 : rInf.DrawText( aText, *this, 0, aText.getLength(), false );
525 : }
526 19674 : else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetText()[rInf.GetIdx()])
527 : {
528 : assert(false); // this is some debugging only code
529 0 : rInf.DrawBackBrush( *this );
530 0 : const OUString aText(CH_TXT_ATR_SUBST_FIELDSTART);
531 0 : rInf.DrawText( aText, *this, 0, aText.getLength(), false );
532 : }
533 19674 : else if( GetLen() )
534 : {
535 18139 : rInf.DrawBackBrush( *this );
536 18139 : rInf.DrawBorder( *this );
537 :
538 : // do we have to repaint a post it portion?
539 18139 : if( rInf.OnWin() && pPortion && !pPortion->Width() )
540 1805 : pPortion->PrePaint( rInf, this );
541 :
542 18139 : const SwWrongList *pWrongList = rInf.GetpWrongList();
543 18139 : const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
544 18139 : const SwWrongList *pSmarttags = rInf.GetSmartTags();
545 :
546 18139 : const bool bWrong = 0 != pWrongList;
547 18139 : const bool bGrammarCheck = 0 != pGrammarCheckList;
548 18139 : const bool bSmartTags = 0 != pSmarttags;
549 :
550 18139 : if ( bWrong || bSmartTags || bGrammarCheck )
551 6081 : rInf.DrawMarkedText( *this, rInf.GetLen(), false, bWrong, bSmartTags, bGrammarCheck );
552 : else
553 12058 : rInf.DrawText( *this, rInf.GetLen(), false );
554 : }
555 19674 : }
556 :
557 16 : bool SwTextPortion::GetExpText( const SwTextSizeInfo &, OUString & ) const
558 : {
559 16 : return false;
560 : }
561 :
562 : // Responsible for the justified paragraph. They calculate the blank
563 : // count and the resulting added space.
564 1563 : sal_Int32 SwTextPortion::GetSpaceCnt( const SwTextSizeInfo &rInf,
565 : sal_Int32& rCharCnt ) const
566 : {
567 1563 : sal_Int32 nCnt = 0;
568 1563 : sal_Int32 nPos = 0;
569 1563 : if ( InExpGrp() )
570 : {
571 10 : if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
572 : {
573 : // OnWin() likes to return a blank instead of an empty string from
574 : // time to time. We cannot use that here at all, however.
575 0 : bool bOldOnWin = rInf.OnWin();
576 0 : ((SwTextSizeInfo &)rInf).SetOnWin( false );
577 :
578 0 : OUString aStr;
579 0 : GetExpText( rInf, aStr );
580 0 : ((SwTextSizeInfo &)rInf).SetOnWin( bOldOnWin );
581 :
582 0 : nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
583 0 : nPos = aStr.getLength();
584 : }
585 : }
586 1553 : else if( !IsDropPortion() )
587 : {
588 1553 : nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
589 1553 : nPos = GetLen();
590 : }
591 1563 : rCharCnt = rCharCnt + nPos;
592 1563 : return nCnt;
593 : }
594 :
595 4089 : long SwTextPortion::CalcSpacing( long nSpaceAdd, const SwTextSizeInfo &rInf ) const
596 : {
597 4089 : sal_Int32 nCnt = 0;
598 :
599 4089 : if ( InExpGrp() )
600 : {
601 15 : if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
602 : {
603 : // OnWin() likes to return a blank instead of an empty string from
604 : // time to time. We cannot use that here at all, however.
605 0 : bool bOldOnWin = rInf.OnWin();
606 0 : ((SwTextSizeInfo &)rInf).SetOnWin( false );
607 :
608 0 : OUString aStr;
609 0 : GetExpText( rInf, aStr );
610 0 : ((SwTextSizeInfo &)rInf).SetOnWin( bOldOnWin );
611 0 : if( nSpaceAdd > 0 )
612 0 : nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
613 : else
614 : {
615 0 : nSpaceAdd = -nSpaceAdd;
616 0 : nCnt = aStr.getLength();
617 0 : }
618 : }
619 : }
620 4074 : else if( !IsDropPortion() )
621 : {
622 4074 : if( nSpaceAdd > 0 )
623 4074 : nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
624 : else
625 : {
626 0 : nSpaceAdd = -nSpaceAdd;
627 0 : nCnt = GetLen();
628 0 : SwLinePortion* pPor = GetPortion();
629 :
630 : // we do not want an extra space in front of margin portions
631 0 : if ( nCnt )
632 : {
633 0 : while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
634 0 : pPor = pPor->GetPortion();
635 :
636 0 : if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
637 0 : --nCnt;
638 : }
639 : }
640 : }
641 :
642 4089 : return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
643 : }
644 :
645 634 : void SwTextPortion::HandlePortion( SwPortionHandler& rPH ) const
646 : {
647 634 : rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
648 634 : }
649 :
650 25 : SwTextInputFieldPortion::SwTextInputFieldPortion()
651 : : SwTextPortion()
652 : , mbContainsInputFieldStart( false )
653 25 : , mbContainsInputFieldEnd( false )
654 : {
655 25 : SetWhichPor( POR_INPUTFLD );
656 25 : }
657 :
658 25 : bool SwTextInputFieldPortion::Format( SwTextFormatInfo &rInf )
659 : {
660 : mbContainsInputFieldStart =
661 25 : rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART;
662 : mbContainsInputFieldEnd =
663 25 : rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND;
664 :
665 25 : bool bRet = false;
666 50 : if ( rInf.GetLen() == 1
667 25 : && ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) )
668 : {
669 0 : Width( 0 );
670 : }
671 : else
672 : {
673 25 : SwTextSlot aFormatText( &rInf, this, true, true );
674 25 : if ( rInf.GetLen() == 0 )
675 : {
676 5 : Width( 0 );
677 : }
678 : else
679 : {
680 20 : const sal_Int32 nFormerLineStart = rInf.GetLineStart();
681 20 : if ( !mbContainsInputFieldStart )
682 : {
683 0 : rInf.SetLineStart( 0 );
684 : }
685 :
686 20 : bRet = SwTextPortion::Format( rInf );
687 :
688 20 : if ( mbContainsInputFieldEnd )
689 : {
690 : // adjust portion length accordingly, if complete text fits into the portion
691 20 : if ( GetLen() == rInf.GetLen() )
692 : {
693 20 : SetLen( GetLen() + 1 );
694 : }
695 : }
696 :
697 20 : if ( mbContainsInputFieldStart )
698 : {
699 : // adjust portion length accordingly
700 20 : SetLen( GetLen() + 1 );
701 : }
702 : else
703 : {
704 0 : rInf.SetLineStart( nFormerLineStart );
705 : }
706 25 : }
707 : }
708 :
709 25 : return bRet;
710 : }
711 :
712 25 : void SwTextInputFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
713 : {
714 25 : if ( Width() )
715 : {
716 22 : rInf.DrawViewOpt( *this, POR_INPUTFLD );
717 : SwTextSlot aPaintText( &rInf, this, true, true,
718 22 : ContainsOnlyDummyChars() ? OUString(" ") : OUString() );
719 22 : SwTextPortion::Paint( rInf );
720 : }
721 25 : }
722 :
723 45 : bool SwTextInputFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
724 : {
725 45 : sal_Int32 nIdx = rInf.GetIdx();
726 45 : sal_Int32 nLen = rInf.GetLen();
727 45 : if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
728 : {
729 45 : ++nIdx;
730 45 : --nLen;
731 : }
732 45 : if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND )
733 : {
734 45 : --nLen;
735 : }
736 45 : rText = rInf.GetText().copy( nIdx, std::min( nLen, rInf.GetText().getLength() - nIdx ) );
737 :
738 45 : return true;
739 : }
740 :
741 0 : SwPosSize SwTextInputFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
742 : {
743 0 : SwTextSlot aFormatText( &rInf, this, true, false );
744 0 : if ( rInf.GetLen() == 0 )
745 : {
746 0 : return SwPosSize( 0, 0 );
747 : }
748 :
749 0 : return rInf.GetTextSize();
750 : }
751 :
752 2 : sal_uInt16 SwTextInputFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
753 : {
754 4 : if( !Width()
755 2 : && ContainsOnlyDummyChars()
756 2 : && !rInf.GetOpt().IsPagePreview()
757 2 : && !rInf.GetOpt().IsReadonly()
758 4 : && SwViewOption::IsFieldShadings() )
759 : {
760 2 : return rInf.GetTextSize( " " ).Width();
761 : }
762 :
763 0 : return SwTextPortion::GetViewWidth( rInf );
764 : }
765 :
766 24 : bool SwTextInputFieldPortion::ContainsOnlyDummyChars() const
767 : {
768 24 : return GetLen() <= 2
769 4 : && mbContainsInputFieldStart
770 28 : && mbContainsInputFieldEnd;
771 : }
772 :
773 3780 : SwHolePortion::SwHolePortion( const SwTextPortion &rPor )
774 3780 : : nBlankWidth( 0 )
775 : {
776 3780 : SetLen( 1 );
777 3780 : Height( rPor.Height() );
778 3780 : SetAscent( rPor.GetAscent() );
779 3780 : SetWhichPor( POR_HOLE );
780 3780 : }
781 :
782 3827 : SwLinePortion *SwHolePortion::Compress() { return this; }
783 :
784 920 : void SwHolePortion::Paint( const SwTextPaintInfo &rInf ) const
785 : {
786 920 : if( !rInf.GetOut() )
787 920 : return;
788 :
789 : // #i16816# export stuff only needed for tagged pdf support
790 920 : if (!SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut()) )
791 920 : return;
792 :
793 : // #i68503# the hole must have no decoration for a consistent visual appearance
794 0 : const SwFont* pOrigFont = rInf.GetFont();
795 0 : SwFont* pHoleFont = NULL;
796 0 : SwFontSave* pFontSave = NULL;
797 0 : if( pOrigFont->GetUnderline() != UNDERLINE_NONE
798 0 : || pOrigFont->GetOverline() != UNDERLINE_NONE
799 0 : || pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
800 : {
801 0 : pHoleFont = new SwFont( *pOrigFont );
802 0 : pHoleFont->SetUnderline( UNDERLINE_NONE );
803 0 : pHoleFont->SetOverline( UNDERLINE_NONE );
804 0 : pHoleFont->SetStrikeout( STRIKEOUT_NONE );
805 0 : pFontSave = new SwFontSave( rInf, pHoleFont );
806 : }
807 :
808 0 : const OUString aText( ' ' );
809 0 : rInf.DrawText( aText, *this, 0, 1, false );
810 :
811 0 : delete pFontSave;
812 0 : delete pHoleFont;
813 : }
814 :
815 0 : bool SwHolePortion::Format( SwTextFormatInfo &rInf )
816 : {
817 0 : return rInf.IsFull() || rInf.X() >= rInf.Width();
818 : }
819 :
820 4 : void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
821 : {
822 4 : rPH.Text( GetLen(), GetWhichPor() );
823 4 : }
824 :
825 16 : void SwFieldMarkPortion::Paint( const SwTextPaintInfo & /*rInf*/) const
826 : {
827 : // These shouldn't be painted!
828 : //SwTextPortion::Paint(rInf);
829 16 : }
830 :
831 222 : bool SwFieldMarkPortion::Format( SwTextFormatInfo & )
832 : {
833 222 : Width(0);
834 222 : return false;
835 : }
836 :
837 2 : void SwFieldFormCheckboxPortion::Paint( const SwTextPaintInfo& rInf ) const
838 : {
839 2 : SwTextNode* pNd = const_cast<SwTextNode*>(rInf.GetTextFrm()->GetTextNode());
840 2 : const SwDoc *doc=pNd->GetDoc();
841 2 : SwIndex aIndex( pNd, rInf.GetIdx() );
842 4 : SwPosition aPosition(*pNd, aIndex);
843 :
844 2 : IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
845 :
846 : OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX,
847 : "Where is my form field bookmark???");
848 :
849 2 : if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
850 : {
851 2 : const ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
852 2 : bool bChecked = pCheckboxFm && pCheckboxFm->IsChecked();
853 2 : rInf.DrawCheckBox(*this, bChecked);
854 2 : }
855 2 : }
856 :
857 9 : bool SwFieldFormCheckboxPortion::Format( SwTextFormatInfo & rInf )
858 : {
859 9 : SwTextNode *pNd = rInf.GetTextFrm( )->GetTextNode( );
860 9 : const SwDoc *doc = pNd->GetDoc( );
861 9 : SwIndex aIndex( pNd, rInf.GetIdx( ) );
862 18 : SwPosition aPosition( *pNd, aIndex );
863 9 : IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
864 : OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX, "Where is my form field bookmark???");
865 9 : if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
866 : {
867 9 : Width( rInf.GetTextHeight( ) );
868 9 : Height( rInf.GetTextHeight( ) );
869 9 : SetAscent( rInf.GetAscent( ) );
870 : }
871 18 : return false;
872 177 : }
873 :
874 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|