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