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