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