1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
24 #include <hintids.hxx>
25 #include <editeng/charscaleitem.hxx>
26 #include <svl/itemiter.hxx>
27 #include <svx/svdobj.hxx>
28 #include <vcl/svapp.hxx>
29 #include <fmtanchr.hxx>
30 #include <fmtfsize.hxx>
31 #include <fmtornt.hxx>
32 #include <fmtflcnt.hxx>
33 #include <fmtcntnt.hxx>
39 #include <IDocumentLayoutAccess.hxx>
40 #include <txatbase.hxx>
42 #include <rootfrm.hxx>
48 #include <htmltbl.hxx>
49 #include <swtable.hxx>
50 #include "redlnitr.hxx"
51 #include <redline.hxx>
52 #include <fmtsrnd.hxx>
54 #include <breakit.hxx>
55 #include <com/sun/star/i18n/WordType.hpp>
56 #include <com/sun/star/i18n/XBreakIterator.hpp>
57 #include <editeng/lrspitem.hxx>
59 #include <frameformats.hxx>
60 #include <sortedobjs.hxx>
61 #include <anchoredobject.hxx>
63 #include <flyfrms.hxx>
65 using namespace ::com::sun::star::i18n
;
66 using namespace ::com::sun::star
;
68 static sal_Int32
GetNextAttrImpl(SwTextNode
const* pTextNode
,
69 size_t nStartIndex
, size_t nEndIndex
, sal_Int32 nPosition
);
71 SwAttrIter::SwAttrIter(SwTextNode
const * pTextNode
)
72 : m_pViewShell(nullptr)
74 , m_pScriptInfo(nullptr)
81 , m_pTextNode(pTextNode
)
82 , m_pMergedPara(nullptr)
84 m_aFontCacheIds
[SwFontScript::Latin
] = m_aFontCacheIds
[SwFontScript::CJK
] = m_aFontCacheIds
[SwFontScript::CTL
] = nullptr;
87 SwAttrIter::SwAttrIter(SwTextNode
& rTextNode
, SwScriptInfo
& rScrInf
, SwTextFrame
const*const pFrame
)
88 : m_pViewShell(nullptr)
90 , m_pScriptInfo(nullptr)
94 , m_pTextNode(&rTextNode
)
95 , m_pMergedPara(nullptr)
97 CtorInitAttrIter(rTextNode
, rScrInf
, pFrame
);
100 void SwAttrIter::Chg( SwTextAttr
const *pHt
)
102 assert(pHt
&& m_pFont
&& "No attribute of font available for change");
103 if( m_pRedline
&& m_pRedline
->IsOn() )
104 m_pRedline
->ChangeTextAttr( m_pFont
, *pHt
, true );
106 m_aAttrHandler
.PushAndChg( *pHt
, *m_pFont
);
110 void SwAttrIter::Rst( SwTextAttr
const *pHt
)
112 assert(pHt
&& m_pFont
&& "No attribute of font available for reset");
113 // get top from stack after removing pHt
114 if( m_pRedline
&& m_pRedline
->IsOn() )
115 m_pRedline
->ChangeTextAttr( m_pFont
, *pHt
, false );
117 m_aAttrHandler
.PopAndChg( *pHt
, *m_pFont
);
121 SwAttrIter::~SwAttrIter()
127 bool SwAttrIter::MaybeHasHints() const
129 return nullptr != m_pTextNode
->GetpSwpHints() || nullptr != m_pMergedPara
;
133 * Returns the attribute for a position
135 * Only if the attribute is exactly at the position @param nPos and
136 * does not have an EndIndex
138 * We need this function for attributes which should alter formatting without
139 * changing the content of the string.
140 * Such "degenerated" attributes are e.g.: fields which retain expanded text and
142 * In order to avoid ambiguities between different such attributes, we insert a
143 * special character at the start of the string, when creating such an attribute.
144 * The Formatter later on encounters such a special character and retrieves the
145 * degenerate attribute via GetAttr().
147 SwTextAttr
*SwAttrIter::GetAttr(TextFrameIndex
const nPosition
) const
149 std::pair
<SwTextNode
const*, sal_Int32
> const pos( m_pMergedPara
150 ? sw::MapViewToModel(*m_pMergedPara
, nPosition
)
151 : std::make_pair(m_pTextNode
, sal_Int32(nPosition
)));
152 return pos
.first
->GetTextAttrForCharAt(pos
.second
);
155 bool SwAttrIter::SeekAndChgAttrIter(TextFrameIndex
const nNewPos
, OutputDevice
* pOut
)
157 std::pair
<SwTextNode
const*, sal_Int32
> const pos( m_pMergedPara
158 ? sw::MapViewToModel(*m_pMergedPara
, nNewPos
)
159 : std::make_pair(m_pTextNode
, sal_Int32(nNewPos
)));
160 bool bChg
= m_nStartIndex
&& pos
.first
== m_pTextNode
&& pos
.second
== m_nPosition
161 ? m_pFont
->IsFntChg()
163 if ( m_pLastOut
.get() != pOut
)
166 m_pFont
->SetFntChg( true );
171 // if the change counter is zero, we know the cache id of the wanted font
172 if ( !m_nChgCnt
&& !m_nPropFont
)
173 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
174 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
175 m_pFont
->ChgPhysFnt( m_pViewShell
, *pOut
);
181 bool SwAttrIter::IsSymbol(TextFrameIndex
const nNewPos
)
184 if ( !m_nChgCnt
&& !m_nPropFont
)
185 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
186 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
187 return m_pFont
->IsSymbol( m_pViewShell
);
190 bool SwTextFrame::IsSymbolAt(TextFrameIndex
const nPos
) const
192 SwTextInfo
info(const_cast<SwTextFrame
*>(this));
193 SwTextIter
iter(const_cast<SwTextFrame
*>(this), &info
);
194 return iter
.IsSymbol(nPos
);
197 bool SwAttrIter::SeekStartAndChgAttrIter( OutputDevice
* pOut
, const bool bParaFont
)
199 SwTextNode
const*const pFirstTextNode(m_pMergedPara
? m_pMergedPara
->pFirstNode
: m_pTextNode
);
200 if ( m_pRedline
&& m_pRedline
->ExtOn() )
201 m_pRedline
->LeaveExtend(*m_pFont
, pFirstTextNode
->GetIndex(), 0);
203 if (m_pTextNode
!= pFirstTextNode
)
205 assert(m_pMergedPara
);
206 m_pTextNode
= m_pMergedPara
->pFirstNode
;
207 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *m_pTextNode
,
208 m_pMergedPara
->mergedText
, nullptr, nullptr);
211 // reset font to its original state
212 m_aAttrHandler
.Reset();
213 m_aAttrHandler
.ResetFont( *m_pFont
);
220 m_pFont
->SetProportion( m_nPropFont
);
223 m_pRedline
->Clear( m_pFont
);
225 m_nChgCnt
= m_nChgCnt
+ m_pRedline
->Seek(*m_pFont
, pFirstTextNode
->GetIndex(), 0, COMPLETE_STRING
);
230 SwpHints
const*const pHints(m_pTextNode
->GetpSwpHints());
231 if (pHints
&& !bParaFont
)
233 SwTextAttr
*pTextAttr
;
234 // While we've not reached the end of the StartArray && the TextAttribute starts at position 0...
235 while ((m_nStartIndex
< pHints
->Count()) &&
236 !((pTextAttr
= pHints
->Get(m_nStartIndex
))->GetStart()))
238 // open the TextAttributes
244 bool bChg
= m_pFont
->IsFntChg();
245 if ( m_pLastOut
.get() != pOut
)
248 m_pFont
->SetFntChg( true );
253 // if the application counter is zero, we know the cache id of the wanted font
254 if ( !m_nChgCnt
&& !m_nPropFont
)
255 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
256 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
257 m_pFont
->ChgPhysFnt( m_pViewShell
, *pOut
);
262 // AMA: New AttrIter Nov 94
263 void SwAttrIter::SeekFwd(const sal_Int32 nOldPos
, const sal_Int32 nNewPos
)
265 SwpHints
const*const pHints(m_pTextNode
->GetpSwpHints());
266 SwTextAttr
*pTextAttr
;
267 const auto nHintsCount
= pHints
->Count();
269 if ( m_nStartIndex
) // If attributes have been opened at all ...
271 // Close attributes that are currently open, but stop at nNewPos+1
273 // As long as we've not yet reached the end of EndArray and the
274 // TextAttribute ends before or at the new position ...
275 while ((m_nEndIndex
< nHintsCount
) &&
276 ((pTextAttr
= pHints
->GetSortedByEnd(m_nEndIndex
))->GetAnyEnd() <= nNewPos
))
278 // Close the TextAttributes, whose StartPos were before or at
279 // the old nPos and are currently open
280 if (pTextAttr
->GetStart() <= nOldPos
) Rst( pTextAttr
);
284 else // skip the not opened ends
286 while ((m_nEndIndex
< nHintsCount
) &&
287 (pHints
->GetSortedByEnd(m_nEndIndex
)->GetAnyEnd() <= nNewPos
))
293 // As long as we've not yet reached the end of EndArray and the
294 // TextAttribute ends before or at the new position...
295 while ((m_nStartIndex
< nHintsCount
) &&
296 ((pTextAttr
= pHints
->Get(m_nStartIndex
))->GetStart() <= nNewPos
))
299 // open the TextAttributes, whose ends lie behind the new position
300 if ( pTextAttr
->GetAnyEnd() > nNewPos
) Chg( pTextAttr
);
306 bool SwAttrIter::Seek(TextFrameIndex
const nNewPos
)
308 // note: nNewPos isn't necessarily an index returned from GetNextAttr
309 std::pair
<SwTextNode
const*, sal_Int32
> const newPos( m_pMergedPara
310 ? sw::MapViewToModel(*m_pMergedPara
, nNewPos
)
311 : std::make_pair(m_pTextNode
, sal_Int32(nNewPos
)));
313 if ( m_pRedline
&& m_pRedline
->ExtOn() )
314 m_pRedline
->LeaveExtend(*m_pFont
, newPos
.first
->GetIndex(), newPos
.second
);
315 if (m_pTextNode
->GetIndex() < newPos
.first
->GetIndex())
317 // Skipping to a different node - first seek until the end of this node
318 // to get rid of all hint items
319 if (m_pTextNode
->GetpSwpHints())
321 sal_Int32
nPos(m_nPosition
);
324 sal_Int32
const nOldPos(nPos
);
325 nPos
= GetNextAttrImpl(m_pTextNode
, m_nStartIndex
, m_nEndIndex
, nPos
);
326 if (nPos
<= m_pTextNode
->Len())
328 SeekFwd(nOldPos
, nPos
);
332 SeekFwd(nOldPos
, m_pTextNode
->Len());
335 while (nPos
< m_pTextNode
->Len());
337 // Unapply current para items:
338 // the SwAttrHandler doesn't appear to be capable of *unapplying*
339 // items at all; it can only apply a previously effective item.
340 // So do this by recreating the font from scratch.
341 // Apply new para items:
342 assert(m_pMergedPara
);
343 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *newPos
.first
,
344 m_pMergedPara
->mergedText
, nullptr, nullptr);
346 m_pTextNode
= newPos
.first
;
353 // sw_redlinehide: Seek(0) must move before the first character, which
354 // has a special case where the first node starts with delete redline.
355 if ((!nNewPos
&& !m_pMergedPara
)
356 || newPos
.first
!= m_pTextNode
357 || newPos
.second
< m_nPosition
)
361 if (m_pTextNode
!= newPos
.first
)
363 m_pTextNode
= newPos
.first
;
364 // sw_redlinehide: hope it's okay to use the current text node
365 // here; the AttrHandler shouldn't care about non-char items
366 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *m_pTextNode
,
367 m_pMergedPara
->mergedText
, nullptr, nullptr);
370 if (m_pMergedPara
|| m_pTextNode
->GetpSwpHints())
373 m_pRedline
->Clear( nullptr );
375 // reset font to its original state
376 m_aAttrHandler
.Reset();
377 m_aAttrHandler
.ResetFont( *m_pFont
);
380 m_pFont
->SetProportion( m_nPropFont
);
387 // resetting the font here makes it necessary to apply any
388 // changes for extended input directly to the font
389 if ( m_pRedline
&& m_pRedline
->ExtOn() )
391 m_pRedline
->UpdateExtFont( *m_pFont
);
397 if (m_pTextNode
->GetpSwpHints())
401 // iterate hint by hint: SeekFwd does not mix ends and starts,
402 // it always applies all the starts last, so it must be called once
403 // per position where hints start/end!
404 sal_Int32
nPos(m_nPosition
);
407 sal_Int32
const nOldPos(nPos
);
408 nPos
= GetNextAttrImpl(m_pTextNode
, m_nStartIndex
, m_nEndIndex
, nPos
);
409 if (nPos
<= newPos
.second
)
411 SeekFwd(nOldPos
, nPos
);
415 SeekFwd(nOldPos
, newPos
.second
);
418 while (nPos
< newPos
.second
);
422 SeekFwd(m_nPosition
, newPos
.second
);
426 m_pFont
->SetActual( m_pScriptInfo
->WhichFont(nNewPos
) );
429 m_nChgCnt
= m_nChgCnt
+ m_pRedline
->Seek(*m_pFont
, m_pTextNode
->GetIndex(), newPos
.second
, m_nPosition
);
430 m_nPosition
= newPos
.second
;
433 m_pFont
->SetProportion( m_nPropFont
);
435 return m_pFont
->IsFntChg();
438 static void InsertCharAttrs(SfxPoolItem
const** pAttrs
, SfxItemSet
const& rItems
)
440 SfxItemIter
iter(rItems
);
441 for (SfxPoolItem
const* pItem
= iter
.GetCurItem(); pItem
; pItem
= iter
.NextItem())
443 auto const nWhich(pItem
->Which());
444 if (isCHRATR(nWhich
) && RES_CHRATR_RSID
!= nWhich
)
446 pAttrs
[nWhich
- RES_CHRATR_BEGIN
] = pItem
;
448 else if (nWhich
== RES_TXTATR_UNKNOWN_CONTAINER
)
450 pAttrs
[RES_CHRATR_END
- RES_CHRATR_BEGIN
] = pItem
;
455 // if return false: portion ends at start of redline, indexes unchanged
456 // if return true: portion end not known (past end of redline), indexes point to first hint past end of redline
457 static bool CanSkipOverRedline(
458 SwTextNode
const& rStartNode
, sal_Int32
const nStartRedline
,
459 SwRangeRedline
const& rRedline
,
460 size_t & rStartIndex
, size_t & rEndIndex
,
461 bool const isTheAnswerYes
)
463 size_t nStartIndex(rStartIndex
);
464 size_t nEndIndex(rEndIndex
);
465 SwPosition
const*const pRLEnd(rRedline
.End());
466 if (!pRLEnd
->GetNode().IsTextNode() // if fully deleted...
467 || pRLEnd
->GetContentIndex() == pRLEnd
->GetNode().GetTextNode()->Len())
469 // shortcut: nothing follows redline
470 // current state is end state
473 std::vector
<SwTextAttr
*> activeCharFmts
;
474 // can't compare the SwFont that's stored somewhere, it doesn't have compare
475 // operator, so try to recreate the situation with some temp arrays here
476 SfxPoolItem
const* activeCharAttrsStart
[RES_CHRATR_END
- RES_CHRATR_BEGIN
+ 1] = { nullptr, };
477 if (rStartNode
!= pRLEnd
->GetNode())
478 { // nodes' attributes are only needed if there are different nodes
479 InsertCharAttrs(activeCharAttrsStart
, rStartNode
.GetSwAttrSet());
481 if (SwpHints
const*const pStartHints
= rStartNode
.GetpSwpHints())
483 // check hint ends of hints that start before and end within
484 sal_Int32
const nRedlineEnd(rStartNode
== pRLEnd
->GetNode()
485 ? pRLEnd
->GetContentIndex()
487 for ( ; nEndIndex
< pStartHints
->Count(); ++nEndIndex
)
489 SwTextAttr
*const pAttr(pStartHints
->GetSortedByEnd(nEndIndex
));
494 if (nRedlineEnd
< *pAttr
->End())
498 if (nStartRedline
<= pAttr
->GetStart())
502 if (pAttr
->IsFormatIgnoreEnd())
506 switch (pAttr
->Which())
508 // if any of these ends inside RL then we need a new portion
509 case RES_TXTATR_REFMARK
:
510 case RES_TXTATR_TOXMARK
:
511 case RES_TXTATR_META
: // actually these 2 aren't allowed to overlap ???
512 case RES_TXTATR_METAFIELD
:
513 case RES_TXTATR_INETFMT
:
514 case RES_TXTATR_CJK_RUBY
:
515 case RES_TXTATR_INPUTFIELD
:
516 case RES_TXTATR_CONTENTCONTROL
:
518 if (!isTheAnswerYes
) return false; // always break
521 // these are guaranteed not to overlap
522 // and come in order of application
523 case RES_TXTATR_AUTOFMT
:
524 case RES_TXTATR_CHARFMT
:
526 if (pAttr
->Which() == RES_TXTATR_CHARFMT
)
528 activeCharFmts
.push_back(pAttr
);
530 // pure formatting hints may end inside the redline &
531 // start again inside the redline, which must not cause
532 // a new text portion if they have the same items - so
533 // store the effective items & compare all at the end
534 SfxItemSet
const& rSet((pAttr
->Which() == RES_TXTATR_CHARFMT
)
535 ? static_cast<SfxItemSet
const&>(pAttr
->GetCharFormat().GetCharFormat()->GetAttrSet())
536 : *pAttr
->GetAutoFormat().GetStyleHandle());
537 InsertCharAttrs(activeCharAttrsStart
, rSet
);
540 // SwTextNode::SetAttr puts it into AUTOFMT which is quite
541 // sensible so it doesn't actually exist as a hint
542 case RES_TXTATR_UNKNOWN_CONTAINER
:
543 default: assert(false);
546 assert(nEndIndex
== pStartHints
->Count() ||
547 pRLEnd
->GetContentIndex() < pStartHints
->GetSortedByEnd(nEndIndex
)->GetAnyEnd());
550 if (rStartNode
!= pRLEnd
->GetNode())
556 // treat para properties as text properties
557 // ... with the FormatToTextAttr we get autofmts that correspond to the *effective* attr set difference
558 // effective attr set: para props + charfmts + autofmt *in that order*
559 // ... and the charfmt must be *nominally* the same
561 SfxPoolItem
const* activeCharAttrsEnd
[RES_CHRATR_END
- RES_CHRATR_BEGIN
+ 1] = { nullptr, };
562 if (rStartNode
!= pRLEnd
->GetNode())
563 { // nodes' attributes are only needed if there are different nodes
564 InsertCharAttrs(activeCharAttrsEnd
,
565 pRLEnd
->GetNode().GetTextNode()->GetSwAttrSet());
568 if (SwpHints
*const pEndHints
= pRLEnd
->GetNode().GetTextNode()->GetpSwpHints())
570 // check hint starts of hints that start within and end after
572 sal_Int32
const nRedlineStart(rStartNode
== pRLEnd
->GetNode()
576 for ( ; nStartIndex
< pEndHints
->Count(); ++nStartIndex
)
578 SwTextAttr
*const pAttr(pEndHints
->Get(nStartIndex
));
579 // compare with < here, not <=, to get the effective formatting
580 // of the 1st char after the redline; should not cause problems
581 // with consecutive delete redlines because those are handed by
582 // GetNextRedln() and here we have the last end pos.
583 if (pRLEnd
->GetContentIndex() < pAttr
->GetStart())
589 if (pAttr
->IsFormatIgnoreStart())
593 assert(nRedlineStart
<= pAttr
->GetStart()); // we wouldn't be here otherwise?
594 if (*pAttr
->End() <= pRLEnd
->GetContentIndex())
598 switch (pAttr
->Which())
600 case RES_TXTATR_REFMARK
:
601 case RES_TXTATR_TOXMARK
:
602 case RES_TXTATR_META
: // actually these 2 aren't allowed to overlap ???
603 case RES_TXTATR_METAFIELD
:
604 case RES_TXTATR_INETFMT
:
605 case RES_TXTATR_CJK_RUBY
:
606 case RES_TXTATR_INPUTFIELD
:
607 case RES_TXTATR_CONTENTCONTROL
:
609 if (!isTheAnswerYes
) return false;
612 case RES_TXTATR_AUTOFMT
:
613 case RES_TXTATR_CHARFMT
:
615 // char formats must be *nominally* the same
616 if (pAttr
->Which() == RES_TXTATR_CHARFMT
)
618 auto iter
= std::find_if(activeCharFmts
.begin(), activeCharFmts
.end(),
619 [&pAttr
](const SwTextAttr
* pCharFmt
) { return *pCharFmt
== *pAttr
; });
620 if (iter
!= activeCharFmts
.end())
621 activeCharFmts
.erase(iter
);
622 else if (!isTheAnswerYes
)
625 SfxItemSet
const& rSet((pAttr
->Which() == RES_TXTATR_CHARFMT
)
626 ? static_cast<SfxItemSet
const&>(pAttr
->GetCharFormat().GetCharFormat()->GetAttrSet())
627 : *pAttr
->GetAutoFormat().GetStyleHandle());
628 InsertCharAttrs(activeCharAttrsEnd
, rSet
);
632 // SwTextNode::SetAttr puts it into AUTOFMT which is quite
633 // sensible so it doesn't actually exist as a hint
634 case RES_TXTATR_UNKNOWN_CONTAINER
:
635 default: assert(false);
638 if (rStartNode
!= pRLEnd
->GetNode())
640 // need to iterate the nEndIndex forward too so the loop in the
641 // caller can look for the right ends in the next iteration
642 for (nEndIndex
= 0; nEndIndex
< pEndHints
->Count(); ++nEndIndex
)
644 SwTextAttr
*const pAttr(pEndHints
->GetSortedByEnd(nEndIndex
));
647 if (pRLEnd
->GetContentIndex() < *pAttr
->End())
655 // if we didn't find a matching start for any end, then it really ends inside
656 if (!activeCharFmts
.empty())
658 if (!isTheAnswerYes
) return false;
660 for (size_t i
= 0; i
< SAL_N_ELEMENTS(activeCharAttrsStart
); ++i
)
662 // all of these are poolable
663 // assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->IsItemPoolable(*activeCharAttrsStart[i]));
664 if (activeCharAttrsStart
[i
] != activeCharAttrsEnd
[i
])
666 if (!isTheAnswerYes
) return false;
669 rStartIndex
= nStartIndex
;
670 rEndIndex
= nEndIndex
;
674 static sal_Int32
GetNextAttrImpl(SwTextNode
const*const pTextNode
,
675 size_t const nStartIndex
, size_t const nEndIndex
,
676 sal_Int32
const nPosition
)
678 // note: this used to be COMPLETE_STRING, but was set to Len() + 1 below,
679 // which is rather silly, so set it to Len() instead
680 sal_Int32 nNext
= pTextNode
->Len();
681 if (SwpHints
const*const pHints
= pTextNode
->GetpSwpHints())
683 // are there attribute starts left?
684 for (size_t i
= nStartIndex
; i
< pHints
->Count(); ++i
)
686 SwTextAttr
*const pAttr(pHints
->Get(i
));
687 if (!pAttr
->IsFormatIgnoreStart())
689 nNext
= pAttr
->GetStart();
693 // are there attribute ends left?
694 for (size_t i
= nEndIndex
; i
< pHints
->Count(); ++i
)
696 SwTextAttr
*const pAttr(pHints
->GetSortedByEnd(i
));
697 if (!pAttr
->IsFormatIgnoreEnd())
699 sal_Int32
const nNextEnd
= pAttr
->GetAnyEnd();
700 nNext
= std::min(nNext
, nNextEnd
); // pick nearest one
705 // TODO: maybe use hints like FieldHints for this instead of looking at the text...
706 const sal_Int32 l
= std::min(nNext
, pTextNode
->Len());
707 sal_Int32 p
= nPosition
;
708 const sal_Unicode
* pStr
= pTextNode
->GetText().getStr();
711 sal_Unicode aChar
= pStr
[p
];
714 case CH_TXT_ATR_FORMELEMENT
:
715 case CH_TXT_ATR_FIELDSTART
:
716 case CH_TXT_ATR_FIELDSEP
:
717 case CH_TXT_ATR_FIELDEND
:
718 goto break_
; // sigh...
727 // found a CH_TXT_ATR_FIELD*: if it's same as current position,
728 // skip behind it so that both before- and after-positions are returned
729 nNext
= (nPosition
< p
) ? p
: p
+ 1;
734 TextFrameIndex
SwAttrIter::GetNextAttr() const
736 size_t nStartIndex(m_nStartIndex
);
737 size_t nEndIndex(m_nEndIndex
);
738 size_t nPosition(m_nPosition
);
739 SwTextNode
const* pTextNode(m_pTextNode
);
740 SwRedlineTable::size_type
nActRedline(m_pRedline
? m_pRedline
->GetAct() : SwRedlineTable::npos
);
744 sal_Int32 nNext
= GetNextAttrImpl(pTextNode
, nStartIndex
, nEndIndex
, nPosition
);
747 std::pair
<sal_Int32
, std::pair
<SwRangeRedline
const*, size_t>> const redline(
748 m_pRedline
->GetNextRedln(nNext
, pTextNode
, nActRedline
));
749 if (redline
.second
.first
)
751 assert(m_pMergedPara
);
752 assert(redline
.second
.first
->End()->GetNodeIndex() <= m_pMergedPara
->pLastNode
->GetIndex()
753 || !redline
.second
.first
->End()->GetNode().IsTextNode());
754 if (CanSkipOverRedline(*pTextNode
, redline
.first
, *redline
.second
.first
,
755 nStartIndex
, nEndIndex
, m_nPosition
== redline
.first
))
756 { // if current position is start of the redline, must skip!
757 nActRedline
+= redline
.second
.second
;
758 if (&redline
.second
.first
->End()->GetNode() != pTextNode
)
760 pTextNode
= redline
.second
.first
->End()->GetNode().GetTextNode();
761 nPosition
= redline
.second
.first
->End()->GetContentIndex();
765 nPosition
= redline
.second
.first
->End()->GetContentIndex();
770 return sw::MapModelToView(*m_pMergedPara
, pTextNode
, redline
.first
);
776 ? sw::MapModelToView(*m_pMergedPara
, pTextNode
, redline
.first
)
777 : TextFrameIndex(redline
.first
);
782 return TextFrameIndex(nNext
);
792 VclPtr
<OutputDevice
> m_pOut
;
793 SwViewShell
const* m_pSh
;
795 sal_uLong
& m_rAbsMin
;
796 tools::Long m_nRowWidth
;
797 tools::Long m_nWordWidth
;
798 tools::Long m_nWordAdd
;
799 sal_Int32 m_nNoLineBreak
;
800 SwMinMaxArgs(OutputDevice
* pOutI
, SwViewShell
const* pShI
, sal_uLong
& rMinI
, sal_uLong
& rAbsI
)
808 , m_nNoLineBreak(COMPLETE_STRING
)
810 void Minimum( tools::Long nNew
) const {
811 if (static_cast<tools::Long
>(m_rMin
) < nNew
)
814 void NewWord() { m_nWordAdd
= m_nWordWidth
= 0; }
819 static bool lcl_MinMaxString( SwMinMaxArgs
& rArg
, SwFont
* pFnt
, const OUString
&rText
,
820 sal_Int32 nIdx
, sal_Int32 nEnd
)
825 sal_Int32 nStop
= nIdx
;
826 LanguageType eLang
= pFnt
->GetLanguage();
827 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
829 bool bClear
= CH_BLANK
== rText
[ nStop
];
830 Boundary
aBndry( g_pBreakIt
->GetBreakIter()->getWordBoundary( rText
, nIdx
,
831 g_pBreakIt
->GetLocale( eLang
),
832 WordType::DICTIONARY_WORD
, true ) );
833 nStop
= aBndry
.endPos
;
834 if (nIdx
<= aBndry
.startPos
&& nIdx
&& nIdx
- 1 != rArg
.m_nNoLineBreak
)
841 SwDrawTextInfo
aDrawInf(rArg
.m_pSh
, *rArg
.m_pOut
, rText
, nIdx
, nStop
- nIdx
);
842 tools::Long nCurrentWidth
= pFnt
->GetTextSize_( aDrawInf
).Width();
843 rArg
.m_nRowWidth
+= nCurrentWidth
;
848 rArg
.m_nWordWidth
+= nCurrentWidth
;
849 if (static_cast<tools::Long
>(rArg
.m_rAbsMin
) < rArg
.m_nWordWidth
)
850 rArg
.m_rAbsMin
= rArg
.m_nWordWidth
;
851 rArg
.Minimum(rArg
.m_nWordWidth
+ rArg
.m_nWordAdd
);
859 bool SwTextNode::IsSymbolAt(const sal_Int32 nBegin
) const
861 SwScriptInfo aScriptInfo
;
862 SwAttrIter
aIter( *const_cast<SwTextNode
*>(this), aScriptInfo
);
863 aIter
.Seek( TextFrameIndex(nBegin
) );
864 return aIter
.GetFnt()->IsSymbol( getIDocumentLayoutAccess().GetCurrentViewShell() );
869 class SwMinMaxNodeArgs
872 sal_uLong m_nMaxWidth
; // sum of all frame widths
873 tools::Long m_nMinWidth
; // biggest frame
874 tools::Long m_nLeftRest
; // space not already covered by frames in the left margin
875 tools::Long m_nRightRest
; // space not already covered by frames in the right margin
876 tools::Long m_nLeftDiff
; // Min/Max-difference of the frame in the left margin
877 tools::Long m_nRightDiff
; // Min/Max-difference of the frame in the right margin
878 SwNodeOffset m_nIndex
; // index of the node
879 void Minimum( tools::Long nNew
) {
880 if (nNew
> m_nMinWidth
)
887 static void lcl_MinMaxNode(SwFrameFormat
* pNd
, SwMinMaxNodeArgs
& rIn
)
889 const SwFormatAnchor
& rFormatA
= pNd
->GetAnchor();
891 if ((RndStdIds::FLY_AT_PARA
!= rFormatA
.GetAnchorId()) &&
892 (RndStdIds::FLY_AT_CHAR
!= rFormatA
.GetAnchorId()))
897 const SwNode
*pAnchorNode
= rFormatA
.GetAnchorNode();
898 OSL_ENSURE(pAnchorNode
, "Unexpected NULL arguments");
899 if (!pAnchorNode
|| rIn
.m_nIndex
!= pAnchorNode
->GetIndex())
902 tools::Long nMin
, nMax
;
903 SwHTMLTableLayout
*pLayout
= nullptr;
904 const bool bIsDrawFrameFormat
= pNd
->Which()==RES_DRAWFRMFMT
;
905 if( !bIsDrawFrameFormat
)
907 // Does the frame contain a table at the start or the end?
908 const SwNodes
& rNodes
= pNd
->GetDoc()->GetNodes();
909 const SwFormatContent
& rFlyContent
= pNd
->GetContent();
910 SwNodeOffset nStt
= rFlyContent
.GetContentIdx()->GetIndex();
911 SwTableNode
* pTableNd
= rNodes
[nStt
+1]->GetTableNode();
914 SwNode
*pNd2
= rNodes
[nStt
];
915 pNd2
= rNodes
[pNd2
->EndOfSectionIndex()-1];
916 if( pNd2
->IsEndNode() )
917 pTableNd
= pNd2
->StartOfSectionNode()->GetTableNode();
921 pLayout
= pTableNd
->GetTable().GetHTMLTableLayout();
924 const SwFormatHoriOrient
& rOrient
= pNd
->GetHoriOrient();
925 sal_Int16 eHoriOri
= rOrient
.GetHoriOrient();
930 nMin
= pLayout
->GetMin();
931 nMax
= pLayout
->GetMax();
936 if( bIsDrawFrameFormat
)
938 const SdrObject
* pSObj
= pNd
->FindSdrObject();
940 nMin
= pSObj
->GetCurrentBoundRect().GetWidth();
947 const SwFormatFrameSize
&rSz
= pNd
->GetFrameSize();
948 nMin
= rSz
.GetWidth();
954 const SvxLRSpaceItem
&rLR
= pNd
->GetLRSpace();
955 nMin
+= rLR
.GetLeft();
956 nMin
+= rLR
.GetRight();
957 nMax
+= rLR
.GetLeft();
958 nMax
+= rLR
.GetRight();
960 if( css::text::WrapTextMode_THROUGH
== pNd
->GetSurround().GetSurround() )
966 // Frames, which are left- or right-aligned are only party considered
967 // when calculating the maximum, since the border is already being considered.
968 // Only if the frame extends into the text body, this part is being added
971 case text::HoriOrientation::RIGHT
:
975 rIn
.m_nRightRest
-= rIn
.m_nRightDiff
;
976 rIn
.m_nRightDiff
= nDiff
;
978 if( text::RelOrientation::FRAME
!= rOrient
.GetRelationOrient() )
980 if (rIn
.m_nRightRest
> 0)
981 rIn
.m_nRightRest
= 0;
983 rIn
.m_nRightRest
-= nMin
;
986 case text::HoriOrientation::LEFT
:
990 rIn
.m_nLeftRest
-= rIn
.m_nLeftDiff
;
991 rIn
.m_nLeftDiff
= nDiff
;
993 if (text::RelOrientation::FRAME
!= rOrient
.GetRelationOrient() && rIn
.m_nLeftRest
< 0)
995 rIn
.m_nLeftRest
-= nMin
;
1000 rIn
.m_nMaxWidth
+= nMax
;
1006 #define FLYINCNT_MIN_WIDTH 284
1009 * Changing this method very likely requires changing of GetScalingOfSelectedText
1010 * This one is called exclusively from import filters, so there is no layout.
1012 void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex
, sal_uLong
& rMin
, sal_uLong
&rMax
,
1013 sal_uLong
& rAbsMin
) const
1015 SwViewShell
const * pSh
= GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
1016 OutputDevice
* pOut
= nullptr;
1018 pOut
= pSh
->GetWin()->GetOutDev();
1020 pOut
= Application::GetDefaultDevice();
1022 MapMode
aOldMap( pOut
->GetMapMode() );
1023 pOut
->SetMapMode( MapMode( MapUnit::MapTwip
) );
1029 SvxTextLeftMarginItem
const& rTextLeftMargin(GetSwAttrSet().GetTextLeftMargin());
1030 SvxRightMarginItem
const& rRightMargin(GetSwAttrSet().GetRightMargin());
1031 tools::Long nLROffset
= rTextLeftMargin
.GetTextLeft() + GetLeftMarginWithNum( true );
1033 // For enumerations a negative first line indentation is probably filled already
1034 if( !GetFirstLineOfsWithNum( nFLOffs
) || nFLOffs
> nLROffset
)
1035 nLROffset
= nFLOffs
;
1037 SwMinMaxNodeArgs aNodeArgs
;
1038 aNodeArgs
.m_nMinWidth
= 0;
1039 aNodeArgs
.m_nMaxWidth
= 0;
1040 aNodeArgs
.m_nLeftRest
= nLROffset
;
1041 aNodeArgs
.m_nRightRest
= rRightMargin
.GetRight();
1042 aNodeArgs
.m_nLeftDiff
= 0;
1043 aNodeArgs
.m_nRightDiff
= 0;
1046 sw::SpzFrameFormats
* pSpzs
= const_cast<sw::SpzFrameFormats
*>(GetDoc().GetSpzFrameFormats());
1049 aNodeArgs
.m_nIndex
= nIndex
;
1050 for(auto pFormat
: *pSpzs
)
1051 lcl_MinMaxNode(pFormat
, aNodeArgs
);
1054 if (aNodeArgs
.m_nLeftRest
< 0)
1055 aNodeArgs
.Minimum(nLROffset
- aNodeArgs
.m_nLeftRest
);
1056 aNodeArgs
.m_nLeftRest
-= aNodeArgs
.m_nLeftDiff
;
1057 if (aNodeArgs
.m_nLeftRest
< 0)
1058 aNodeArgs
.m_nMaxWidth
-= aNodeArgs
.m_nLeftRest
;
1060 if (aNodeArgs
.m_nRightRest
< 0)
1061 aNodeArgs
.Minimum(rRightMargin
.GetRight() - aNodeArgs
.m_nRightRest
);
1062 aNodeArgs
.m_nRightRest
-= aNodeArgs
.m_nRightDiff
;
1063 if (aNodeArgs
.m_nRightRest
< 0)
1064 aNodeArgs
.m_nMaxWidth
-= aNodeArgs
.m_nRightRest
;
1066 SwScriptInfo aScriptInfo
;
1067 SwAttrIter
aIter( *const_cast<SwTextNode
*>(this), aScriptInfo
);
1068 TextFrameIndex
nIdx(0);
1069 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1070 TextFrameIndex
nLen(m_Text
.getLength());
1071 tools::Long nCurrentWidth
= 0;
1072 tools::Long nAdd
= 0;
1073 SwMinMaxArgs
aArg( pOut
, pSh
, rMin
, rAbsMin
);
1074 while( nIdx
< nLen
)
1076 TextFrameIndex nNextChg
= aIter
.GetNextAttr();
1077 TextFrameIndex nStop
= aScriptInfo
.NextScriptChg( nIdx
);
1078 if( nNextChg
> nStop
)
1080 SwTextAttr
*pHint
= nullptr;
1081 sal_Unicode cChar
= CH_BLANK
;
1083 while( nStop
< nLen
&& nStop
< nNextChg
&&
1084 CH_TAB
!= (cChar
= m_Text
[sal_Int32(nStop
)]) &&
1085 CH_BREAK
!= cChar
&& CHAR_HARDBLANK
!= cChar
&&
1086 CHAR_HARDHYPHEN
!= cChar
&& CHAR_SOFTHYPHEN
!= cChar
&&
1087 CH_TXT_ATR_INPUTFIELDSTART
!= cChar
&&
1088 CH_TXT_ATR_INPUTFIELDEND
!= cChar
&&
1089 CH_TXT_ATR_FORMELEMENT
!= cChar
&&
1090 CH_TXT_ATR_FIELDSTART
!= cChar
&&
1091 CH_TXT_ATR_FIELDSEP
!= cChar
&&
1092 CH_TXT_ATR_FIELDEND
!= cChar
&&
1095 // this looks like some defensive programming to handle dummy char
1096 // with missing hint? but it's rather silly because it may pass the
1097 // dummy char to lcl_MinMaxString in that case...
1098 if( ( CH_TXTATR_BREAKWORD
!= cChar
&& CH_TXTATR_INWORD
!= cChar
)
1099 || ( nullptr == ( pHint
= aIter
.GetAttr( nStop
) ) ) )
1102 if (lcl_MinMaxString(aArg
, aIter
.GetFnt(), m_Text
, sal_Int32(nIdx
), sal_Int32(nStop
)))
1107 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1112 if (static_cast<tools::Long
>(rMax
) < aArg
.m_nRowWidth
)
1113 rMax
= aArg
.m_nRowWidth
;
1114 aArg
.m_nRowWidth
= 0;
1116 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1122 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1125 case CHAR_SOFTHYPHEN
:
1128 case CHAR_HARDBLANK
:
1129 case CHAR_HARDHYPHEN
:
1131 OUString
sTmp( cChar
);
1132 SwDrawTextInfo
aDrawInf( pSh
,
1133 *pOut
, sTmp
, 0, 1, 0, false );
1134 nCurrentWidth
= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1135 aArg
.m_nWordWidth
+= nCurrentWidth
;
1136 aArg
.m_nRowWidth
+= nCurrentWidth
;
1137 if (static_cast<tools::Long
>(rAbsMin
) < aArg
.m_nWordWidth
)
1138 rAbsMin
= aArg
.m_nWordWidth
;
1139 aArg
.Minimum(aArg
.m_nWordWidth
+ aArg
.m_nWordAdd
);
1140 aArg
.m_nNoLineBreak
= sal_Int32(nIdx
++);
1143 case CH_TXTATR_BREAKWORD
:
1144 case CH_TXTATR_INWORD
:
1148 tools::Long nOldWidth
= aArg
.m_nWordWidth
;
1149 tools::Long nOldAdd
= aArg
.m_nWordAdd
;
1152 switch( pHint
->Which() )
1154 case RES_TXTATR_FLYCNT
:
1156 SwFrameFormat
*pFrameFormat
= pHint
->GetFlyCnt().GetFrameFormat();
1157 const SvxLRSpaceItem
&rLR
= pFrameFormat
->GetLRSpace();
1158 if( RES_DRAWFRMFMT
== pFrameFormat
->Which() )
1160 const SdrObject
* pSObj
= pFrameFormat
->FindSdrObject();
1162 nCurrentWidth
= pSObj
->GetCurrentBoundRect().GetWidth();
1168 const SwFormatFrameSize
& rTmpSize
= pFrameFormat
->GetFrameSize();
1169 if( RES_FLYFRMFMT
== pFrameFormat
->Which()
1170 && rTmpSize
.GetWidthPercent() )
1172 // This is a hack for the following situation: In the paragraph there's a
1173 // text frame with relative size. Then let's take 0.5 cm as minimum width
1174 // and USHRT_MAX as maximum width
1175 // It were cleaner and maybe necessary later on to iterate over the content
1176 // of the text frame and call GetMinMaxSize recursively
1177 nCurrentWidth
= FLYINCNT_MIN_WIDTH
; // 0.5 cm
1178 rMax
= std::max(rMax
, sal_uLong(USHRT_MAX
));
1181 nCurrentWidth
= pFrameFormat
->GetFrameSize().GetWidth();
1183 nCurrentWidth
+= rLR
.GetLeft();
1184 nCurrentWidth
+= rLR
.GetRight();
1185 aArg
.m_nWordAdd
= nOldWidth
+ nOldAdd
;
1186 aArg
.m_nWordWidth
= nCurrentWidth
;
1187 aArg
.m_nRowWidth
+= nCurrentWidth
;
1188 if (static_cast<tools::Long
>(rAbsMin
) < aArg
.m_nWordWidth
)
1189 rAbsMin
= aArg
.m_nWordWidth
;
1190 aArg
.Minimum(aArg
.m_nWordWidth
+ aArg
.m_nWordAdd
);
1193 case RES_TXTATR_FTN
:
1195 const OUString aText
= pHint
->GetFootnote().GetNumStr();
1196 if( lcl_MinMaxString( aArg
, aIter
.GetFnt(), aText
, 0,
1197 aText
.getLength() ) )
1202 case RES_TXTATR_FIELD
:
1203 case RES_TXTATR_ANNOTATION
:
1205 SwField
*pField
= const_cast<SwField
*>(pHint
->GetFormatField().GetField());
1206 const OUString aText
= pField
->ExpandField(true, nullptr);
1207 if( lcl_MinMaxString( aArg
, aIter
.GetFnt(), aText
, 0,
1208 aText
.getLength() ) )
1213 aArg
.m_nWordWidth
= nOldWidth
;
1214 aArg
.m_nWordAdd
= nOldAdd
;
1216 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1219 case CH_TXT_ATR_INPUTFIELDSTART
:
1220 case CH_TXT_ATR_INPUTFIELDEND
:
1221 case CH_TXT_ATR_FORMELEMENT
:
1222 case CH_TXT_ATR_FIELDSTART
:
1223 case CH_TXT_ATR_FIELDSEP
:
1224 case CH_TXT_ATR_FIELDEND
:
1225 { // just skip it and continue with the content...
1226 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1231 if (static_cast<tools::Long
>(rMax
) < aArg
.m_nRowWidth
)
1232 rMax
= aArg
.m_nRowWidth
;
1234 nLROffset
+= rRightMargin
.GetRight();
1236 rAbsMin
+= nLROffset
;
1240 if (static_cast<tools::Long
>(rMin
) < aNodeArgs
.m_nMinWidth
)
1241 rMin
= aNodeArgs
.m_nMinWidth
;
1242 if (static_cast<tools::Long
>(rAbsMin
) < aNodeArgs
.m_nMinWidth
)
1243 rAbsMin
= aNodeArgs
.m_nMinWidth
;
1244 rMax
+= aNodeArgs
.m_nMaxWidth
;
1247 if( rMax
< rMin
) // e.g. Frames with flow through only contribute to the minimum
1249 pOut
->SetMapMode( aOldMap
);
1253 * Calculates the width of the text part specified by nStart and nEnd,
1254 * the height of the line containing nStart is divided by this width,
1255 * indicating the scaling factor, if the text part is rotated.
1256 * Having CH_BREAKs in the text part, this method returns the scaling
1257 * factor for the longest of the text parts separated by the CH_BREAK
1259 * Changing this method very likely requires changing of "GetMinMaxSize"
1261 sal_uInt16
SwTextFrame::GetScalingOfSelectedText(
1262 TextFrameIndex nStart
, TextFrameIndex nEnd
)
1264 assert(GetOffset() <= nStart
&& (!GetFollow() || nStart
< GetFollow()->GetOffset()));
1265 SwViewShell
const*const pSh
= getRootFrame()->GetCurrShell();
1267 OutputDevice
*const pOut
= &pSh
->GetRefDev();
1270 MapMode
aOldMap( pOut
->GetMapMode() );
1271 pOut
->SetMapMode( MapMode( MapUnit::MapTwip
) );
1275 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
1277 SwScriptInfo aScriptInfo
;
1278 SwAttrIter
aIter(*GetTextNodeFirst(), aScriptInfo
, this);
1279 aIter
.SeekAndChgAttrIter( nStart
, pOut
);
1281 Boundary aBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1282 GetText(), sal_Int32(nStart
),
1283 g_pBreakIt
->GetLocale( aIter
.GetFnt()->GetLanguage() ),
1284 WordType::DICTIONARY_WORD
, true );
1286 if (sal_Int32(nStart
) == aBound
.startPos
)
1288 // cursor is at left or right border of word
1289 pOut
->SetMapMode( aOldMap
);
1293 nStart
= TextFrameIndex(aBound
.startPos
);
1294 nEnd
= TextFrameIndex(aBound
.endPos
);
1298 pOut
->SetMapMode( aOldMap
);
1303 SwScriptInfo aScriptInfo
;
1304 SwAttrIter
aIter(*GetTextNodeFirst(), aScriptInfo
, this);
1306 // We do not want scaling attributes to be considered during this
1307 // calculation. For this, we push a temporary scaling attribute with
1308 // scaling value 100 and priority flag on top of the scaling stack
1309 SwAttrHandler
& rAH
= aIter
.GetAttrHandler();
1310 SvxCharScaleWidthItem
aItem(100, RES_CHRATR_SCALEW
);
1311 SwTextAttrEnd
aAttr( aItem
, 0, COMPLETE_STRING
);
1312 aAttr
.SetPriorityAttr( true );
1313 rAH
.PushAndChg( aAttr
, *(aIter
.GetFnt()) );
1315 TextFrameIndex nIdx
= nStart
;
1317 sal_uLong nWidth
= 0;
1318 sal_uLong nProWidth
= 0;
1320 while( nIdx
< nEnd
)
1322 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1324 // scan for end of portion
1325 TextFrameIndex
const nNextChg
= std::min(aIter
.GetNextAttr(), aScriptInfo
.NextScriptChg(nIdx
));
1327 TextFrameIndex nStop
= nIdx
;
1328 sal_Unicode cChar
= CH_BLANK
;
1329 SwTextAttr
* pHint
= nullptr;
1331 // stop at special characters in [ nIdx, nNextChg ]
1332 while( nStop
< nEnd
&& nStop
< nNextChg
)
1334 cChar
= GetText()[sal_Int32(nStop
)];
1337 CH_BREAK
== cChar
||
1338 CHAR_HARDBLANK
== cChar
||
1339 CHAR_HARDHYPHEN
== cChar
||
1340 CHAR_SOFTHYPHEN
== cChar
||
1341 CH_TXT_ATR_INPUTFIELDSTART
== cChar
||
1342 CH_TXT_ATR_INPUTFIELDEND
== cChar
||
1343 CH_TXT_ATR_FORMELEMENT
== cChar
||
1344 CH_TXT_ATR_FIELDSTART
== cChar
||
1345 CH_TXT_ATR_FIELDSEP
== cChar
||
1346 CH_TXT_ATR_FIELDEND
== cChar
||
1348 (CH_TXTATR_BREAKWORD
== cChar
|| CH_TXTATR_INWORD
== cChar
) &&
1349 (nullptr == (pHint
= aIter
.GetAttr(nStop
)))
1359 // calculate text widths up to cChar
1362 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, GetText(), sal_Int32(nIdx
), sal_Int32(nStop
- nIdx
));
1363 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1367 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1369 if ( cChar
== CH_BREAK
)
1371 nWidth
= std::max( nWidth
, nProWidth
);
1375 else if ( cChar
== CH_TAB
)
1377 // tab receives width of one space
1378 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, OUStringChar(CH_BLANK
), 0, 1);
1379 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1382 else if ( cChar
== CHAR_SOFTHYPHEN
)
1384 else if ( cChar
== CHAR_HARDBLANK
|| cChar
== CHAR_HARDHYPHEN
)
1386 OUString
sTmp( cChar
);
1387 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, sTmp
, 0, 1);
1388 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1391 else if ( pHint
&& ( cChar
== CH_TXTATR_BREAKWORD
|| cChar
== CH_TXTATR_INWORD
) )
1393 switch( pHint
->Which() )
1395 case RES_TXTATR_FTN
:
1397 const OUString aText
= pHint
->GetFootnote().GetNumStr();
1398 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, aText
, 0, aText
.getLength());
1400 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1404 case RES_TXTATR_FIELD
:
1405 case RES_TXTATR_ANNOTATION
:
1407 SwField
*pField
= const_cast<SwField
*>(pHint
->GetFormatField().GetField());
1408 OUString
const aText
= pField
->ExpandField(true, getRootFrame());
1409 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, aText
, 0, aText
.getLength());
1411 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1417 // any suggestions for a default action?
1422 else if (CH_TXT_ATR_INPUTFIELDSTART
== cChar
||
1423 CH_TXT_ATR_INPUTFIELDEND
== cChar
||
1424 CH_TXT_ATR_FORMELEMENT
== cChar
||
1425 CH_TXT_ATR_FIELDSTART
== cChar
||
1426 CH_TXT_ATR_FIELDSEP
== cChar
||
1427 CH_TXT_ATR_FIELDEND
== cChar
)
1428 { // just skip it and continue with the content...
1433 nWidth
= std::max( nWidth
, nProWidth
);
1435 // search for the line containing nStart
1438 SwTextInfo
aInf(this);
1439 SwTextIter
aLine(this, &aInf
);
1440 aLine
.CharToLine( nStart
);
1441 pOut
->SetMapMode( aOldMap
);
1442 return o3tl::narrowing
<sal_uInt16
>( nWidth
?
1443 ( ( 100 * aLine
.GetCurr()->Height() ) / nWidth
) : 0 );
1445 // no frame or no paragraph, we take the height of the character
1446 // at nStart as line height
1448 aIter
.SeekAndChgAttrIter( nStart
, pOut
);
1449 pOut
->SetMapMode( aOldMap
);
1451 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, GetText(), sal_Int32(nStart
), 1);
1452 return o3tl::narrowing
<sal_uInt16
>( nWidth
? ((100 * aIter
.GetFnt()->GetTextSize_( aDrawInf
).Height()) / nWidth
) : 0 );
1455 std::vector
<SwFlyAtContentFrame
*> SwTextFrame::GetSplitFlyDrawObjs() const
1457 std::vector
<SwFlyAtContentFrame
*> aObjs
;
1458 const SwSortedObjs
* pSortedObjs
= GetDrawObjs();
1464 for (const auto& pSortedObj
: *pSortedObjs
)
1466 SwFlyFrame
* pFlyFrame
= pSortedObj
->DynCastFlyFrame();
1472 if (!pFlyFrame
->IsFlySplitAllowed())
1477 aObjs
.push_back(static_cast<SwFlyAtContentFrame
*>(pFlyFrame
));
1483 SwFlyAtContentFrame
* SwTextFrame::HasNonLastSplitFlyDrawObj() const
1485 const SwTextFrame
* pFollow
= GetFollow();
1491 if (mnOffset
!= pFollow
->GetOffset())
1496 // At this point we know what we're part of a chain that is an anchor for split fly frames, but
1497 // we're not the last one. See if we have a matching fly.
1499 // Look up the master of the anchor.
1500 const SwTextFrame
* pAnchor
= this;
1501 while (pAnchor
->IsFollow())
1503 pAnchor
= pAnchor
->FindMaster();
1505 for (const auto& pFly
: pAnchor
->GetSplitFlyDrawObjs())
1507 // Nominally all flys are anchored in the master; see if this fly is effectively anchored in
1509 SwTextFrame
* pFlyAnchor
= pFly
->FindAnchorCharFrame();
1510 if (pFlyAnchor
!= this)
1514 if (pFly
->GetFollow())
1523 bool SwTextFrame::IsEmptyMasterWithSplitFly() const
1525 if (!IsEmptyMaster())
1530 if (!m_pDrawObjs
|| m_pDrawObjs
->size() != 1)
1535 SwFlyFrame
* pFlyFrame
= (*m_pDrawObjs
)[0]->DynCastFlyFrame();
1536 if (!pFlyFrame
|| !pFlyFrame
->IsFlySplitAllowed())
1541 if (mnOffset
!= GetFollow()->GetOffset())
1549 bool SwTextFrame::IsEmptyWithSplitFly() const
1556 if (GetTextNodeFirst()->GetSwAttrSet().HasItem(RES_PAGEDESC
))
1561 if (getFrameArea().Bottom() <= GetUpper()->getFramePrintArea().Bottom())
1566 // This is a master that doesn't fit the current parent.
1567 if (!m_pDrawObjs
|| m_pDrawObjs
->size() != 1)
1572 SwFlyFrame
* pFlyFrame
= (*m_pDrawObjs
)[0]->DynCastFlyFrame();
1573 if (!pFlyFrame
|| !pFlyFrame
->IsFlySplitAllowed())
1578 // It has a split fly anchored to it.
1579 if (pFlyFrame
->GetFrameFormat().GetVertOrient().GetPos() >= 0)
1584 // Negative vertical offset means that visually it already may have a first line.
1585 // Consider that, we may need to split the frame, so the fly frame is on one page and the empty
1586 // paragraph's frame is on a next page.
1590 SwTwips
SwTextNode::GetWidthOfLeadingTabs() const
1596 while ( nIdx
< GetText().getLength() )
1598 const sal_Unicode cCh
= GetText()[nIdx
];
1599 if ( cCh
!='\t' && cCh
!=' ' )
1608 SwPosition
aPos( *this, nIdx
);
1610 // Find the non-follow text frame:
1611 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*this);
1612 for( SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next() )
1614 // Only consider master frames:
1615 if (!pFrame
->IsFollow() &&
1616 pFrame
->GetTextNodeForFirstText() == this)
1618 SwRectFnSet
aRectFnSet(pFrame
);
1620 pFrame
->GetCharRect( aRect
, aPos
);
1621 nRet
= pFrame
->IsRightToLeft() ?
1622 aRectFnSet
.GetPrtRight(*pFrame
) - aRectFnSet
.GetRight(aRect
) :
1623 aRectFnSet
.GetLeft(aRect
) - aRectFnSet
.GetPrtLeft(*pFrame
);
1632 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */