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