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