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