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