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