LibreOffice Module sw (master)  1
porlay.cxx
Go to the documentation of this file.
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 "porlay.hxx"
21 #include "itrform2.hxx"
22 #include "porglue.hxx"
23 #include <blink.hxx>
24 #include "redlnitr.hxx"
25 #include "porfly.hxx"
26 #include "porrst.hxx"
27 #include "pormulti.hxx"
28 #include "pordrop.hxx"
29 #include <breakit.hxx>
30 #include <unicode/uchar.h>
31 #include <com/sun/star/i18n/ScriptType.hpp>
32 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
33 #include <com/sun/star/i18n/CTLScriptType.hpp>
34 #include <com/sun/star/i18n/WordType.hpp>
35 #include <com/sun/star/i18n/XBreakIterator.hpp>
36 #include <paratr.hxx>
37 #include <sal/log.hxx>
38 #include <editeng/adjustitem.hxx>
40 #include <svl/asiancfg.hxx>
41 #include <svl/languageoptions.hxx>
42 #include <tools/multisel.hxx>
43 #include <unotools/charclass.hxx>
44 #include <charfmt.hxx>
45 #include <docary.hxx>
46 #include <redline.hxx>
47 #include <calbck.hxx>
48 #include <doc.hxx>
49 #include <swscanner.hxx>
50 #include <txatbase.hxx>
54 #include <IMark.hxx>
55 
56 using namespace ::com::sun::star;
57 using namespace i18n::ScriptType;
58 
59 #include <unicode/ubidi.h>
61 #include <i18nutil/unicode.hxx>
62 
63 #define IS_JOINING_GROUP(c, g) ( u_getIntPropertyValue( (c), UCHAR_JOINING_GROUP ) == U_JG_##g )
64 #define isAinChar(c) IS_JOINING_GROUP((c), AIN)
65 #define isAlefChar(c) IS_JOINING_GROUP((c), ALEF)
66 #define isDalChar(c) IS_JOINING_GROUP((c), DAL)
67 #if U_ICU_VERSION_MAJOR_NUM >= 58
68 #define isFehChar(c) (IS_JOINING_GROUP((c), FEH) || IS_JOINING_GROUP((c), AFRICAN_FEH))
69 #else
70 #define isFehChar(c) IS_JOINING_GROUP((c), FEH)
71 #endif
72 #define isGafChar(c) IS_JOINING_GROUP((c), GAF)
73 #define isHehChar(c) IS_JOINING_GROUP((c), HEH)
74 #define isKafChar(c) IS_JOINING_GROUP((c), KAF)
75 #define isLamChar(c) IS_JOINING_GROUP((c), LAM)
76 #if U_ICU_VERSION_MAJOR_NUM >= 58
77 #define isQafChar(c) (IS_JOINING_GROUP((c), QAF) || IS_JOINING_GROUP((c), AFRICAN_QAF))
78 #else
79 #define isQafChar(c) IS_JOINING_GROUP((c), QAF)
80 #endif
81 #define isRehChar(c) IS_JOINING_GROUP((c), REH)
82 #define isTahChar(c) IS_JOINING_GROUP((c), TAH)
83 #define isTehMarbutaChar(c) IS_JOINING_GROUP((c), TEH_MARBUTA)
84 #define isWawChar(c) IS_JOINING_GROUP((c), WAW)
85 #define isSeenOrSadChar(c) (IS_JOINING_GROUP((c), SAD) || IS_JOINING_GROUP((c), SEEN))
86 
87 // Beh and charters that behave like Beh in medial form.
88 static bool isBehChar(sal_Unicode cCh)
89 {
90  bool bRet = false;
91  switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
92  {
93  case U_JG_BEH:
94  case U_JG_NOON:
95 #if U_ICU_VERSION_MAJOR_NUM >= 58
96  case U_JG_AFRICAN_NOON:
97 #endif
98  case U_JG_NYA:
99  case U_JG_YEH:
100  case U_JG_FARSI_YEH:
101  case U_JG_BURUSHASKI_YEH_BARREE:
102  bRet = true;
103  break;
104  default:
105  bRet = false;
106  break;
107  }
108 
109  return bRet;
110 }
111 
112 // Yeh and charters that behave like Yeh in final form.
113 static bool isYehChar(sal_Unicode cCh)
114 {
115  bool bRet = false;
116  switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
117  {
118  case U_JG_YEH:
119  case U_JG_FARSI_YEH:
120  case U_JG_YEH_BARREE:
121  case U_JG_BURUSHASKI_YEH_BARREE:
122  case U_JG_YEH_WITH_TAIL:
123  bRet = true;
124  break;
125  default:
126  bRet = false;
127  break;
128  }
129 
130  return bRet;
131 }
132 
133 static bool isTransparentChar ( sal_Unicode cCh )
134 {
135  return u_getIntPropertyValue( cCh, UCHAR_JOINING_TYPE ) == U_JT_TRANSPARENT;
136 }
137 
138 // Checks if cCh + cNectCh builds a ligature (used for Kashidas)
139 static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh )
140 {
141  // Lam + Alef
142  return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
143 }
144 
145 // Checks if cCh is connectable to cPrevCh (used for Kashidas)
146 static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh )
147 {
148  const int32_t nJoiningType = u_getIntPropertyValue( cPrevCh, UCHAR_JOINING_TYPE );
149  bool bRet = nJoiningType != U_JT_RIGHT_JOINING && nJoiningType != U_JT_NON_JOINING;
150 
151  // check for ligatures cPrevChar + cChar
152  if( bRet )
153  bRet = !lcl_IsLigature( cPrevCh, cCh );
154 
155  return bRet;
156 }
157 
158 static bool lcl_HasStrongLTR ( const OUString& rText, sal_Int32 nStart, sal_Int32 nEnd )
159  {
160  for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
161  {
162  const UCharDirection nCharDir = u_charDirection ( rText[ nCharIdx ] );
163  if ( nCharDir == U_LEFT_TO_RIGHT ||
164  nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
165  nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
166  return true;
167  }
168  return false;
169  }
170 
171 // This is (meant to be) functionally equivalent to 'delete m_pNext' where
172 // deleting a SwLineLayout recursively deletes the owned m_pNext SwLineLayout.
173 //
174 // Here, instead of using a potentially deep stack, iterate over all the
175 // SwLineLayouts that would be deleted recursively and delete them linearly
177 {
178  if (!m_pNext)
179  return;
180  std::vector<SwLineLayout*> aNexts;
181  SwLineLayout* pNext = m_pNext;
182  do
183  {
184  aNexts.push_back(pNext);
185  SwLineLayout* pLastNext = pNext;
186  pNext = pNext->GetNext();
187  pLastNext->SetNext(nullptr);
188  }
189  while (pNext);
190  for (auto a : aNexts)
191  delete a;
192 }
193 
194 // class SwLineLayout: This is the layout of a single line, which is made
195 // up of its dimension, the character count and the word spacing in the line.
196 // Line objects are managed in an own pool, in order to store them continuously
197 // in memory so that they are paged out together and don't fragment memory.
199 {
200  Truncate();
201  DeleteNext();
202  if( pBlink )
203  pBlink->Delete( this );
204  m_pLLSpaceAdd.reset();
205  m_pKanaComp.reset();
206 }
207 
209 {
210  // First attribute change: copy mass and length from *pIns into the first
211  // text portion
212  if( !mpNextPortion )
213  {
214  if( GetLen() )
215  {
216  mpNextPortion = SwTextPortion::CopyLinePortion(*this);
217  if( IsBlinking() && pBlink )
218  {
219  SetBlinking( false );
220  pBlink->Replace( this, mpNextPortion );
221  }
222  }
223  else
224  {
225  SetNextPortion( pIns );
226  return pIns;
227  }
228  }
229  // Call with scope or we'll end up with recursion!
230  return mpNextPortion->SwLinePortion::Insert( pIns );
231 }
232 
234 {
235  // First attribute change: copy mass and length from *pIns into the first
236  // text portion
237  if( !mpNextPortion )
238  mpNextPortion = SwTextPortion::CopyLinePortion(*this);
239  // Call with scope or we'll end up with recursion!
240  return mpNextPortion->SwLinePortion::Append( pIns );
241 }
242 
243 // For special treatment of empty lines
244 
246 {
247  if( GetLen() )
248  return SwTextPortion::Format( rInf );
249 
250  Height( rInf.GetTextHeight() );
251  return true;
252 }
253 
254 // We collect all FlyPortions at the beginning of the line and make that a
255 // MarginPortion.
257 {
258  SwMarginPortion *pLeft = (GetNextPortion() && GetNextPortion()->IsMarginPortion()) ?
259  static_cast<SwMarginPortion *>(GetNextPortion()) : nullptr;
260  if( !GetNextPortion() )
261  SetNextPortion(SwTextPortion::CopyLinePortion(*this));
262  if( !pLeft )
263  {
264  pLeft = new SwMarginPortion;
265  pLeft->SetNextPortion( GetNextPortion() );
266  SetNextPortion( pLeft );
267  }
268  else
269  {
270  pLeft->Height( 0 );
271  pLeft->Width( 0 );
272  pLeft->SetLen(TextFrameIndex(0));
273  pLeft->SetAscent( 0 );
274  pLeft->SetNextPortion( nullptr );
275  pLeft->SetFixWidth(0);
276  }
277 
278  SwLinePortion *pPos = pLeft->GetNextPortion();
279  while( pPos )
280  {
281  if( pPos->IsFlyPortion() )
282  {
283  // The FlyPortion gets sucked out...
284  pLeft->Join( static_cast<SwGluePortion*>(pPos) );
285  pPos = pLeft->GetNextPortion();
286  if( GetpKanaComp() && !GetKanaComp().empty() )
287  GetKanaComp().pop_front();
288  }
289  else
290  pPos = nullptr;
291  }
292  return pLeft;
293 }
294 
296 {
297  if ( !m_pLLSpaceAdd )
298  CreateSpaceAdd();
299  else
300  SetLLSpaceAdd( 0, 0 );
301 }
302 
303 void SwLineLayout::CreateSpaceAdd( const long nInit )
304 {
305  m_pLLSpaceAdd.reset( new std::vector<long> );
306  SetLLSpaceAdd( nInit, 0 );
307 }
308 
309 // Returns true if there are only blanks in [nStt, nEnd[
310 static bool lcl_HasOnlyBlanks(const OUString& rText, TextFrameIndex nStt, TextFrameIndex nEnd)
311 {
312  bool bBlankOnly = true;
313  while ( nStt < nEnd )
314  {
315  const sal_Unicode cChar = rText[ sal_Int32(nStt++) ];
316  if ( ' ' != cChar && CH_FULL_BLANK != cChar && CH_SIX_PER_EM != cChar )
317  {
318  bBlankOnly = false;
319  break;
320  }
321  }
322  return bBlankOnly;
323 }
324 
325 // Swapped out from FormatLine()
327 {
328  const sal_uInt16 nLineWidth = rInf.RealWidth();
329 
330  sal_uInt16 nFlyAscent = 0;
331  sal_uInt16 nFlyHeight = 0;
332  sal_uInt16 nFlyDescent = 0;
333  bool bOnlyPostIts = true;
334  SetHanging( false );
335 
336  bool bTmpDummy = !GetLen();
337  SwFlyCntPortion* pFlyCnt = nullptr;
338  if( bTmpDummy )
339  {
340  nFlyAscent = 0;
341  nFlyHeight = 0;
342  nFlyDescent = 0;
343  }
344 
345  // #i3952#
346  const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
349 
350  bool bHasBlankPortion = false;
351  bool bHasOnlyBlankPortions = true;
352 
353  if( mpNextPortion )
354  {
355  SetContent( false );
356  if( mpNextPortion->IsBreakPortion() )
357  {
358  SetLen( mpNextPortion->GetLen() );
359  if( GetLen() )
360  bTmpDummy = false;
361  }
362  else
363  {
364  const sal_uInt16 nLineHeight = Height();
365  Init( GetNextPortion() );
366  SwLinePortion *pPos = mpNextPortion;
367  SwLinePortion *pLast = this;
368  sal_uInt16 nMaxDescent = 0;
369 
370  // A group is a segment in the portion chain of pCurr or a fixed
371  // portion spanning to the end or the next fixed portion
372  while( pPos )
373  {
375  "sw.core", "SwLineLayout::CalcLine: don't use SwLinePortions !" );
376 
377  // Null portions are eliminated. They can form if two FlyFrames
378  // overlap.
379  if( !pPos->Compress() )
380  {
381  // Only take over Height and Ascent if the rest of the line
382  // is empty.
383  if( !pPos->GetNextPortion() )
384  {
385  if( !Height() )
386  Height( pPos->Height() );
387  if( !GetAscent() )
388  SetAscent( pPos->GetAscent() );
389  }
390  delete pLast->Cut( pPos );
391  pPos = pLast->GetNextPortion();
392  continue;
393  }
394 
395  TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + nLineLength;
396  nLineLength += pPos->GetLen();
397  AddPrtWidth( pPos->Width() );
398 
399  // #i3952#
400  if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
401  {
402  if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
403  ( pPos->IsTextPortion() &&
404  lcl_HasOnlyBlanks( rInf.GetText(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
405  {
406  pLast = pPos;
407  pPos = pPos->GetNextPortion();
408  bHasBlankPortion = true;
409  continue;
410  }
411  }
412 
413  // Ignore drop portion height
414  if( pPos->IsDropPortion() && static_cast<SwDropPortion*>(pPos)->GetLines() > 1)
415  {
416  pLast = pPos;
417  pPos = pPos->GetNextPortion();
418  continue;
419  }
420 
421  bHasOnlyBlankPortions = false;
422 
423  // We had an attribute change: Sum up/build maxima of length and mass
424 
425  sal_uInt16 nPosHeight = pPos->Height();
426  sal_uInt16 nPosAscent = pPos->GetAscent();
427 
428  SAL_WARN_IF( nPosHeight < nPosAscent,
429  "sw.core", "SwLineLayout::CalcLine: bad ascent or height" );
430 
431  if( pPos->IsHangingPortion() )
432  {
433  SetHanging(true);
434  rInf.GetParaPortion()->SetMargin();
435  }
436 
437  // To prevent that a paragraph-end-character does not change
438  // the line height through a Descent and thus causing the line
439  // to reformat.
440  if ( !pPos->IsBreakPortion() || !Height() )
441  {
442  if (!pPos->IsPostItsPortion()) bOnlyPostIts = false;
443 
444  if( bTmpDummy && !nLineLength )
445  {
446  if( pPos->IsFlyPortion() )
447  {
448  if( nFlyHeight < nPosHeight )
449  nFlyHeight = nPosHeight;
450  if( nFlyAscent < nPosAscent )
451  nFlyAscent = nPosAscent;
452  if( nFlyDescent < nPosHeight - nPosAscent )
453  nFlyDescent = nPosHeight - nPosAscent;
454  }
455  else
456  {
457  if( pPos->InNumberGrp() )
458  {
459  sal_uInt16 nTmp = rInf.GetFont()->GetAscent(
460  rInf.GetVsh(), *rInf.GetOut() );
461  if( nTmp > nPosAscent )
462  {
463  nPosHeight += nTmp - nPosAscent;
464  nPosAscent = nTmp;
465  }
466  nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
467  *rInf.GetOut() );
468  if( nTmp > nPosHeight )
469  nPosHeight = nTmp;
470  }
471  Height( nPosHeight );
472  nAscent = nPosAscent;
473  nMaxDescent = nPosHeight - nPosAscent;
474  }
475  }
476  else if( !pPos->IsFlyPortion() )
477  {
478  if( Height() < nPosHeight )
479  {
480  // Height is set to 0 when Init() is called.
481  if (bIgnoreBlanksAndTabsForLineHeightCalculation && pPos->GetWhichPor() == PortionType::FlyCnt)
482  // Compat flag set: take the line height, if it's larger.
483  Height(std::max(nPosHeight, nLineHeight));
484  else
485  // Just care about the portion height.
486  Height(nPosHeight);
487  }
488  SwFlyCntPortion* pAsFly(nullptr);
489  if(pPos->IsFlyCntPortion())
490  pAsFly = static_cast<SwFlyCntPortion*>(pPos);
491  if( pAsFly || ( pPos->IsMultiPortion()
492  && static_cast<SwMultiPortion*>(pPos)->HasFlyInContent() ) )
493  rLine.SetFlyInCntBase();
494  if(pAsFly && pAsFly->GetAlign() != sw::LineAlign::NONE)
495  {
496  pAsFly->SetMax(false);
497  if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
498  pFlyCnt = pAsFly;
499  }
500  else
501  {
502  if( nAscent < nPosAscent )
503  nAscent = nPosAscent;
504  if( nMaxDescent < nPosHeight - nPosAscent )
505  nMaxDescent = nPosHeight - nPosAscent;
506  }
507  }
508  }
509  else if( pPos->GetLen() )
510  bTmpDummy = false;
511 
512  if( !HasContent() && !pPos->InNumberGrp() )
513  {
514  if ( pPos->InExpGrp() )
515  {
516  OUString aText;
517  if( pPos->GetExpText( rInf, aText ) && !aText.isEmpty() )
518  SetContent(true);
519  }
520  else if( ( pPos->InTextGrp() || pPos->IsMultiPortion() ) &&
521  pPos->GetLen() )
522  SetContent(true);
523  }
524 
525  bTmpDummy &= !HasContent() && ( !pPos->Width() || pPos->IsFlyPortion() );
526 
527  pLast = pPos;
528  pPos = pPos->GetNextPortion();
529  }
530 
531  if( pFlyCnt )
532  {
533  if( pFlyCnt->Height() == Height() )
534  {
535  pFlyCnt->SetMax( true );
536  if( Height() > nMaxDescent + nAscent )
537  {
538  if( sw::LineAlign::BOTTOM == pFlyCnt->GetAlign() )
539  nAscent = Height() - nMaxDescent;
540  else if( sw::LineAlign::CENTER == pFlyCnt->GetAlign() )
541  nAscent = ( Height() + nAscent - nMaxDescent ) / 2;
542  }
543  pFlyCnt->SetAscent( nAscent );
544  }
545  }
546 
547  if( bTmpDummy && nFlyHeight )
548  {
549  nAscent = nFlyAscent;
550  if( nFlyDescent > nFlyHeight - nFlyAscent )
551  Height( nFlyHeight + nFlyDescent );
552  else
553  Height( nFlyHeight );
554  }
555  else if( nMaxDescent > Height() - nAscent )
556  Height( nMaxDescent + nAscent );
557 
558  if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
559  {
560  Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
561  nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
562  }
563  }
564  }
565  else
566  {
567  SetContent( !bTmpDummy );
568 
569  // #i3952#
570  if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
571  lcl_HasOnlyBlanks( rInf.GetText(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
572  {
573  bHasBlankPortion = true;
574  }
575  }
576 
577  // #i3952#
578  if ( bHasBlankPortion && bHasOnlyBlankPortions )
579  {
580  sal_uInt16 nTmpAscent = GetAscent();
581  sal_uInt16 nTmpHeight = Height();
582  rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
583  SetAscent( nTmpAscent );
584  Height( nTmpHeight );
585  }
586 
587  // Robust:
588  if( nLineWidth < Width() )
589  Width( nLineWidth );
590  SAL_WARN_IF( nLineWidth < Width(), "sw.core", "SwLineLayout::CalcLine: line is bursting" );
591  SetDummy( bTmpDummy );
592  std::pair<SwTextNode const*, sal_Int32> const start(
593  rInf.GetTextFrame()->MapViewToModel(rLine.GetStart()));
594  std::pair<SwTextNode const*, sal_Int32> const end(
595  rInf.GetTextFrame()->MapViewToModel(rLine.GetEnd()));
596  SetRedline( rLine.GetRedln() &&
597  rLine.GetRedln()->CheckLine(start.first->GetIndex(), start.second,
598  end.first->GetIndex(), end.second) );
599 }
600 
601 // #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
602 // to control, if the fly content portions and line portion are considered.
604  SwTwips& _orDescent,
605  SwTwips& _orObjAscent,
606  SwTwips& _orObjDescent,
607  const SwLinePortion* _pDontConsiderPortion,
608  const bool _bNoFlyCntPorAndLinePor ) const
609 {
610  _orAscent = 0;
611  _orDescent = 0;
612  _orObjAscent = 0;
613  _orObjDescent = 0;
614 
615  const SwLinePortion* pTmpPortion = this;
616  if ( !pTmpPortion->GetLen() && pTmpPortion->GetNextPortion() )
617  {
618  pTmpPortion = pTmpPortion->GetNextPortion();
619  }
620 
621  while ( pTmpPortion )
622  {
623  if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
624  ( !_bNoFlyCntPorAndLinePor ||
625  ( !pTmpPortion->IsFlyCntPortion() &&
626  !(pTmpPortion == this && pTmpPortion->GetNextPortion() ) ) ) )
627  {
628  SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
629  SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
630  nPortionAsc;
631 
632  const bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
633  static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
634  ( pTmpPortion != _pDontConsiderPortion );
635 
636  if ( bFlyCmp )
637  {
638  _orObjAscent = std::max( _orObjAscent, nPortionAsc );
639  _orObjDescent = std::max( _orObjDescent, nPortionDesc );
640  }
641 
642  if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
643  {
644  _orAscent = std::max( _orAscent, nPortionAsc );
645  _orDescent = std::max( _orDescent, nPortionDesc );
646  }
647  }
648  pTmpPortion = pTmpPortion->GetNextPortion();
649  }
650 }
651 
653 {
654  m_bFormatAdj = m_bDummy = m_bEndHyph = m_bMidHyph = m_bFly
655  = m_bRest = m_bBlinking = m_bClipping = m_bContent = m_bRedline
656  = m_bForcedLeftMargin = m_bHanging = false;
657 }
658 
660  : m_pNext( nullptr ), m_nRealHeight( 0 ),
661  m_bUnderscore( false )
662 {
663  ResetFlags();
665 }
666 
668 {
669  const SwLinePortion *pRet = mpNextPortion ? mpNextPortion : this;
670  return const_cast<SwLinePortion*>(pRet);
671 }
672 
674 {
675  if (TextFrameIndex(0) != rRange.nLen)
676  {
677  if (TextFrameIndex(0) == nLen) {
678  nStart = rRange.nStart;
679  nLen = rRange.nLen ;
680  }
681  else {
682  if(rRange.nStart + rRange.nLen > nStart + nLen) {
683  nLen = rRange.nStart + rRange.nLen - nStart;
684  }
685  if(rRange.nStart < nStart) {
686  nLen += nStart - rRange.nStart;
687  nStart = rRange.nStart;
688  }
689  }
690  }
691  return *this;
692 }
693 
695  : m_nInvalidityPos(0)
696  , m_nDefaultDir(0)
697 {
698 };
699 
701 {
702 }
703 
704 // Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
705 // Sw Script Types (SwFontScript::Latin, SwFontScript::CJK, SwFontScript::CTL), used to identify the font
706 static SwFontScript lcl_ScriptToFont(sal_uInt16 const nScript)
707 {
708  switch ( nScript ) {
709  case i18n::ScriptType::LATIN : return SwFontScript::Latin;
710  case i18n::ScriptType::ASIAN : return SwFontScript::CJK;
711  case i18n::ScriptType::COMPLEX : return SwFontScript::CTL;
712  }
713 
714  OSL_FAIL( "Somebody tells lies about the script type!" );
715  return SwFontScript::Latin;
716 }
717 
719 {
720  const sal_uInt16 nScript(ScriptType(nIdx));
721  return lcl_ScriptToFont(nScript);
722 }
723 
724 SwFontScript SwScriptInfo::WhichFont(sal_Int32 nIdx, OUString const& rText)
725 {
726  const sal_uInt16 nScript(g_pBreakIt->GetRealScriptOfText(rText, nIdx));
727  return lcl_ScriptToFont(nScript);
728 }
729 
730 // searches for script changes in rText and stores them
732  sw::MergedPara const*const pMerged)
733 {
734  InitScriptInfo( rNode, pMerged, m_nDefaultDir == UBIDI_RTL );
735 }
736 
738  sw::MergedPara const*const pMerged, bool bRTL)
739 {
740  assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
741 
742  const OUString& rText(pMerged ? pMerged->mergedText : rNode.GetText());
743 
744  // HIDDEN TEXT INFORMATION
745 
746  m_HiddenChg.clear();
747  if (pMerged)
748  {
749  SwTextNode const* pNode(nullptr);
750  TextFrameIndex nOffset(0);
751  for (auto iter = pMerged->extents.begin(); iter != pMerged->extents.end(); ++iter)
752  {
753  if (iter->pNode == pNode)
754  {
755  nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
756  continue; // skip extents at end of previous node
757  }
758  pNode = iter->pNode;
759  Range aRange( 0, pNode->Len() > 0 ? pNode->Len() - 1 : 0 );
760  MultiSelection aHiddenMulti( aRange );
761  CalcHiddenRanges( *pNode, aHiddenMulti );
762 
763  for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
764  {
765  const Range& rRange = aHiddenMulti.GetRange( i );
766  const sal_Int32 nStart = rRange.Min();
767  const sal_Int32 nEnd = rRange.Max() + 1;
768 
769  while (true)
770  {
771  // because of the selectRedLineDeleted call, never overlaps
772  // extents, must be contained inside one extent
773  assert(!(iter->nStart <= nStart && nStart < iter->nEnd && iter->nEnd < nEnd));
774  assert(!(nStart < iter->nStart && iter->nStart < nEnd && nEnd <= iter->nEnd));
775  if (iter->nStart <= nStart && nEnd <= iter->nEnd)
776  {
777  if (iter->nStart == nStart && !m_HiddenChg.empty()
778  && m_HiddenChg.back() == nOffset)
779  {
780  // previous one went until end of extent, extend it
781  m_HiddenChg.back() += TextFrameIndex(nEnd - iter->nStart);
782  }
783  else // new one
784  {
785  m_HiddenChg.push_back(nOffset + TextFrameIndex(nStart - iter->nStart));
786  m_HiddenChg.push_back(nOffset + TextFrameIndex(nEnd - iter->nStart));
787  }
788  break;
789  }
790  else
791  {
792  nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
793  ++iter;
794  // because selectRedLineDeleted, must find it in pNode
795  assert(iter != pMerged->extents.end());
796  assert(iter->pNode == pNode);
797  }
798  }
799  }
800  }
801  }
802  else
803  {
804  Range aRange( 0, !rText.isEmpty() ? rText.getLength() - 1 : 0 );
805  MultiSelection aHiddenMulti( aRange );
806  CalcHiddenRanges( rNode, aHiddenMulti );
807 
808  for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
809  {
810  const Range& rRange = aHiddenMulti.GetRange( i );
811  const sal_Int32 nStart = rRange.Min();
812  const sal_Int32 nEnd = rRange.Max() + 1;
813 
814  m_HiddenChg.push_back( TextFrameIndex(nStart) );
815  m_HiddenChg.push_back( TextFrameIndex(nEnd) );
816  }
817  }
818 
819  // SCRIPT AND SCRIPT RELATED INFORMATION
820 
822 
823  // COMPLETE_STRING means the data structure is up to date
825 
826  // this is the default direction
827  m_nDefaultDir = static_cast<sal_uInt8>(bRTL ? UBIDI_RTL : UBIDI_LTR);
828 
829  // counter for script info arrays
830  size_t nCnt = 0;
831  // counter for compression information arrays
832  size_t nCntComp = 0;
833  // counter for kashida array
834  size_t nCntKash = 0;
835 
836  sal_Int16 nScript = i18n::ScriptType::LATIN;
837 
838  // compression type
840 
841  auto const& rParaItems((pMerged ? *pMerged->pParaPropsNode : rNode).GetSwAttrSet());
842  // justification type
843  const bool bAdjustBlock = SvxAdjust::Block == rParaItems.GetAdjust().GetAdjust();
844 
845  // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
846 
847  if( nChg )
848  {
849  // if change position = 0 we do not use any data from the arrays
850  // because by deleting all characters of the first group at the beginning
851  // of a paragraph nScript is set to a wrong value
852  SAL_WARN_IF( !CountScriptChg(), "sw.core", "Where're my changes of script?" );
853  while( nCnt < CountScriptChg() )
854  {
855  if ( nChg > GetScriptChg( nCnt ) )
856  nCnt++;
857  else
858  {
859  nScript = GetScriptType( nCnt );
860  break;
861  }
862  }
863  if( CharCompressType::NONE != aCompEnum )
864  {
865  while( nCntComp < CountCompChg() )
866  {
867  if ( nChg <= GetCompStart( nCntComp ) )
868  break;
869  nCntComp++;
870  }
871  }
872  if ( bAdjustBlock )
873  {
874  while( nCntKash < CountKashida() )
875  {
876  if ( nChg <= GetKashida( nCntKash ) )
877  break;
878  nCntKash++;
879  }
880  }
881  }
882 
883  // ADJUST nChg VALUE:
884 
885  // by stepping back one position we know that we are inside a group
886  // declared as an nScript group
887  if ( nChg )
888  --nChg;
889 
890  const TextFrameIndex nGrpStart = nCnt ? GetScriptChg(nCnt - 1) : TextFrameIndex(0);
891 
892  // we go back in our group until we reach the first character of
893  // type nScript
894  while ( nChg > nGrpStart &&
895  nScript != g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)))
896  --nChg;
897 
898  // If we are at the start of a group, we do not trust nScript,
899  // we better get nScript from the breakiterator:
900  if ( nChg == nGrpStart )
901  nScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)));
902 
903  // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
904 
905  // remove invalid entries from script information arrays
906  m_ScriptChanges.erase(m_ScriptChanges.begin() + nCnt, m_ScriptChanges.end());
907 
908  // get the start of the last compression group
909  TextFrameIndex nLastCompression = nChg;
910  if( nCntComp )
911  {
912  --nCntComp;
913  nLastCompression = GetCompStart( nCntComp );
914  if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
915  {
916  nLastCompression = nChg;
917  ++nCntComp;
918  }
919  }
920 
921  // remove invalid entries from compression information arrays
922  m_CompressionChanges.erase(m_CompressionChanges.begin() + nCntComp,
923  m_CompressionChanges.end());
924 
925  // get the start of the last kashida group
926  TextFrameIndex nLastKashida = nChg;
927  if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
928  {
929  --nCntKash;
930  nLastKashida = GetKashida( nCntKash );
931  }
932 
933  // remove invalid entries from kashida array
934  m_Kashida.erase(m_Kashida.begin() + nCntKash, m_Kashida.end());
935 
936  // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
937  // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
938 
939  if (WEAK == g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)))
940  {
941  // If the beginning of the current group is weak, this means that
942  // all of the characters in this group are weak. We have to assign
943  // the scripts to these characters depending on the fonts which are
944  // set for these characters to display them.
945  TextFrameIndex nEnd(
946  g_pBreakIt->GetBreakIter()->endOfScript(rText, sal_Int32(nChg), WEAK));
947 
948  if (nEnd > TextFrameIndex(rText.getLength()) || nEnd < TextFrameIndex(0))
949  nEnd = TextFrameIndex(rText.getLength());
950 
952 
953  SAL_WARN_IF( i18n::ScriptType::LATIN != nScript &&
954  i18n::ScriptType::ASIAN != nScript &&
955  i18n::ScriptType::COMPLEX != nScript, "sw.core", "Wrong default language" );
956 
957  nChg = nEnd;
958 
959  // Get next script type or set to weak in order to exit
960  sal_uInt8 nNextScript = (nEnd < TextFrameIndex(rText.getLength()))
961  ? static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nEnd)))
962  : sal_uInt8(WEAK);
963 
964  if ( nScript != nNextScript )
965  {
966  m_ScriptChanges.emplace_back(nEnd, nScript);
967  nCnt++;
968  nScript = nNextScript;
969  }
970  }
971 
972  // UPDATE THE SCRIPT INFO ARRAYS:
973 
974  while (nChg < TextFrameIndex(rText.getLength())
975  || (m_ScriptChanges.empty() && rText.isEmpty()))
976  {
977  SAL_WARN_IF( i18n::ScriptType::WEAK == nScript,
978  "sw.core", "Inserting WEAK into SwScriptInfo structure" );
979 
980  TextFrameIndex nSearchStt = nChg;
981  nChg = TextFrameIndex(g_pBreakIt->GetBreakIter()->endOfScript(
982  rText, sal_Int32(nSearchStt), nScript));
983 
984  if (nChg > TextFrameIndex(rText.getLength()) || nChg < TextFrameIndex(0))
985  nChg = TextFrameIndex(rText.getLength());
986 
987  // #i28203#
988  // for 'complex' portions, we make sure that a portion does not contain more
989  // than one script:
990  if( i18n::ScriptType::COMPLEX == nScript )
991  {
992  const short nScriptType = ScriptTypeDetector::getCTLScriptType(
993  rText, sal_Int32(nSearchStt) );
994  TextFrameIndex nNextCTLScriptStart = nSearchStt;
995  short nCurrentScriptType = nScriptType;
996  while( css::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
997  {
998  nNextCTLScriptStart = TextFrameIndex(
1000  rText, sal_Int32(nNextCTLScriptStart)));
1001  if (nNextCTLScriptStart >= TextFrameIndex(rText.getLength())
1002  || nNextCTLScriptStart >= nChg)
1003  break;
1004  nCurrentScriptType = ScriptTypeDetector::getCTLScriptType(
1005  rText, sal_Int32(nNextCTLScriptStart));
1006  }
1007  nChg = std::min( nChg, nNextCTLScriptStart );
1008  }
1009 
1010  // special case for dotted circle since it can be used with complex
1011  // before a mark, so we want it associated with the mark's script
1012  if (nChg < TextFrameIndex(rText.getLength()) && nChg > TextFrameIndex(0)
1013  && (i18n::ScriptType::WEAK ==
1014  g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg) - 1)))
1015  {
1016  int8_t nType = u_charType(rText[sal_Int32(nChg)]);
1017  if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
1018  nType == U_COMBINING_SPACING_MARK )
1019  {
1020  m_ScriptChanges.emplace_back(nChg-TextFrameIndex(1), nScript);
1021  }
1022  else
1023  {
1024  m_ScriptChanges.emplace_back(nChg, nScript);
1025  }
1026  }
1027  else
1028  {
1029  m_ScriptChanges.emplace_back(nChg, nScript);
1030  }
1031  ++nCnt;
1032 
1033  // if current script is asian, we search for compressible characters
1034  // in this range
1035  if ( CharCompressType::NONE != aCompEnum &&
1036  i18n::ScriptType::ASIAN == nScript )
1037  {
1038  CompType ePrevState = NONE;
1039  CompType eState = NONE;
1040  TextFrameIndex nPrevChg = nLastCompression;
1041 
1042  while ( nLastCompression < nChg )
1043  {
1044  sal_Unicode cChar = rText[ sal_Int32(nLastCompression) ];
1045 
1046  // examine current character
1047  switch ( cChar )
1048  {
1049  // Left punctuation found
1050  case 0x3008: case 0x300A: case 0x300C: case 0x300E:
1051  case 0x3010: case 0x3014: case 0x3016: case 0x3018:
1052  case 0x301A: case 0x301D:
1053  eState = SPECIAL_LEFT;
1054  break;
1055  // Right punctuation found
1056  case 0x3009: case 0x300B:
1057  case 0x300D: case 0x300F: case 0x3011: case 0x3015:
1058  case 0x3017: case 0x3019: case 0x301B: case 0x301E:
1059  case 0x301F:
1060  eState = SPECIAL_RIGHT;
1061  break;
1062  case 0x3001: case 0x3002: // Fullstop or comma
1063  eState = SPECIAL_MIDDLE ;
1064  break;
1065  default:
1066  eState = ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE;
1067  }
1068 
1069  // insert range of compressible characters
1070  if( ePrevState != eState )
1071  {
1072  if ( ePrevState != NONE )
1073  {
1074  // insert start and type
1075  if ( CharCompressType::PunctuationAndKana == aCompEnum ||
1076  ePrevState != KANA )
1077  {
1078  m_CompressionChanges.emplace_back(nPrevChg,
1079  nLastCompression - nPrevChg, ePrevState);
1080  }
1081  }
1082 
1083  ePrevState = eState;
1084  nPrevChg = nLastCompression;
1085  }
1086 
1087  nLastCompression++;
1088  }
1089 
1090  // we still have to examine last entry
1091  if ( ePrevState != NONE )
1092  {
1093  // insert start and type
1094  if ( CharCompressType::PunctuationAndKana == aCompEnum ||
1095  ePrevState != KANA )
1096  {
1097  m_CompressionChanges.emplace_back(nPrevChg,
1098  nLastCompression - nPrevChg, ePrevState);
1099  }
1100  }
1101  }
1102 
1103  // we search for connecting opportunities (kashida)
1104  else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
1105  {
1106  // sw_redlinehide: this is the only place that uses SwScanner with
1107  // frame text, so we convert to sal_Int32 here
1108  std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfCharM(
1109  [&pMerged](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
1110  {
1111  std::pair<SwTextNode const*, sal_Int32> const pos(
1112  sw::MapViewToModel(*pMerged, TextFrameIndex(nBegin)));
1113  return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, script);
1114  });
1115  std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfChar1(
1116  [&rNode](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
1117  { return rNode.GetLang(nBegin, bNoChar ? 0 : 1, script); });
1118  auto pGetLangOfChar(pMerged ? pGetLangOfCharM : pGetLangOfChar1);
1119  SwScanner aScanner( pGetLangOfChar, rText, nullptr, ModelToViewHelper(),
1120  i18n::WordType::DICTIONARY_WORD,
1121  sal_Int32(nLastKashida), sal_Int32(nChg));
1122 
1123  // the search has to be performed on a per word base
1124  while ( aScanner.NextWord() )
1125  {
1126  const OUString& rWord = aScanner.GetWord();
1127 
1128  sal_Int32 nIdx = 0;
1129  sal_Int32 nKashidaPos = -1;
1130  sal_Unicode cCh;
1131  sal_Unicode cPrevCh = 0;
1132 
1133  int nPriorityLevel = 7; // 0..6 = level found
1134  // 7 not found
1135 
1136  sal_Int32 nWordLen = rWord.getLength();
1137 
1138  // ignore trailing vowel chars
1139  while( nWordLen && isTransparentChar( rWord[ nWordLen - 1 ] ))
1140  --nWordLen;
1141 
1142  while (nIdx < nWordLen)
1143  {
1144  cCh = rWord[ nIdx ];
1145 
1146  // 1. Priority:
1147  // after user inserted kashida
1148  if ( 0x640 == cCh )
1149  {
1150  nKashidaPos = aScanner.GetBegin() + nIdx;
1151  nPriorityLevel = 0;
1152  }
1153 
1154  // 2. Priority:
1155  // after a Seen or Sad
1156  if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
1157  {
1158  if( isSeenOrSadChar( cCh )
1159  && (rWord[ nIdx+1 ] != 0x200C) ) // #i98410#: prevent ZWNJ expansion
1160  {
1161  nKashidaPos = aScanner.GetBegin() + nIdx;
1162  nPriorityLevel = 1;
1163  }
1164  }
1165 
1166  // 3. Priority:
1167  // before final form of Teh Marbuta, Heh, Dal
1168  if ( nPriorityLevel >= 2 && nIdx > 0 )
1169  {
1170  if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
1171  isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
1172  ( isHehChar ( cCh ) && nIdx == nWordLen - 1)) // Heh (dual joining) only at end of word
1173  {
1174 
1175  SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1176  // check if character is connectable to previous character,
1177  if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1178  {
1179  nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1180  nPriorityLevel = 2;
1181  }
1182  }
1183  }
1184 
1185  // 4. Priority:
1186  // before final form of Alef, Tah, Lam, Kaf or Gaf
1187  if ( nPriorityLevel >= 3 && nIdx > 0 )
1188  {
1189  if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
1190  (( isLamChar ( cCh ) || // Lam,
1191  isTahChar ( cCh ) || // Tah,
1192  isKafChar ( cCh ) || // Kaf (all dual joining)
1193  isGafChar ( cCh ) )
1194  && nIdx == nWordLen - 1)) // only at end of word
1195  {
1196  SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1197  // check if character is connectable to previous character,
1198  if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1199  {
1200  nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1201  nPriorityLevel = 3;
1202  }
1203  }
1204  }
1205 
1206  // 5. Priority:
1207  // before medial Beh-like
1208  if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
1209  {
1210  if ( isBehChar ( cCh ) )
1211  {
1212  // check if next character is Reh or Yeh-like
1213  sal_Unicode cNextCh = rWord[ nIdx + 1 ];
1214  if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
1215  {
1216  SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1217  // check if character is connectable to previous character,
1218  if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1219  {
1220  nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1221  nPriorityLevel = 4;
1222  }
1223  }
1224  }
1225  }
1226 
1227  // 6. Priority:
1228  // before the final form of Waw, Ain, Qaf and Feh
1229  if ( nPriorityLevel >= 5 && nIdx > 0 )
1230  {
1231  if ( isWawChar ( cCh ) || // Wav (right joining)
1232  // final form may appear in the middle of word
1233  (( isAinChar ( cCh ) || // Ain (dual joining)
1234  isQafChar ( cCh ) || // Qaf (dual joining)
1235  isFehChar ( cCh ) ) // Feh (dual joining)
1236  && nIdx == nWordLen - 1)) // only at end of word
1237  {
1238  SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1239  // check if character is connectable to previous character,
1240  if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1241  {
1242  nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1243  nPriorityLevel = 5;
1244  }
1245  }
1246  }
1247 
1248  // other connecting possibilities
1249  if ( nPriorityLevel >= 6 && nIdx > 0 )
1250  {
1251  // Reh, Zain
1252  if ( isRehChar ( cCh ) )
1253  {
1254  SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
1255  // check if character is connectable to previous character,
1256  if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1257  {
1258  nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1259  nPriorityLevel = 6;
1260  }
1261  }
1262  }
1263 
1264  // Do not consider vowel marks when checking if a character
1265  // can be connected to previous character.
1266  if ( !isTransparentChar ( cCh) )
1267  cPrevCh = cCh;
1268 
1269  ++nIdx;
1270  } // end of current word
1271 
1272  if ( -1 != nKashidaPos )
1273  {
1274  m_Kashida.insert(m_Kashida.begin() + nCntKash, TextFrameIndex(nKashidaPos));
1275  nCntKash++;
1276  }
1277  } // end of kashida search
1278  }
1279 
1280  if (nChg < TextFrameIndex(rText.getLength()))
1281  nScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)));
1282 
1283  nLastCompression = nChg;
1284  nLastKashida = nChg;
1285  }
1286 
1287 #if OSL_DEBUG_LEVEL > 0
1288  // check kashida data
1289  TextFrameIndex nTmpKashidaPos(-1);
1290  bool bWrongKash = false;
1291  for (size_t i = 0; i < m_Kashida.size(); ++i)
1292  {
1293  TextFrameIndex nCurrKashidaPos = GetKashida( i );
1294  if ( nCurrKashidaPos <= nTmpKashidaPos )
1295  {
1296  bWrongKash = true;
1297  break;
1298  }
1299  nTmpKashidaPos = nCurrKashidaPos;
1300  }
1301  SAL_WARN_IF( bWrongKash, "sw.core", "Kashida array contains wrong data" );
1302 #endif
1303 
1304  // remove invalid entries from direction information arrays
1305  m_DirectionChanges.clear();
1306 
1307  // Perform Unicode Bidi Algorithm for text direction information
1308  {
1309  UpdateBidiInfo( rText );
1310 
1311  // #i16354# Change script type for RTL text to CTL:
1312  // 1. All text in RTL runs will use the CTL font
1313  // #i89825# change the script type also to CTL (hennerdrewes)
1314  // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
1315  for (size_t nDirIdx = 0; nDirIdx < m_DirectionChanges.size(); ++nDirIdx)
1316  {
1317  const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
1318  // nStart is start of RTL run:
1319  const TextFrameIndex nStart = nDirIdx > 0 ? GetDirChg(nDirIdx - 1) : TextFrameIndex(0);
1320  // nEnd is end of RTL run:
1321  const TextFrameIndex nEnd = GetDirChg( nDirIdx );
1322 
1323  if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
1324  (nCurrDirType > UBIDI_LTR && // non-strong text in embedded LTR run
1325  !lcl_HasStrongLTR(rText, sal_Int32(nStart), sal_Int32(nEnd))))
1326  {
1327  // nScriptIdx points into the ScriptArrays:
1328  size_t nScriptIdx = 0;
1329 
1330  // Skip entries in ScriptArray which are not inside the RTL run:
1331  // Make nScriptIdx become the index of the script group with
1332  // 1. nStartPosOfGroup <= nStart and
1333  // 2. nEndPosOfGroup > nStart
1334  while ( GetScriptChg( nScriptIdx ) <= nStart )
1335  ++nScriptIdx;
1336 
1337  const TextFrameIndex nStartPosOfGroup = nScriptIdx
1338  ? GetScriptChg(nScriptIdx - 1)
1339  : TextFrameIndex(0);
1340  const sal_uInt8 nScriptTypeOfGroup = GetScriptType( nScriptIdx );
1341 
1342  SAL_WARN_IF( nStartPosOfGroup > nStart || GetScriptChg( nScriptIdx ) <= nStart,
1343  "sw.core", "Script override with CTL font trouble" );
1344 
1345  // Check if we have to insert a new script change at
1346  // position nStart. If nStartPosOfGroup < nStart,
1347  // we have to insert a new script change:
1348  if (nStart > TextFrameIndex(0) && nStartPosOfGroup < nStart)
1349  {
1350  m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
1351  ScriptChangeInfo(nStart, nScriptTypeOfGroup) );
1352  ++nScriptIdx;
1353  }
1354 
1355  // Remove entries in ScriptArray which end inside the RTL run:
1356  while (nScriptIdx < m_ScriptChanges.size()
1357  && GetScriptChg(nScriptIdx) <= nEnd)
1358  {
1359  m_ScriptChanges.erase(m_ScriptChanges.begin() + nScriptIdx);
1360  }
1361 
1362  // Insert a new entry in ScriptArray for the end of the RTL run:
1363  m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
1364  ScriptChangeInfo(nEnd, i18n::ScriptType::COMPLEX) );
1365 
1366 #if OSL_DEBUG_LEVEL > 1
1367  // Check that ScriptChangeInfos are in increasing order of
1368  // position and that we don't have "empty" changes.
1369  sal_uInt8 nLastTyp = i18n::ScriptType::WEAK;
1370  TextFrameIndex nLastPos = TextFrameIndex(0);
1371  for (const auto& rScriptChange : m_ScriptChanges)
1372  {
1373  SAL_WARN_IF( nLastTyp == rScriptChange.type ||
1374  nLastPos >= rScriptChange.position,
1375  "sw.core", "Heavy InitScriptType() confusion" );
1376  nLastPos = rScriptChange.position;
1377  nLastTyp = rScriptChange.type;
1378  }
1379 #endif
1380  }
1381  }
1382  }
1383 }
1384 
1385 void SwScriptInfo::UpdateBidiInfo( const OUString& rText )
1386 {
1387  // remove invalid entries from direction information arrays
1388  m_DirectionChanges.clear();
1389 
1390  // Bidi functions from icu 2.0
1391 
1392  UErrorCode nError = U_ZERO_ERROR;
1393  UBiDi* pBidi = ubidi_openSized( rText.getLength(), 0, &nError );
1394  nError = U_ZERO_ERROR;
1395 
1396  ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(),
1397  m_nDefaultDir, nullptr, &nError );
1398  nError = U_ZERO_ERROR;
1399  int nCount = ubidi_countRuns( pBidi, &nError );
1400  int32_t nStart = 0;
1401  int32_t nEnd;
1402  UBiDiLevel nCurrDir;
1403  for ( int nIdx = 0; nIdx < nCount; ++nIdx )
1404  {
1405  ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
1406  m_DirectionChanges.emplace_back(TextFrameIndex(nEnd), nCurrDir);
1407  nStart = nEnd;
1408  }
1409 
1410  ubidi_close( pBidi );
1411 }
1412 
1413 // returns the position of the next character which belongs to another script
1414 // than the character of the actual (input) position.
1415 // If there's no script change until the end of the paragraph, it will return
1416 // COMPLETE_STRING.
1417 // Scripts are Asian (Chinese, Japanese, Korean),
1418 // Latin ( English etc.)
1419 // and Complex ( Hebrew, Arabian )
1421 {
1422  const size_t nEnd = CountScriptChg();
1423  for( size_t nX = 0; nX < nEnd; ++nX )
1424  {
1425  if( nPos < GetScriptChg( nX ) )
1426  return GetScriptChg( nX );
1427  }
1428 
1430 }
1431 
1432 // returns the script of the character at the input position
1433 sal_Int16 SwScriptInfo::ScriptType(const TextFrameIndex nPos) const
1434 {
1435  const size_t nEnd = CountScriptChg();
1436  for( size_t nX = 0; nX < nEnd; ++nX )
1437  {
1438  if( nPos < GetScriptChg( nX ) )
1439  return GetScriptType( nX );
1440  }
1441 
1442  // the default is the application language script
1444 }
1445 
1447  const sal_uInt8* pLevel ) const
1448 {
1449  const sal_uInt8 nCurrDir = pLevel ? *pLevel : 62;
1450  const size_t nEnd = CountDirChg();
1451  for( size_t nX = 0; nX < nEnd; ++nX )
1452  {
1453  if( nPos < GetDirChg( nX ) &&
1454  ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
1455  return GetDirChg( nX );
1456  }
1457 
1459 }
1460 
1462 {
1463  const size_t nEnd = CountDirChg();
1464  for( size_t nX = 0; nX < nEnd; ++nX )
1465  {
1466  if( nPos < GetDirChg( nX ) )
1467  return GetDirType( nX );
1468  }
1469 
1470  return 0;
1471 }
1472 
1474 {
1475  for (auto const& it : m_HiddenChg)
1476  {
1477  if (nPos < it)
1478  {
1479  return it;
1480  }
1481  }
1483 }
1484 
1485 // Takes a string and replaced the hidden ranges with cChar.
1486 sal_Int32 SwScriptInfo::MaskHiddenRanges( const SwTextNode& rNode, OUStringBuffer & rText,
1487  const sal_Int32 nStt, const sal_Int32 nEnd,
1488  const sal_Unicode cChar )
1489 {
1490  assert(rNode.GetText().getLength() == rText.getLength());
1491 
1492  std::vector<sal_Int32> aList;
1493  sal_Int32 nHiddenStart;
1494  sal_Int32 nHiddenEnd;
1495  sal_Int32 nNumOfHiddenChars = 0;
1496  GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1497  auto rFirst( aList.crbegin() );
1498  auto rLast( aList.crend() );
1499  while ( rFirst != rLast )
1500  {
1501  nHiddenEnd = *(rFirst++);
1502  nHiddenStart = *(rFirst++);
1503 
1504  if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
1505  continue;
1506 
1507  while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
1508  {
1509  if (nHiddenStart >= nStt)
1510  {
1511  rText[nHiddenStart] = cChar;
1512  ++nNumOfHiddenChars;
1513  }
1514  ++nHiddenStart;
1515  }
1516  }
1517 
1518  return nNumOfHiddenChars;
1519 }
1520 
1521 // Takes a SwTextNode and deletes the hidden ranges from the node.
1523 {
1524  std::vector<sal_Int32> aList;
1525  sal_Int32 nHiddenStart;
1526  sal_Int32 nHiddenEnd;
1527  GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1528  auto rFirst( aList.crbegin() );
1529  auto rLast( aList.crend() );
1530  while ( rFirst != rLast )
1531  {
1532  nHiddenEnd = *(rFirst++);
1533  nHiddenStart = *(rFirst++);
1534 
1535  SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd );
1537  }
1538 }
1539 
1540 bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTextNode& rNode, sal_Int32 nPos,
1541  sal_Int32& rnStartPos, sal_Int32& rnEndPos,
1542  std::vector<sal_Int32>* pList )
1543 {
1544  rnStartPos = COMPLETE_STRING;
1545  rnEndPos = 0;
1546 
1547  bool bNewContainsHiddenChars = false;
1548 
1549  // Optimization: First examine the flags at the text node:
1550 
1551  if ( !rNode.IsCalcHiddenCharFlags() )
1552  {
1553  bool bWholePara = rNode.HasHiddenCharAttribute( true );
1554  bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
1555  if ( !bContainsHiddenChars )
1556  return false;
1557 
1558  if ( bWholePara )
1559  {
1560  if ( pList )
1561  {
1562  pList->push_back( 0 );
1563  pList->push_back(rNode.GetText().getLength());
1564  }
1565 
1566  rnStartPos = 0;
1567  rnEndPos = rNode.GetText().getLength();
1568  return true;
1569  }
1570  }
1571 
1572  // sw_redlinehide: this won't work if it's merged
1573 #if 0
1574  const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
1575  if ( pSI )
1576  {
1577 
1578  // Check first, if we have a valid SwScriptInfo object for this text node:
1579 
1580  bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
1581  const bool bNewHiddenCharsHidePara =
1582  rnStartPos == 0 && rnEndPos >= rNode.GetText().getLength();
1583  rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
1584  }
1585  else
1586 #endif
1587  {
1588 
1589  // No valid SwScriptInfo Object, we have to do it the hard way:
1590 
1591  Range aRange(0, (!rNode.GetText().isEmpty())
1592  ? rNode.GetText().getLength() - 1
1593  : 0);
1594  MultiSelection aHiddenMulti( aRange );
1595  SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti );
1596  for( sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1597  {
1598  const Range& rRange = aHiddenMulti.GetRange( i );
1599  const sal_Int32 nHiddenStart = rRange.Min();
1600  const sal_Int32 nHiddenEnd = rRange.Max() + 1;
1601 
1602  if ( nHiddenStart > nPos )
1603  break;
1604  if (nPos < nHiddenEnd)
1605  {
1606  rnStartPos = nHiddenStart;
1607  rnEndPos = std::min<sal_Int32>(nHiddenEnd,
1608  rNode.GetText().getLength());
1609  break;
1610  }
1611  }
1612 
1613  if ( pList )
1614  {
1615  for( sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1616  {
1617  const Range& rRange = aHiddenMulti.GetRange( i );
1618  pList->push_back( rRange.Min() );
1619  pList->push_back( rRange.Max() + 1 );
1620  }
1621  }
1622 
1623  bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
1624  }
1625 
1626  return bNewContainsHiddenChars;
1627 }
1628 
1630  TextFrameIndex & rnStartPos, TextFrameIndex & rnEndPos) const
1631 {
1632  rnStartPos = TextFrameIndex(COMPLETE_STRING);
1633  rnEndPos = TextFrameIndex(0);
1634 
1635  const size_t nEnd = CountHiddenChg();
1636  for( size_t nX = 0; nX < nEnd; ++nX )
1637  {
1638  const TextFrameIndex nHiddenStart = GetHiddenChg( nX++ );
1639  const TextFrameIndex nHiddenEnd = GetHiddenChg( nX );
1640 
1641  if ( nHiddenStart > nPos )
1642  break;
1643  if (nPos < nHiddenEnd)
1644  {
1645  rnStartPos = nHiddenStart;
1646  rnEndPos = nHiddenEnd;
1647  break;
1648  }
1649  }
1650 
1651  return CountHiddenChg() > 0;
1652 }
1653 
1654 bool SwScriptInfo::IsInHiddenRange( const SwTextNode& rNode, sal_Int32 nPos )
1655 {
1656  sal_Int32 nStartPos;
1657  sal_Int32 nEndPos;
1658  SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
1659  return nStartPos != COMPLETE_STRING;
1660 }
1661 
1662 #ifdef DBG_UTIL
1663 // returns the type of the compressed character
1665 {
1666  const size_t nEnd = CountCompChg();
1667  for( size_t nX = 0; nX < nEnd; ++nX )
1668  {
1669  const TextFrameIndex nChg = GetCompStart(nX);
1670 
1671  if ( nPos < nChg )
1672  return NONE;
1673 
1674  if( nPos < nChg + GetCompLen( nX ) )
1675  return GetCompType( nX );
1676  }
1677  return NONE;
1678 }
1679 #endif
1680 
1681 // returns, if there are compressible kanas or specials
1682 // between nStart and nEnd
1683 size_t SwScriptInfo::HasKana(TextFrameIndex const nStart, TextFrameIndex const nLen) const
1684 {
1685  const size_t nCnt = CountCompChg();
1686  TextFrameIndex nEnd = nStart + nLen;
1687 
1688  for( size_t nX = 0; nX < nCnt; ++nX )
1689  {
1690  TextFrameIndex nKanaStart = GetCompStart(nX);
1691  TextFrameIndex nKanaEnd = nKanaStart + GetCompLen(nX);
1692 
1693  if ( nKanaStart >= nEnd )
1694  return SAL_MAX_SIZE;
1695 
1696  if ( nStart < nKanaEnd )
1697  return nX;
1698  }
1699 
1700  return SAL_MAX_SIZE;
1701 }
1702 
1703 long SwScriptInfo::Compress(long* pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen,
1704  const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
1705  bool bCenter,
1706  Point* pPoint ) const
1707 {
1708  SAL_WARN_IF( !nCompress, "sw.core", "Compression without compression?!" );
1709  SAL_WARN_IF( !nLen, "sw.core", "Compression without text?!" );
1710  const size_t nCompCount = CountCompChg();
1711 
1712  // In asian typography, there are full width and half width characters.
1713  // Full width punctuation characters can be compressed by 50%
1714  // to determine this, we compare the font width with 75% of its height
1715  const long nMinWidth = ( 3 * nFontHeight ) / 4;
1716 
1717  size_t nCompIdx = HasKana( nIdx, nLen );
1718 
1719  if ( SAL_MAX_SIZE == nCompIdx )
1720  return 0;
1721 
1722  TextFrameIndex nChg = GetCompStart( nCompIdx );
1723  TextFrameIndex nCompLen = GetCompLen( nCompIdx );
1724  sal_Int32 nI = 0;
1725  nLen += nIdx;
1726 
1727  if( nChg > nIdx )
1728  {
1729  nI = sal_Int32(nChg - nIdx);
1730  nIdx = nChg;
1731  }
1732  else if( nIdx < nChg + nCompLen )
1733  nCompLen -= nIdx - nChg;
1734 
1735  if( nIdx > nLen || nCompIdx >= nCompCount )
1736  return 0;
1737 
1738  long nSub = 0;
1739  long nLast = nI ? pKernArray[ nI - 1 ] : 0;
1740  do
1741  {
1742  const CompType nType = GetCompType( nCompIdx );
1743 #ifdef DBG_UTIL
1744  SAL_WARN_IF( nType != DbgCompType( nIdx ), "sw.core", "Gimme the right type!" );
1745 #endif
1746  nCompLen += nIdx;
1747  if( nCompLen > nLen )
1748  nCompLen = nLen;
1749 
1750  // are we allowed to compress the character?
1751  if ( pKernArray[ nI ] - nLast < nMinWidth )
1752  {
1753  nIdx++; nI++;
1754  }
1755  else
1756  {
1757  while( nIdx < nCompLen )
1758  {
1759  SAL_WARN_IF( SwScriptInfo::NONE == nType, "sw.core", "None compression?!" );
1760 
1761  // nLast is width of current character
1762  nLast -= pKernArray[ nI ];
1763 
1764  nLast *= nCompress;
1765  long nMove = 0;
1766  if( SwScriptInfo::KANA != nType )
1767  {
1768  nLast /= 24000;
1769  if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
1770  {
1771  if( nI )
1772  nMove = nLast;
1773  else
1774  {
1775  pPoint->AdjustX(nLast );
1776  nLast = 0;
1777  }
1778  }
1779  else if( bCenter && SwScriptInfo::SPECIAL_MIDDLE == nType )
1780  nMove = nLast / 2;
1781  }
1782  else
1783  nLast /= 100000;
1784  nSub -= nLast;
1785  nLast = pKernArray[ nI ];
1786  if( nI && nMove )
1787  pKernArray[ nI - 1 ] += nMove;
1788  pKernArray[ nI++ ] -= nSub;
1789  ++nIdx;
1790  }
1791  }
1792 
1793  if( nIdx >= nLen )
1794  break;
1795 
1796  TextFrameIndex nTmpChg = nLen;
1797  if( ++nCompIdx < nCompCount )
1798  {
1799  nTmpChg = GetCompStart( nCompIdx );
1800  if( nTmpChg > nLen )
1801  nTmpChg = nLen;
1802  nCompLen = GetCompLen( nCompIdx );
1803  }
1804 
1805  while( nIdx < nTmpChg )
1806  {
1807  nLast = pKernArray[ nI ];
1808  pKernArray[ nI++ ] -= nSub;
1809  ++nIdx;
1810  }
1811  } while( nIdx < nLen );
1812  return nSub;
1813 }
1814 
1815 // Note on calling KashidaJustify():
1816 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
1817 // total number of kashida positions, or the number of kashida positions after some positions
1818 // have been dropped, depending on the state of the m_KashidaInvalid set.
1819 
1820 sal_Int32 SwScriptInfo::KashidaJustify( long* pKernArray,
1821  long* pScrArray,
1822  TextFrameIndex const nStt,
1823  TextFrameIndex const nLen,
1824  long nSpaceAdd ) const
1825 {
1826  SAL_WARN_IF( !nLen, "sw.core", "Kashida justification without text?!" );
1827 
1828  if( !IsKashidaLine(nStt))
1829  return -1;
1830 
1831  // evaluate kashida information in collected in SwScriptInfo
1832 
1833  size_t nCntKash = 0;
1834  while( nCntKash < CountKashida() )
1835  {
1836  if ( nStt <= GetKashida( nCntKash ) )
1837  break;
1838  ++nCntKash;
1839  }
1840 
1841  const TextFrameIndex nEnd = nStt + nLen;
1842 
1843  size_t nCntKashEnd = nCntKash;
1844  while ( nCntKashEnd < CountKashida() )
1845  {
1846  if ( nEnd <= GetKashida( nCntKashEnd ) )
1847  break;
1848  ++nCntKashEnd;
1849  }
1850 
1851  size_t nActualKashCount = nCntKashEnd - nCntKash;
1852  for (size_t i = nCntKash; i < nCntKashEnd; ++i)
1853  {
1854  if ( nActualKashCount && !IsKashidaValid ( i ) )
1855  --nActualKashCount;
1856  }
1857 
1858  if ( !pKernArray )
1859  return nActualKashCount;
1860 
1861  // do nothing if there is no more kashida
1862  if ( nCntKash < CountKashida() )
1863  {
1864  // skip any invalid kashidas
1865  while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
1866  ++nCntKash;
1867 
1868  TextFrameIndex nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash)
1869  ? GetKashida(nCntKash)
1870  : nEnd;
1871  long nKashAdd = nSpaceAdd;
1872 
1873  while ( nIdx < nEnd )
1874  {
1875  TextFrameIndex nArrayPos = nIdx - nStt;
1876 
1877  // next kashida position
1878  ++nCntKash;
1879  while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
1880  ++nCntKash;
1881 
1882  nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash) ? GetKashida(nCntKash) : nEnd;
1883  if ( nIdx > nEnd )
1884  nIdx = nEnd;
1885 
1886  const TextFrameIndex nArrayEnd = nIdx - nStt;
1887 
1888  while ( nArrayPos < nArrayEnd )
1889  {
1890  pKernArray[ sal_Int32(nArrayPos) ] += nKashAdd;
1891  if ( pScrArray )
1892  pScrArray[ sal_Int32(nArrayPos) ] += nKashAdd;
1893  ++nArrayPos;
1894  }
1895  nKashAdd += nSpaceAdd;
1896  }
1897  }
1898 
1899  return 0;
1900 }
1901 
1902 // Checks if the current text is 'Arabic' text. Note that only the first
1903 // character has to be checked because a ctl portion only contains one
1904 // script, see NewTextPortion
1905 bool SwScriptInfo::IsArabicText(const OUString& rText,
1906  TextFrameIndex const nStt, TextFrameIndex const nLen)
1907 {
1908  using namespace ::com::sun::star::i18n;
1909  static const ScriptTypeList typeList[] = {
1910  { UnicodeScript_kArabic, UnicodeScript_kArabic, sal_Int16(UnicodeScript_kArabic) }, // 11,
1911  { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, sal_Int16(UnicodeScript_kScriptCount) } // 88
1912  };
1913 
1914  // go forward if current position does not hold a regular character:
1915  const CharClass& rCC = GetAppCharClass();
1916  sal_Int32 nIdx = sal_Int32(nStt);
1917  const sal_Int32 nEnd = sal_Int32(nStt + nLen);
1918  while ( nIdx < nEnd && !rCC.isLetterNumeric( rText, nIdx ) )
1919  {
1920  ++nIdx;
1921  }
1922 
1923  if( nIdx == nEnd )
1924  {
1925  // no regular character found in this portion. Go backward:
1926  --nIdx;
1927  while ( nIdx >= 0 && !rCC.isLetterNumeric( rText, nIdx ) )
1928  {
1929  --nIdx;
1930  }
1931  }
1932 
1933  if( nIdx >= 0 )
1934  {
1935  const sal_Unicode cCh = rText[nIdx];
1936  const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, sal_Int16(UnicodeScript_kScriptCount) );
1937  return type == sal_Int16(UnicodeScript_kArabic);
1938  }
1939  return false;
1940 }
1941 
1942 bool SwScriptInfo::IsKashidaValid(size_t const nKashPos) const
1943 {
1944  return m_KashidaInvalid.find(nKashPos) == m_KashidaInvalid.end();
1945 }
1946 
1947 void SwScriptInfo::ClearKashidaInvalid(size_t const nKashPos)
1948 {
1949  m_KashidaInvalid.erase(nKashPos);
1950 }
1951 
1952 // bMark == true:
1953 // marks the first valid kashida in the given text range as invalid
1954 // bMark == false:
1955 // clears all kashida invalid flags in the given text range
1957  TextFrameIndex const nStt, TextFrameIndex const nLen,
1958  bool bMark, sal_Int32 nMarkCount)
1959 {
1960  size_t nCntKash = 0;
1961  while( nCntKash < CountKashida() )
1962  {
1963  if ( nStt <= GetKashida( nCntKash ) )
1964  break;
1965  nCntKash++;
1966  }
1967 
1968  const TextFrameIndex nEnd = nStt + nLen;
1969 
1970  while ( nCntKash < CountKashida() )
1971  {
1972  if ( nEnd <= GetKashida( nCntKash ) )
1973  break;
1974  if(bMark)
1975  {
1976  if ( MarkKashidaInvalid ( nCntKash ) )
1977  {
1978  --nMarkCount;
1979  if (!nMarkCount)
1980  return true;
1981  }
1982  }
1983  else
1984  {
1985  ClearKashidaInvalid ( nCntKash );
1986  }
1987  nCntKash++;
1988  }
1989  return false;
1990 }
1991 
1992 bool SwScriptInfo::MarkKashidaInvalid(size_t const nKashPos)
1993 {
1994  return m_KashidaInvalid.insert(nKashPos).second;
1995 }
1996 
1997 // retrieve the kashida positions in the given text range
1999  TextFrameIndex const nStt, TextFrameIndex const nLen,
2000  std::vector<TextFrameIndex>& rKashidaPosition)
2001 {
2002  size_t nCntKash = 0;
2003  while( nCntKash < CountKashida() )
2004  {
2005  if ( nStt <= GetKashida( nCntKash ) )
2006  break;
2007  nCntKash++;
2008  }
2009 
2010  const TextFrameIndex nEnd = nStt + nLen;
2011 
2012  size_t nCntKashEnd = nCntKash;
2013  while ( nCntKashEnd < CountKashida() )
2014  {
2015  if ( nEnd <= GetKashida( nCntKashEnd ) )
2016  break;
2017  rKashidaPosition.push_back(GetKashida(nCntKashEnd));
2018  nCntKashEnd++;
2019  }
2020 }
2021 
2023 {
2024  m_NoKashidaLine.push_back( nStt );
2025  m_NoKashidaLineEnd.push_back( nStt + nLen );
2026 }
2027 
2028 // determines if the line uses kashida justification
2030 {
2031  for (size_t i = 0; i < m_NoKashidaLine.size(); ++i)
2032  {
2033  if (nCharIdx >= m_NoKashidaLine[i] && nCharIdx < m_NoKashidaLineEnd[i])
2034  return false;
2035  }
2036  return true;
2037 }
2038 
2040 {
2041  size_t i = 0;
2042  while (i < m_NoKashidaLine.size())
2043  {
2044  if (nStt + nLen >= m_NoKashidaLine[i] && nStt < m_NoKashidaLineEnd[i])
2045  {
2046  m_NoKashidaLine.erase(m_NoKashidaLine.begin() + i);
2047  m_NoKashidaLineEnd.erase(m_NoKashidaLineEnd.begin() + i);
2048  }
2049  else
2050  ++i;
2051  }
2052 }
2053 
2054 // mark the given character indices as invalid kashida positions
2055 void SwScriptInfo::MarkKashidasInvalid(sal_Int32 const nCnt,
2056  const TextFrameIndex* pKashidaPositions)
2057 {
2058  SAL_WARN_IF( !pKashidaPositions || nCnt == 0, "sw.core", "Where are kashidas?" );
2059 
2060  size_t nCntKash = 0;
2061  sal_Int32 nKashidaPosIdx = 0;
2062 
2063  while (nCntKash < CountKashida() && nKashidaPosIdx < nCnt)
2064  {
2065  if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
2066  {
2067  ++nCntKash;
2068  continue;
2069  }
2070 
2071  if ( pKashidaPositions [nKashidaPosIdx] != GetKashida( nCntKash ) || !IsKashidaValid ( nCntKash ) )
2072  return; // something is wrong
2073 
2074  MarkKashidaInvalid ( nCntKash );
2075  nKashidaPosIdx++;
2076  }
2077 }
2078 
2079 TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, long* pKernArray,
2080  long* pScrArray, TextFrameIndex const nStt,
2081  TextFrameIndex const nLen,
2082  TextFrameIndex nNumberOfBlanks,
2083  long nSpaceAdd )
2084 {
2085  SAL_WARN_IF( nStt + nLen > TextFrameIndex(rText.getLength()), "sw.core", "String in ThaiJustify too small" );
2086 
2087  SwTwips nNumOfTwipsToDistribute = nSpaceAdd * sal_Int32(nNumberOfBlanks) /
2089 
2090  long nSpaceSum = 0;
2091  TextFrameIndex nCnt(0);
2092 
2093  for (sal_Int32 nI = 0; nI < sal_Int32(nLen); ++nI)
2094  {
2095  const sal_Unicode cCh = rText[sal_Int32(nStt) + nI];
2096 
2097  // check if character is not above or below base
2098  if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
2099  ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 )
2100  {
2101  if (nNumberOfBlanks > TextFrameIndex(0))
2102  {
2103  nSpaceAdd = nNumOfTwipsToDistribute / sal_Int32(nNumberOfBlanks);
2104  --nNumberOfBlanks;
2105  nNumOfTwipsToDistribute -= nSpaceAdd;
2106  }
2107  nSpaceSum += nSpaceAdd;
2108  ++nCnt;
2109  }
2110 
2111  if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
2112  if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
2113  }
2114 
2115  return nCnt;
2116 }
2117 
2119  SwTextFrame const**const o_ppFrame,
2120  bool const bAllowInvalid)
2121 {
2123  SwScriptInfo* pScriptInfo = nullptr;
2124 
2125  for( SwTextFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
2126  {
2127  pScriptInfo = const_cast<SwScriptInfo*>(pLast->GetScriptInfo());
2128  if ( pScriptInfo )
2129  {
2130  if (bAllowInvalid ||
2131  TextFrameIndex(COMPLETE_STRING) == pScriptInfo->GetInvalidityA())
2132  {
2133  if (o_ppFrame)
2134  {
2135  *o_ppFrame = pLast;
2136  }
2137  break;
2138  }
2139  pScriptInfo = nullptr;
2140  }
2141  }
2142 
2143  return pScriptInfo;
2144 }
2145 
2147 {
2148  FormatReset();
2149  m_bFlys = m_bFootnoteNum = m_bMargin = false;
2151 }
2152 
2154 {
2155 }
2156 
2158 {
2159  TextFrameIndex nLen(0);
2160  const SwLineLayout *pLay = this;
2161  while( pLay )
2162  {
2163  nLen += pLay->GetLen();
2164  pLay = pLay->GetNext();
2165  }
2166  return nLen;
2167 }
2168 
2170 {
2171  const SwLineLayout *pLay = this;
2172  while( pLay && pLay->IsDummy() )
2173  pLay = pLay->GetNext();
2174  while( pLay )
2175  {
2176  const SwLinePortion *pPos = pLay->GetNextPortion();
2177  while ( pPos && !pPos->GetLen() )
2178  pPos = pPos->GetNextPortion();
2179  if( pPos && pPos->IsDropPortion() )
2180  return static_cast<const SwDropPortion *>(pPos);
2181  pLay = pLay->GetLen() ? nullptr : pLay->GetNext();
2182  }
2183  return nullptr;
2184 }
2185 
2186 void SwLineLayout::Init( SwLinePortion* pNextPortion )
2187 {
2188  Height( 0 );
2189  Width( 0 );
2190  SetLen(TextFrameIndex(0));
2191  SetAscent( 0 );
2192  SetRealHeight( 0 );
2193  SetNextPortion( pNextPortion );
2194 }
2195 
2196 // looks for hanging punctuation portions in the paragraph
2197 // and return the maximum right offset of them.
2198 // If no such portion is found, the Margin/Hanging-flags will be updated.
2200 {
2201  SwLinePortion* pPor = GetNextPortion();
2202  bool bFound = false;
2203  SwTwips nDiff = 0;
2204  while( pPor)
2205  {
2206  if( pPor->IsHangingPortion() )
2207  {
2208  nDiff = static_cast<SwHangingPortion*>(pPor)->GetInnerWidth() - pPor->Width();
2209  if( nDiff )
2210  bFound = true;
2211  }
2212  // the last post its portion
2213  else if ( pPor->IsPostItsPortion() && ! pPor->GetNextPortion() )
2214  nDiff = nAscent;
2215 
2216  pPor = pPor->GetNextPortion();
2217  }
2218  if( !bFound ) // update the hanging-flag
2219  const_cast<SwLineLayout*>(this)->SetHanging( false );
2220  return nDiff;
2221 }
2222 
2224 {
2225  SAL_WARN_IF( !HasPara(), "sw.core", "Don't call me without a paraportion" );
2226  if( !GetPara()->IsMargin() )
2227  return 0;
2228  const SwLineLayout* pLine = GetPara();
2229  SwTwips nRet = 0;
2230  do
2231  {
2232  SwTwips nDiff = pLine->GetHangingMargin();
2233  if( nDiff > nRet )
2234  nRet = nDiff;
2235  pLine = pLine->GetNext();
2236  } while ( pLine );
2237  if( !nRet ) // update the margin-flag
2238  const_cast<SwParaPortion*>(GetPara())->SetMargin( false );
2239  return nRet;
2240 }
2241 
2243 {
2244  assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2245  || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
2246 
2247  const SfxPoolItem* pItem = nullptr;
2248  if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) &&
2249  static_cast<const SvxCharHiddenItem*>(pItem)->GetValue() )
2250  {
2251  rHiddenMulti.SelectAll();
2252  }
2253 
2254  const SwpHints* pHints = rNode.GetpSwpHints();
2255 
2256  if( pHints )
2257  {
2258  for( size_t nTmp = 0; nTmp < pHints->Count(); ++nTmp )
2259  {
2260  const SwTextAttr* pTextAttr = pHints->Get( nTmp );
2261  const SvxCharHiddenItem* pHiddenItem = CharFormat::GetItem( *pTextAttr, RES_CHRATR_HIDDEN );
2262  if( pHiddenItem )
2263  {
2264  const sal_Int32 nSt = pTextAttr->GetStart();
2265  const sal_Int32 nEnd = *pTextAttr->End();
2266  if( nEnd > nSt )
2267  {
2268  Range aTmp( nSt, nEnd - 1 );
2269  rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
2270  }
2271  }
2272  }
2273  }
2274 
2275  for (const SwIndex* pIndex = rNode.GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
2276  {
2277  const sw::mark::IMark* pMark = pIndex->GetMark();
2278  const sw::mark::IBookmark* pBookmark = dynamic_cast<const sw::mark::IBookmark*>(pMark);
2279  if (pBookmark && pBookmark->IsHidden())
2280  {
2281  // intersect bookmark range with textnode range and add the intersection to rHiddenMulti
2282 
2283  const sal_Int32 nSt = pBookmark->GetMarkStart().nContent.GetIndex();
2284  const sal_Int32 nEnd = pBookmark->GetMarkEnd().nContent.GetIndex();
2285 
2286  if( nEnd > nSt )
2287  {
2288  Range aTmp( nSt, nEnd - 1 );
2289  rHiddenMulti.Select(aTmp, true);
2290  }
2291  }
2292  }
2293 }
2294 
2295 void SwScriptInfo::selectRedLineDeleted(const SwTextNode& rNode, MultiSelection &rHiddenMulti, bool bSelect)
2296 {
2297  assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
2298  || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
2299 
2300  const IDocumentRedlineAccess& rIDRA = rNode.getIDocumentRedlineAccess();
2302  {
2303  SwRedlineTable::size_type nAct = rIDRA.GetRedlinePos( rNode, RedlineType::Any );
2304 
2305  for ( ; nAct < rIDRA.GetRedlineTable().size(); nAct++ )
2306  {
2307  const SwRangeRedline* pRed = rIDRA.GetRedlineTable()[ nAct ];
2308 
2309  if (pRed->Start()->nNode > rNode.GetIndex())
2310  break;
2311 
2312  if (pRed->GetType() != RedlineType::Delete)
2313  continue;
2314 
2315  sal_Int32 nRedlStart;
2316  sal_Int32 nRedlnEnd;
2317  pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
2318  //clip it if the redline extends past the end of the nodes text
2319  nRedlnEnd = std::min<sal_Int32>(nRedlnEnd, rNode.GetText().getLength());
2320  if ( nRedlnEnd > nRedlStart )
2321  {
2322  Range aTmp( nRedlStart, nRedlnEnd - 1 );
2323  rHiddenMulti.Select( aTmp, bSelect );
2324  }
2325  }
2326  }
2327 }
2328 
2329 // Returns a MultiSection indicating the hidden ranges.
2330 void SwScriptInfo::CalcHiddenRanges( const SwTextNode& rNode, MultiSelection& rHiddenMulti )
2331 {
2332  selectHiddenTextProperty(rNode, rHiddenMulti);
2333 
2334  // If there are any hidden ranges in the current text node, we have
2335  // to unhide the redlining ranges:
2336  selectRedLineDeleted(rNode, rHiddenMulti, false);
2337 
2338  // We calculated a lot of stuff. Finally we can update the flags at the text node.
2339 
2340  const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
2341  bool bNewHiddenCharsHidePara = false;
2342  if ( bNewContainsHiddenChars )
2343  {
2344  const Range& rRange = rHiddenMulti.GetRange( 0 );
2345  const sal_Int32 nHiddenStart = rRange.Min();
2346  const sal_Int32 nHiddenEnd = rRange.Max() + 1;
2347  bNewHiddenCharsHidePara =
2348  (nHiddenStart == 0 && nHiddenEnd >= rNode.GetText().getLength());
2349  }
2350  rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
2351 }
2352 
2354  TextFrameIndex nPos, TextFrameIndex const nEnd, LanguageType aLang)
2355 {
2357  if (nEnd > nPos)
2358  {
2359  sal_Int32 nDone = 0;
2360  const lang::Locale &rLocale = g_pBreakIt->GetLocale( aLang );
2361  while ( nPos < nEnd )
2362  {
2363  nPos = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharacters(
2364  rText, sal_Int32(nPos),
2365  rLocale,
2366  i18n::CharacterIteratorMode::SKIPCELL, 1, nDone));
2367  nCount++;
2368  }
2369  }
2370  else
2371  nCount = nEnd - nPos ;
2372 
2373  return nCount;
2374 }
2375 
2376 void SwScriptInfo::CJKJustify( const OUString& rText, long* pKernArray,
2377  long* pScrArray, TextFrameIndex const nStt,
2378  TextFrameIndex const nLen, LanguageType aLang,
2379  long nSpaceAdd, bool bIsSpaceStop )
2380 {
2381  assert( pKernArray != nullptr && sal_Int32(nStt) >= 0 );
2382  if (sal_Int32(nLen) > 0)
2383  {
2384  long nSpaceSum = 0;
2385  const lang::Locale &rLocale = g_pBreakIt->GetLocale( aLang );
2386  sal_Int32 nDone = 0;
2387  sal_Int32 nNext(nStt);
2388  for ( sal_Int32 nI = 0; nI < sal_Int32(nLen); ++nI )
2389  {
2390  if (nI + sal_Int32(nStt) == nNext)
2391  {
2392  nNext = g_pBreakIt->GetBreakIter()->nextCharacters( rText, nNext,
2393  rLocale,
2394  i18n::CharacterIteratorMode::SKIPCELL, 1, nDone );
2395  if (nNext < sal_Int32(nStt + nLen) || !bIsSpaceStop)
2396  nSpaceSum += nSpaceAdd;
2397  }
2398  pKernArray[ nI ] += nSpaceSum;
2399  if ( pScrArray )
2400  pScrArray[ nI ] += nSpaceSum;
2401  }
2402  }
2403 }
2404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SwAttrHandler & GetAttrHandler()
Definition: itratr.hxx:109
virtual SwLinePortion * Insert(SwLinePortion *pPortion) override
Definition: porlay.cxx:208
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:149
SwFontScript WhichFont(TextFrameIndex nIdx) const
Definition: porlay.cxx:718
sal_uLong GetIndex() const
Definition: node.hxx:282
TextFrameIndex GetInvalidityA() const
Definition: scriptinfo.hxx:113
SwViewShell * GetVsh()
Definition: inftxt.hxx:228
void SetHiddenCharAttribute(bool bNewHiddenCharsHidePara, bool bNewContainsHiddenChars) const
Definition: ndtxt.hxx:150
static sal_Int16 getUnicodeScriptType(const sal_Unicode ch, const ScriptTypeList *typeList, sal_Int16 unknownType=0)
bool IsGrfNumPortion() const
Definition: porlin.hxx:111
SwTwips GetHangingMargin_() const
Definition: porlay.cxx:2199
TextFrameIndex NextScriptChg(TextFrameIndex nPos) const
Definition: porlay.cxx:1420
void Init()
TextFrameIndex nStart
Definition: porlay.hxx:38
sal_uInt16 Height() const
Definition: possiz.hxx:44
sal_uInt8 GetDirType(const size_t nCnt) const
Definition: scriptinfo.hxx:140
SwFont * GetFont()
Definition: inftxt.hxx:238
SwLineLayout * GetNext()
Definition: porlay.hxx:143
long AdjustX(long nHorzMove)
const OUString & GetText() const
Definition: ndtxt.hxx:210
SwCharRange & operator+=(const SwCharRange &rRange)
Definition: porlay.cxx:673
SwMarginPortion * CalcLeftMargin()
Definition: porlay.cxx:256
bool InNumberGrp() const
Definition: porlin.hxx:102
static bool lcl_IsLigature(sal_Unicode cCh, sal_Unicode cNextCh)
Definition: porlay.cxx:139
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:218
std::string GetValue
bool HasPara() const
Definition: txtfrm.hxx:812
TextFrameIndex GetStart() const
Definition: itrtxt.hxx:88
static bool isBehChar(sal_Unicode cCh)
Definition: porlay.cxx:88
SwNodeIndex nNode
Definition: pam.hxx:37
TextFrameIndex NextHiddenChg(TextFrameIndex nPos) const
Definition: porlay.cxx:1473
bool IsTextPortion() const
Definition: porlin.hxx:130
void SetFixWidth(const sal_uInt16 nNew)
Definition: porglue.hxx:38
const SfxPoolItem * GetItem(const SwTextAttr &rAttr, sal_uInt16 nWhich)
Extracts pool item of type nWhich from rAttr.
Definition: atrstck.cxx:157
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:273
SwTwips GetHangingMargin() const
Definition: porlay.hxx:158
virtual ~SwParaPortion() override
Definition: porlay.cxx:2153
CompType GetCompType(const size_t nCnt) const
Definition: scriptinfo.hxx:168
void CalcLine(SwTextFormatter &rLine, SwTextFormatInfo &rInf)
Definition: porlay.cxx:326
void MarkKashidasInvalid(sal_Int32 nCnt, const TextFrameIndex *pKashidaPositions)
Marks nCnt kashida positions as invalid pKashidaPositions: array of char indices relative to the para...
Definition: porlay.cxx:2055
sal_Int16 script
TElementType * Next()
Definition: calbck.hxx:373
sal_uInt16 GetTextHeight() const
Definition: inftxt.hxx:717
SwParaPortion * GetPara()
Definition: txtcache.cxx:89
#define isAlefChar(c)
Definition: porlay.cxx:65
static SwScriptInfo * GetScriptInfo(const SwTextNode &rNode, SwTextFrame const **o_pFrame=nullptr, bool bAllowInvalid=false)
return a frame for the node, ScriptInfo is its member...
Definition: porlay.cxx:2118
std::vector< CompressionChangeInfo > m_CompressionChanges
Definition: scriptinfo.hxx:78
bool m_bMargin
Definition: porlay.hxx:251
void SetLen(TextFrameIndex const nLen)
Definition: porlin.hxx:75
virtual void DeleteRange(SwPaM &)=0
Delete a range SwFlyFrameFormat.
long SwTwips
Definition: swtypes.hxx:49
std::deque< TextFrameIndex > m_NoKashidaLine
Definition: scriptinfo.hxx:67
bool IsHangingPortion() const
Definition: porlin.hxx:131
void SetFlyInCntBase(bool bNew=true)
Definition: itrform2.hxx:211
bool IsKashidaLine(TextFrameIndex nCharIdx) const
Definition: porlay.cxx:2029
bool Select(sal_Int32 nIndex, bool bSelect=true)
sal_uInt16 RealWidth() const
Definition: inftxt.hxx:554
std::vector< ScriptChangeInfo > m_ScriptChanges
Definition: scriptinfo.hxx:55
#define isQafChar(c)
Definition: porlay.cxx:79
#define isKafChar(c)
Definition: porlay.cxx:74
void SetMax(bool bMax)
Definition: porfly.hxx:58
void MaxAscentDescent(SwTwips &_orAscent, SwTwips &_orDescent, SwTwips &_orObjAscent, SwTwips &_orObjDescent, const SwLinePortion *_pDontConsiderPortion=nullptr, const bool _bNoFlyCntPorAndLinePor=false) const
determine ascent and descent for positioning of as-character anchored object
Definition: porlay.cxx:603
static bool lcl_HasStrongLTR(const OUString &rText, sal_Int32 nStart, sal_Int32 nEnd)
Definition: porlay.cxx:158
#define isTahChar(c)
Definition: porlay.cxx:82
sal_Int32 GetRangeCount() const
void CalcStartEnd(sal_uLong nNdIdx, sal_Int32 &rStart, sal_Int32 &rEnd) const
Calculates the intersection with text node number nNdIdx.
Definition: docredln.cxx:1264
static void CJKJustify(const OUString &rText, long *pKernArray, long *pScrArray, TextFrameIndex nStt, TextFrameIndex nLen, LanguageType aLang, long nSpaceAdd, bool bIsSpaceStop)
Definition: porlay.cxx:2376
Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
Definition: porlay.hxx:229
sal_uInt8 DirType(const TextFrameIndex nPos) const
Definition: porlay.cxx:1461
sal_uInt16 GetRealScriptOfText(const OUString &rText, sal_Int32 nPos) const
Definition: breakit.cxx:83
static bool IsShowChanges(const RedlineFlags eM)
const SwIndex * GetNext() const
Definition: index.hxx:102
virtual SwLinePortion * Compress()
Definition: porlin.cxx:59
size_t CountCompChg() const
Definition: scriptinfo.hxx:157
size_type size() const
Definition: docary.hxx:368
long GetLen(const Point &rPnt)
void SetNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Use regular blank justification instead of kashdida justification for the given line of text...
Definition: porlay.cxx:2022
bool isLetterNumeric(const OUString &rStr, sal_Int32 nPos) const
sal_uInt16 sal_Unicode
static sal_Int32 MaskHiddenRanges(const SwTextNode &rNode, OUStringBuffer &rText, const sal_Int32 nStt, const sal_Int32 nEnd, const sal_Unicode cChar)
Hidden text attribute handling.
Definition: porlay.cxx:1486
virtual ~SwLineLayout() override
Definition: porlay.cxx:198
std::vector< DirectionChangeInfo > m_DirectionChanges
Definition: scriptinfo.hxx:63
TextFrameIndex GetKashida(const size_t nCnt) const
Definition: scriptinfo.hxx:151
bool MarkOrClearKashidaInvalid(TextFrameIndex nStt, TextFrameIndex nLen, bool bMark, sal_Int32 nMarkCount)
Definition: porlay.cxx:1956
bool IsBreakPortion() const
Definition: porlin.hxx:114
SwIndex nContent
Definition: pam.hxx:38
#define isFehChar(c)
Definition: porlay.cxx:70
size_t pos
sal_uInt16 GetAscent(SwViewShell const *pSh, const OutputDevice &rOut)
Definition: swfont.hxx:326
bool HasHiddenCharAttribute(bool bWholePara) const
Hidden Paragraph Field:
Definition: ndtxt.hxx:720
static SwFontScript lcl_ScriptToFont(sal_uInt16 const nScript)
Definition: porlay.cxx:706
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
int nCount
static sal_Int32 endOfCTLScriptType(const OUString &Text, sal_Int32 nPos)
void UpdateBidiInfo(const OUString &rText)
Definition: porlay.cxx:1385
long Compress(long *pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen, const sal_uInt16 nCompress, const sal_uInt16 nFontHeight, const bool bCentered, Point *pPoint=nullptr) const
Definition: porlay.cxx:1703
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
void FormatReset()
Definition: porlay.hxx:313
void InitScriptInfo(const SwTextNode &rNode, sw::MergedPara const *pMerged, bool bRTL)
Definition: porlay.cxx:737
void ResetFlags()
Definition: porlay.cxx:652
Describes parts of multiple text nodes, which will form a text frame, even when redlines are hidden a...
Definition: txtfrm.hxx:958
virtual bool Format(SwTextFormatInfo &rInf) override
Definition: portxt.cxx:438
TextFrameIndex GetLineStart() const
Definition: inftxt.hxx:595
This portion represents an as-character anchored fly (shape, frame, etc.)
Definition: porfly.hxx:44
virtual CharCompressType getCharacterCompressionType() const =0
Get the character compression type for Asian characters.
void InitSpaceAdd()
Definition: porlay.cxx:295
static bool IsArabicText(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nLen)
Checks if text is Arabic text.
Definition: porlay.cxx:1905
static SwTextPortion * CopyLinePortion(const SwLinePortion &rPortion)
Definition: portxt.cxx:210
std::vector< Extent > extents
Definition: txtfrm.hxx:961
TextFrameIndex NextDirChg(const TextFrameIndex nPos, const sal_uInt8 *pLevel=nullptr) const
Definition: porlay.cxx:1446
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
void Join(SwGluePortion *pVictim)
Definition: porglue.cxx:118
void SetRealHeight(sal_uInt16 nNew)
Definition: porlay.hxx:152
TextFrameIndex nLen
Definition: porlay.hxx:39
virtual SwRedlineTable::size_type GetRedlinePos(const SwNode &rNode, RedlineType nType) const =0
size_t Count() const
Definition: ndhints.hxx:142
sal_uInt16 nAscent
Definition: porlin.hxx:57
bool IsDummy() const
Definition: porlay.hxx:135
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
const IDocumentRedlineAccess & getIDocumentRedlineAccess() const
Provides access to the document redline interface.
Definition: node.cxx:2054
virtual bool GetExpText(const SwTextSizeInfo &rInf, OUString &rText) const
Definition: porlin.cxx:312
uno_Any a
static void CalcHiddenRanges(const SwTextNode &rNode, MultiSelection &rHiddenMulti)
Definition: porlay.cxx:2330
Collection of SwLinePortion instances, representing one line of text.
Definition: porlay.hxx:78
CharCompressType
const sal_Unicode CH_FULL_BLANK
Definition: swfont.hxx:47
static void selectRedLineDeleted(const SwTextNode &rNode, MultiSelection &rHiddenMulti, bool bSelect=true)
Definition: porlay.cxx:2295
size_t CountHiddenChg() const
Definition: scriptinfo.hxx:174
vcl::RenderContext * GetOut()
Definition: inftxt.hxx:231
IDocumentContentOperations & getIDocumentContentOperations()
Provides access to the document content operations interface.
Definition: node.cxx:2063
Records a single change in script type.
Definition: scriptinfo.hxx:47
SwParaPortion * GetParaPortion()
Definition: inftxt.hxx:128
void SetWhichPor(const PortionType nNew)
Definition: porlin.hxx:94
sal_uInt8 m_nDefaultDir
Definition: scriptinfo.hxx:84
bool IsFlyPortion() const
Definition: porlin.hxx:125
bool IsKashidaValid(size_t nKashPos) const
Definition: porlay.cxx:1942
size_t HasKana(TextFrameIndex nStart, TextFrameIndex nEnd) const
Definition: porlay.cxx:1683
TElementType * First()
Definition: calbck.hxx:342
int i
QPRO_FUNC_TYPE const nType
vector_type::size_type size_type
Definition: docary.hxx:330
SwLinePortion * GetFirstPortion() const
Definition: porlay.cxx:667
long GetHeight() const
Definition: swfont.hxx:283
long Len() const
std::pair< SwTextNode *, sal_Int32 > MapViewToModel(MergedPara const &, TextFrameIndex nIndex)
Definition: txtfrm.cxx:1153
static sal_Int16 GetI18NScriptTypeOfLanguage(LanguageType nLang)
Marks a character position inside a document model node.
Definition: index.hxx:37
SwTextNode * pParaPropsNode
most paragraph properties are taken from the first non-empty node
Definition: txtfrm.hxx:966
#define isLamChar(c)
Definition: porlay.cxx:75
static sal_Int16 getCTLScriptType(const OUString &Text, sal_Int32 nPos)
virtual SwLinePortion * Append(SwLinePortion *pPortion) override
Definition: porlay.cxx:233
#define isDalChar(c)
Definition: porlay.cxx:66
const IDocumentSettingAccess * getIDocumentSettingAccess() const
Provides access to the document setting interface.
Definition: node.cxx:2052
void GetDefaultAscentAndHeight(SwViewShell const *pShell, OutputDevice const &rOut, sal_uInt16 &nAscent, sal_uInt16 &nHeight) const
Takes the default font and calculated the ascent and height.
Definition: atrstck.cxx:834
bool IsDropPortion() const
Definition: porlin.hxx:121
OUString mergedText
note: cannot be const currently to avoid UB because SwTextGuess::Guess const_casts it and modifies it...
Definition: txtfrm.hxx:964
TextFrameIndex GetParLen() const
Definition: porlay.cxx:2157
SwLinePortion * Cut(SwLinePortion *pVictim)
Definition: porlin.cxx:202
TextFrameIndex GetCompLen(const size_t nCnt) const
Definition: scriptinfo.hxx:163
TextFrameIndex GetLen() const
Definition: porlin.hxx:74
std::deque< TextFrameIndex > m_NoKashidaLineEnd
Definition: scriptinfo.hxx:68
bool InTabGrp() const
Definition: porlin.hxx:100
bool IsCalcHiddenCharFlags() const
Optimization: Asking for information about hidden characters at SwScriptInfo updates these flags...
Definition: ndtxt.hxx:148
SwTextFrame * GetTextFrame()
Definition: inftxt.hxx:291
sal_Int32 nLineWidth
std::pair< SwTextNode *, sal_Int32 > MapViewToModel(TextFrameIndex nIndex) const
map position in potentially merged text frame to SwPosition
Definition: txtfrm.cxx:1220
enumrange< T >::Iterator end(enumrange< T >)
const SwPosition * Start() const
Definition: pam.hxx:212
const OUString & GetText() const
Definition: inftxt.hxx:246
const Range & GetRange(sal_Int32 nRange) const
virtual bool Format(SwTextFormatInfo &rInf) override
Definition: porlay.cxx:245
bool IsHolePortion() const
Definition: porlin.hxx:126
CompType DbgCompType(const TextFrameIndex nPos) const
Definition: porlay.cxx:1664
SwLinePortion * mpNextPortion
Definition: porlin.hxx:54
bool IsMultiPortion() const
Definition: porlin.hxx:134
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter() const
Definition: breakit.hxx:62
const OUString & GetWord() const
Definition: swscanner.hxx:64
#define isAinChar(c)
Definition: porlay.cxx:64
Base class for anything that can be part of a line in the Writer layout.
Definition: porlin.hxx:50
long Max() const
const css::lang::Locale & GetLocale(const LanguageType aLang)
Definition: breakit.hxx:67
TextFrameIndex GetDirChg(const size_t nCnt) const
Definition: scriptinfo.hxx:135
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
#define isWawChar(c)
Definition: porlay.cxx:84
void ClearNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Clear forced blank justification for a given line.
Definition: porlay.cxx:2039
std::vector< TextFrameIndex > m_HiddenChg
Definition: scriptinfo.hxx:69
sal_Int16 ScriptType(const TextFrameIndex nPos) const
Definition: porlay.cxx:1433
static bool lcl_ConnectToPrev(sal_Unicode cCh, sal_Unicode cPrevCh)
Definition: porlay.cxx:146
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
bool InExpGrp() const
Definition: porlin.hxx:107
void SetHanging(const bool bNew)
Definition: porlay.hxx:128
#define SAL_WARN_IF(condition, area, stream)
#define isRehChar(c)
Definition: porlay.cxx:81
void SetNext(SwLineLayout *pNew)
Definition: porlay.hxx:145
unsigned char sal_uInt8
PortionType GetWhichPor() const
Definition: porlin.hxx:95
sw::LineAlign GetAlign() const
Definition: porfly.hxx:56
const Range & GetTotalRange() const
TextFrameIndex GetHiddenChg(const size_t nCnt) const
Definition: scriptinfo.hxx:175
static bool isTransparentChar(sal_Unicode cCh)
Definition: porlay.cxx:133
bool CheckLine(sal_uLong nStartNode, sal_Int32 nChkStart, sal_uLong nEndNode, sal_Int32 nChkEnd)
Definition: redlnitr.cxx:802
static bool isYehChar(sal_Unicode cCh)
Definition: porlay.cxx:113
size_t CountDirChg() const
Definition: scriptinfo.hxx:134
TextFrameIndex m_nInvalidityPos
Definition: scriptinfo.hxx:83
sal_uInt8 GetScriptType(const size_t nCnt) const
Definition: scriptinfo.hxx:128
sal_Int32 GetIndex() const
Definition: index.hxx:95
SwTwips HangingMargin() const
Definition: porlay.cxx:2223
static TextFrameIndex ThaiJustify(const OUString &rText, long *pKernArray, long *pScrArray, TextFrameIndex nIdx, TextFrameIndex nLen, TextFrameIndex nNumberOfBlanks=TextFrameIndex(0), long nSpaceAdd=0)
Performs a thai justification on the kerning array.
Definition: porlay.cxx:2079
LanguageType GetAppLanguage()
Definition: init.cxx:729
IDocumentSettingAccess const & getIDocumentSettingAccess() const
Definition: doc.cxx:175
std::unordered_set< size_t > m_KashidaInvalid
indexes into m_Kashida
Definition: scriptinfo.hxx:66
RedlineType GetType(sal_uInt16 nPos=0) const
Definition: docredln.cxx:1728
const sal_Int32 * End() const
Definition: txatbase.hxx:148
void ClearKashidaInvalid(size_t nKashPos)
Definition: porlay.cxx:1947
bool InTextGrp() const
Definition: porlin.hxx:98
sal_uInt16 Width() const
Definition: possiz.hxx:46
sal_Int32 GetBegin() const
Definition: swscanner.hxx:66
SwFontScript
Definition: swfont.hxx:122
#define isHehChar(c)
Definition: porlay.cxx:73
virtual bool get(DocumentSettingId id) const =0
Return the specified document setting.
virtual const SwPosition & GetMarkEnd() const =0
void SetNextPortion(SwLinePortion *pNew)
Definition: porlin.hxx:76
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:723
#define isSeenOrSadChar(c)
Definition: porlay.cxx:85
static void DeleteHiddenRanges(SwTextNode &rNode)
Hidden text attribute handling.
Definition: porlay.cxx:1522
const SwDropPortion * FindDropPortion() const
Definition: porlay.cxx:2169
static bool GetBoundsOfHiddenRange(const SwTextNode &rNode, sal_Int32 nPos, sal_Int32 &rnStartPos, sal_Int32 &rnEndPos, std::vector< sal_Int32 > *pList=nullptr)
Hidden text range information - static and non-version.
Definition: porlay.cxx:1540
void SetMargin(const bool bNew=true)
Definition: porlay.hxx:299
void DeleteNext()
Definition: porlay.cxx:176
size_t CountKashida() const
Definition: scriptinfo.hxx:146
TextFrameIndex GetCompStart(const size_t nCnt) const
Definition: scriptinfo.hxx:158
#define RES_CHRATR_HIDDEN
Definition: hintids.hxx:199
void SetAscent(const sal_uInt16 nNewAsc)
Definition: porlin.hxx:79
static bool IsInHiddenRange(const SwTextNode &rNode, sal_Int32 nPos)
Definition: porlay.cxx:1654
TextFrameIndex GetScriptChg(const size_t nCnt) const
Definition: scriptinfo.hxx:123
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:37
ResultType type
virtual RedlineFlags GetRedlineFlags() const =0
Query the currently set redline mode.
size_t CountScriptChg() const
Definition: scriptinfo.hxx:122
bool m_bFlys
Definition: porlay.hxx:242
SwDoc & GetDoc()
Definition: txtfrm.hxx:450
const SwIndex * GetFirstIndex() const
Definition: index.hxx:129
virtual const SwPosition & GetMarkStart() const =0
long Min() const
void GetKashidaPositions(TextFrameIndex nStt, TextFrameIndex nLen, std::vector< TextFrameIndex > &rKashidaPosition)
retrieves kashida opportunities for a given text range.
Definition: porlay.cxx:1998
TextFrameIndex GetEnd() const
Definition: itrtxt.hxx:89
virtual const SwRedlineTable & GetRedlineTable() const =0
void SelectAll(bool bSelect=true)
bool NextWord()
Definition: txtedt.cxx:799
SwLinePortion * GetNextPortion() const
Definition: porlin.hxx:72
static TextFrameIndex CountCJKCharacters(const OUString &rText, TextFrameIndex nPos, TextFrameIndex nEnd, LanguageType aLang)
Definition: porlay.cxx:2353
bool MarkKashidaInvalid(size_t nKashPos)
Definition: porlay.cxx:1992
bool m_bFootnoteNum
Definition: porlay.hxx:250
sal_Int32 KashidaJustify(long *pKernArray, long *pScrArray, TextFrameIndex nStt, TextFrameIndex nLen, long nSpaceAdd=0) const
Performs a kashida justification on the kerning array.
Definition: porlay.cxx:1820
o3tl::strong_int< sal_Int32, struct Tag_TextFrameIndex > TextFrameIndex
Denotes a character index in a text frame at a layout level, after extent mapping from a text node at...
#define isGafChar(c)
Definition: porlay.cxx:72
CharClass & GetAppCharClass()
Definition: init.cxx:709
#define isTehMarbutaChar(c)
Definition: porlay.cxx:83
virtual bool IsHidden() const =0
bool IsFlyCntPortion() const
Definition: porlin.hxx:112
sal_uInt16 & GetAscent()
Definition: porlin.hxx:77
SwRedlineItr * GetRedln()
Definition: itratr.hxx:81
const sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:61
static bool lcl_HasOnlyBlanks(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nEnd)
Definition: porlay.cxx:310
void CreateSpaceAdd(const long nInit=0)
Definition: porlay.cxx:303
static void selectHiddenTextProperty(const SwTextNode &rNode, MultiSelection &rHiddenMulti)
Definition: porlay.cxx:2242
bool IsPostItsPortion() const
Definition: porlin.hxx:128
std::deque< TextFrameIndex > m_Kashida
Definition: scriptinfo.hxx:64
void Init(SwLinePortion *pNextPortion=nullptr)
Definition: porlay.cxx:2186
const sal_Unicode CH_SIX_PER_EM
Definition: swfont.hxx:49