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