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