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