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