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