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