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 <editeng/cmapitem.hxx>
27 #include <svl/itemiter.hxx>
28 #include <svx/svdobj.hxx>
29 #include <vcl/svapp.hxx>
30 #include <fmtanchr.hxx>
31 #include <fmtfsize.hxx>
32 #include <fmtornt.hxx>
33 #include <fmtflcnt.hxx>
34 #include <fmtcntnt.hxx>
36 #include <fmtpdsc.hxx>
41 #include <IDocumentLayoutAccess.hxx>
42 #include <IDocumentSettingAccess.hxx>
43 #include <txatbase.hxx>
45 #include <rootfrm.hxx>
51 #include <htmltbl.hxx>
52 #include <swtable.hxx>
53 #include "redlnitr.hxx"
54 #include <redline.hxx>
55 #include <fmtsrnd.hxx>
57 #include <breakit.hxx>
58 #include <com/sun/star/i18n/WordType.hpp>
59 #include <com/sun/star/i18n/XBreakIterator.hpp>
60 #include <editeng/lrspitem.hxx>
62 #include <frameformats.hxx>
63 #include <sortedobjs.hxx>
64 #include <anchoredobject.hxx>
66 #include <flyfrms.hxx>
68 using namespace ::com::sun::star::i18n
;
69 using namespace ::com::sun::star
;
71 static sal_Int32
GetNextAttrImpl(SwTextNode
const* pTextNode
,
72 size_t nStartIndex
, size_t nEndIndex
, sal_Int32 nPosition
);
74 SwAttrIter::SwAttrIter(SwTextNode
const * pTextNode
)
75 : m_pViewShell(nullptr)
77 , m_pScriptInfo(nullptr)
84 , m_pTextNode(pTextNode
)
85 , m_pMergedPara(nullptr)
87 m_aFontCacheIds
[SwFontScript::Latin
] = m_aFontCacheIds
[SwFontScript::CJK
] = m_aFontCacheIds
[SwFontScript::CTL
] = nullptr;
90 SwAttrIter::SwAttrIter(SwTextNode
& rTextNode
, SwScriptInfo
& rScrInf
, SwTextFrame
const*const pFrame
)
91 : m_pViewShell(nullptr)
93 , m_pScriptInfo(nullptr)
97 , m_pTextNode(&rTextNode
)
98 , m_pMergedPara(nullptr)
100 CtorInitAttrIter(rTextNode
, rScrInf
, pFrame
);
103 void SwAttrIter::Chg( SwTextAttr
const *pHt
)
105 assert(pHt
&& m_pFont
&& "No attribute of font available for change");
106 if( m_pRedline
&& m_pRedline
->IsOn() )
107 m_pRedline
->ChangeTextAttr( m_pFont
, *pHt
, true );
109 m_aAttrHandler
.PushAndChg( *pHt
, *m_pFont
);
113 void SwAttrIter::Rst( SwTextAttr
const *pHt
)
115 assert(pHt
&& m_pFont
&& "No attribute of font available for reset");
116 // get top from stack after removing pHt
117 if( m_pRedline
&& m_pRedline
->IsOn() )
118 m_pRedline
->ChangeTextAttr( m_pFont
, *pHt
, false );
120 m_aAttrHandler
.PopAndChg( *pHt
, *m_pFont
);
124 SwAttrIter::~SwAttrIter()
130 bool SwAttrIter::MaybeHasHints() const
132 return nullptr != m_pTextNode
->GetpSwpHints() || nullptr != m_pMergedPara
;
136 * Returns the attribute for a position
138 * Only if the attribute is exactly at the position @param nPos and
139 * does not have an EndIndex
141 * We need this function for attributes which should alter formatting without
142 * changing the content of the string.
143 * Such "degenerated" attributes are e.g.: fields which retain expanded text and
145 * In order to avoid ambiguities between different such attributes, we insert a
146 * special character at the start of the string, when creating such an attribute.
147 * The Formatter later on encounters such a special character and retrieves the
148 * degenerate attribute via GetAttr().
150 SwTextAttr
*SwAttrIter::GetAttr(TextFrameIndex
const nPosition
) const
152 std::pair
<SwTextNode
const*, sal_Int32
> const pos( m_pMergedPara
153 ? sw::MapViewToModel(*m_pMergedPara
, nPosition
)
154 : std::make_pair(m_pTextNode
, sal_Int32(nPosition
)));
155 return pos
.first
->GetTextAttrForCharAt(pos
.second
);
158 bool SwAttrIter::SeekAndChgAttrIter(TextFrameIndex
const nNewPos
, OutputDevice
* pOut
)
160 std::pair
<SwTextNode
const*, sal_Int32
> const pos( m_pMergedPara
161 ? sw::MapViewToModel(*m_pMergedPara
, nNewPos
)
162 : std::make_pair(m_pTextNode
, sal_Int32(nNewPos
)));
163 bool bChg
= m_nStartIndex
&& pos
.first
== m_pTextNode
&& pos
.second
== m_nPosition
164 ? m_pFont
->IsFntChg()
166 if ( m_pLastOut
.get() != pOut
)
169 m_pFont
->SetFntChg( true );
174 // if the change counter is zero, we know the cache id of the wanted font
175 if ( !m_nChgCnt
&& !m_nPropFont
)
176 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
177 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
178 m_pFont
->ChgPhysFnt( m_pViewShell
, *pOut
);
184 bool SwAttrIter::IsSymbol(TextFrameIndex
const nNewPos
)
187 if ( !m_nChgCnt
&& !m_nPropFont
)
188 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
189 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
190 return m_pFont
->IsSymbol( m_pViewShell
);
193 bool SwTextFrame::IsSymbolAt(TextFrameIndex
const nPos
) const
195 SwTextInfo
info(const_cast<SwTextFrame
*>(this));
196 SwTextIter
iter(const_cast<SwTextFrame
*>(this), &info
);
197 return iter
.IsSymbol(nPos
);
200 bool SwAttrIter::SeekStartAndChgAttrIter( OutputDevice
* pOut
, const bool bParaFont
)
202 SwTextNode
const*const pFirstTextNode(m_pMergedPara
? m_pMergedPara
->pFirstNode
: m_pTextNode
);
203 if ( m_pRedline
&& m_pRedline
->ExtOn() )
204 m_pRedline
->LeaveExtend(*m_pFont
, pFirstTextNode
->GetIndex(), 0);
206 if (m_pTextNode
!= pFirstTextNode
)
208 assert(m_pMergedPara
);
209 m_pTextNode
= m_pMergedPara
->pFirstNode
;
210 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *m_pTextNode
,
211 m_pMergedPara
->mergedText
, nullptr, nullptr);
214 // reset font to its original state
215 m_aAttrHandler
.Reset();
216 m_aAttrHandler
.ResetFont( *m_pFont
);
223 m_pFont
->SetProportion( m_nPropFont
);
226 m_pRedline
->Clear( m_pFont
);
228 m_nChgCnt
= m_nChgCnt
+ m_pRedline
->Seek(*m_pFont
, pFirstTextNode
->GetIndex(), 0, COMPLETE_STRING
);
233 SwpHints
const*const pHints(m_pTextNode
->GetpSwpHints());
234 if (pHints
&& !bParaFont
)
236 SwTextAttr
*pTextAttr
;
237 // While we've not reached the end of the StartArray && the TextAttribute starts at position 0...
238 while ((m_nStartIndex
< pHints
->Count()) &&
239 !((pTextAttr
= pHints
->Get(m_nStartIndex
))->GetStart()))
241 // open the TextAttributes
247 bool bChg
= m_pFont
->IsFntChg();
248 if ( m_pLastOut
.get() != pOut
)
251 m_pFont
->SetFntChg( true );
256 // if the application counter is zero, we know the cache id of the wanted font
257 if ( !m_nChgCnt
&& !m_nPropFont
)
258 m_pFont
->SetFontCacheId( m_aFontCacheIds
[ m_pFont
->GetActual() ],
259 m_aFontIdx
[ m_pFont
->GetActual() ], m_pFont
->GetActual() );
260 m_pFont
->ChgPhysFnt( m_pViewShell
, *pOut
);
265 // AMA: New AttrIter Nov 94
266 void SwAttrIter::SeekFwd(const sal_Int32 nOldPos
, const sal_Int32 nNewPos
)
268 SwpHints
const*const pHints(m_pTextNode
->GetpSwpHints());
269 SwTextAttr
*pTextAttr
;
270 const auto nHintsCount
= pHints
->Count();
272 if ( m_nStartIndex
) // If attributes have been opened at all ...
274 // Close attributes that are currently open, but stop at nNewPos+1
276 // As long as we've not yet reached the end of EndArray and the
277 // TextAttribute ends before or at the new position ...
278 while ((m_nEndIndex
< nHintsCount
) &&
279 ((pTextAttr
= pHints
->GetSortedByEnd(m_nEndIndex
))->GetAnyEnd() <= nNewPos
))
281 // Close the TextAttributes, whose StartPos were before or at
282 // the old nPos and are currently open
283 if (pTextAttr
->GetStart() <= nOldPos
) Rst( pTextAttr
);
287 else // skip the not opened ends
289 while ((m_nEndIndex
< nHintsCount
) &&
290 (pHints
->GetSortedByEnd(m_nEndIndex
)->GetAnyEnd() <= nNewPos
))
296 // As long as we've not yet reached the end of EndArray and the
297 // TextAttribute ends before or at the new position...
298 while ((m_nStartIndex
< nHintsCount
) &&
299 ((pTextAttr
= pHints
->Get(m_nStartIndex
))->GetStart() <= nNewPos
))
302 // open the TextAttributes, whose ends lie behind the new position
303 if ( pTextAttr
->GetAnyEnd() > nNewPos
) Chg( pTextAttr
);
308 void SwAttrIter::SeekToEnd()
310 if (m_pTextNode
->GetDoc().getIDocumentSettingAccess().get(
311 DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH
))
313 SfxItemPool
& rPool
{const_cast<SwAttrPool
&>(m_pTextNode
->GetDoc().GetAttrPool())};
314 SwFormatAutoFormat
const& rListAutoFormat
{m_pTextNode
->GetAttr(RES_PARATR_LIST_AUTOFMT
)};
315 std::shared_ptr
<SfxItemSet
> const pSet
{rListAutoFormat
.GetStyleHandle()};
320 if (pSet
->HasItem(RES_TXTATR_CHARFMT
))
322 SwFormatCharFormat
const& rCharFormat
{pSet
->Get(RES_TXTATR_CHARFMT
)};
323 m_pEndCharFormatAttr
.reset(new SwTextAttrEnd
{
324 SfxPoolItemHolder
{rPool
, &rCharFormat
}, -1, -1});
325 Chg(m_pEndCharFormatAttr
.get());
327 // note: RES_TXTATR_CHARFMT should be cleared here but it looks like
328 // SwAttrHandler only looks at RES_CHRATR_* anyway
329 m_pEndAutoFormatAttr
.reset(new SwTextAttrEnd
{
330 SfxPoolItemHolder
{rPool
, &rListAutoFormat
}, -1, -1});
331 Chg(m_pEndAutoFormatAttr
.get());
335 bool SwAttrIter::Seek(TextFrameIndex
const nNewPos
)
337 // note: nNewPos isn't necessarily an index returned from GetNextAttr
338 std::pair
<SwTextNode
const*, sal_Int32
> const newPos( m_pMergedPara
339 ? sw::MapViewToModel(*m_pMergedPara
, nNewPos
)
340 : std::make_pair(m_pTextNode
, sal_Int32(nNewPos
)));
342 if ( m_pRedline
&& m_pRedline
->ExtOn() )
343 m_pRedline
->LeaveExtend(*m_pFont
, newPos
.first
->GetIndex(), newPos
.second
);
344 if (m_pTextNode
->GetIndex() < newPos
.first
->GetIndex())
346 // Skipping to a different node - first seek until the end of this node
347 // to get rid of all hint items
348 if (m_pTextNode
->GetpSwpHints())
350 sal_Int32
nPos(m_nPosition
);
353 sal_Int32
const nOldPos(nPos
);
354 nPos
= GetNextAttrImpl(m_pTextNode
, m_nStartIndex
, m_nEndIndex
, nPos
);
355 if (nPos
<= m_pTextNode
->Len())
357 SeekFwd(nOldPos
, nPos
);
361 SeekFwd(nOldPos
, m_pTextNode
->Len());
364 while (nPos
< m_pTextNode
->Len());
366 // Unapply current para items:
367 // the SwAttrHandler doesn't appear to be capable of *unapplying*
368 // items at all; it can only apply a previously effective item.
369 // So do this by recreating the font from scratch.
370 // Apply new para items:
371 assert(m_pMergedPara
);
372 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *newPos
.first
,
373 m_pMergedPara
->mergedText
, nullptr, nullptr);
375 m_pTextNode
= newPos
.first
;
382 // sw_redlinehide: Seek(0) must move before the first character, which
383 // has a special case where the first node starts with delete redline.
384 if ((!nNewPos
&& !m_pMergedPara
)
385 || newPos
.first
!= m_pTextNode
386 || newPos
.second
< m_nPosition
)
390 if (m_pTextNode
!= newPos
.first
)
392 m_pTextNode
= newPos
.first
;
393 // sw_redlinehide: hope it's okay to use the current text node
394 // here; the AttrHandler shouldn't care about non-char items
395 InitFontAndAttrHandler(*m_pMergedPara
->pParaPropsNode
, *m_pTextNode
,
396 m_pMergedPara
->mergedText
, nullptr, nullptr);
399 // also reset it if the RES_PARATR_LIST_AUTOFMT has been applied!
400 if (m_pMergedPara
|| m_pTextNode
->GetpSwpHints() || m_pEndAutoFormatAttr
)
403 m_pRedline
->Clear( nullptr );
405 // reset font to its original state
406 m_aAttrHandler
.Reset();
407 m_aAttrHandler
.ResetFont( *m_pFont
);
408 m_pEndCharFormatAttr
.reset();
409 m_pEndAutoFormatAttr
.reset();
412 m_pFont
->SetProportion( m_nPropFont
);
419 // resetting the font here makes it necessary to apply any
420 // changes for extended input directly to the font
421 if ( m_pRedline
&& m_pRedline
->ExtOn() )
423 m_pRedline
->UpdateExtFont( *m_pFont
);
432 if (!m_pMergedPara
->extents
.empty())
434 auto const& rLast
{m_pMergedPara
->extents
.back()};
435 isToEnd
= rLast
.pNode
== newPos
.first
&& rLast
.nEnd
== newPos
.second
;
444 isToEnd
= newPos
.second
== m_pTextNode
->Len();
447 if (m_pTextNode
->GetpSwpHints())
451 // iterate hint by hint: SeekFwd does not mix ends and starts,
452 // it always applies all the starts last, so it must be called once
453 // per position where hints start/end!
454 sal_Int32
nPos(m_nPosition
);
457 sal_Int32
const nOldPos(nPos
);
458 nPos
= GetNextAttrImpl(m_pTextNode
, m_nStartIndex
, m_nEndIndex
, nPos
);
459 if (nPos
<= newPos
.second
)
461 SeekFwd(nOldPos
, nPos
);
465 SeekFwd(nOldPos
, newPos
.second
);
468 while (nPos
< newPos
.second
);
472 SeekFwd(m_nPosition
, newPos
.second
);
476 if (isToEnd
&& !m_pEndAutoFormatAttr
)
481 m_pFont
->SetActual( m_pScriptInfo
->WhichFont(nNewPos
) );
484 m_nChgCnt
= m_nChgCnt
+ m_pRedline
->Seek(*m_pFont
, m_pTextNode
->GetIndex(), newPos
.second
, m_nPosition
);
485 m_nPosition
= newPos
.second
;
488 m_pFont
->SetProportion( m_nPropFont
);
490 return m_pFont
->IsFntChg();
493 static void InsertCharAttrs(SfxPoolItem
const** pAttrs
, SfxItemSet
const& rItems
)
495 SfxItemIter
iter(rItems
);
496 for (SfxPoolItem
const* pItem
= iter
.GetCurItem(); pItem
; pItem
= iter
.NextItem())
498 auto const nWhich(pItem
->Which());
499 if (isCHRATR(nWhich
) && RES_CHRATR_RSID
!= nWhich
)
501 pAttrs
[nWhich
- RES_CHRATR_BEGIN
] = pItem
;
503 else if (nWhich
== RES_TXTATR_UNKNOWN_CONTAINER
)
505 pAttrs
[RES_CHRATR_END
- RES_CHRATR_BEGIN
] = pItem
;
510 // if return false: portion ends at start of redline, indexes unchanged
511 // if return true: portion end not known (past end of redline), indexes point to first hint past end of redline
512 static bool CanSkipOverRedline(
513 SwTextNode
const& rStartNode
, sal_Int32
const nStartRedline
,
514 SwRangeRedline
const& rRedline
,
515 size_t & rStartIndex
, size_t & rEndIndex
,
516 bool const isTheAnswerYes
)
518 size_t nStartIndex(rStartIndex
);
519 size_t nEndIndex(rEndIndex
);
520 SwPosition
const*const pRLEnd(rRedline
.End());
521 if (!pRLEnd
->GetNode().IsTextNode() // if fully deleted...
522 || pRLEnd
->GetContentIndex() == pRLEnd
->GetNode().GetTextNode()->Len())
524 // shortcut: nothing follows redline
525 // current state is end state
528 std::vector
<SwTextAttr
*> activeCharFmts
;
529 // can't compare the SwFont that's stored somewhere, it doesn't have compare
530 // operator, so try to recreate the situation with some temp arrays here
531 SfxPoolItem
const* activeCharAttrsStart
[RES_CHRATR_END
- RES_CHRATR_BEGIN
+ 1] = { nullptr, };
532 if (rStartNode
!= pRLEnd
->GetNode())
533 { // nodes' attributes are only needed if there are different nodes
534 InsertCharAttrs(activeCharAttrsStart
, rStartNode
.GetSwAttrSet());
536 if (SwpHints
const*const pStartHints
= rStartNode
.GetpSwpHints())
538 // check hint ends of hints that start before and end within
539 sal_Int32
const nRedlineEnd(rStartNode
== pRLEnd
->GetNode()
540 ? pRLEnd
->GetContentIndex()
542 for ( ; nEndIndex
< pStartHints
->Count(); ++nEndIndex
)
544 SwTextAttr
*const pAttr(pStartHints
->GetSortedByEnd(nEndIndex
));
549 if (nRedlineEnd
< *pAttr
->End())
553 if (nStartRedline
<= pAttr
->GetStart())
557 if (pAttr
->IsFormatIgnoreEnd())
561 switch (pAttr
->Which())
563 // if any of these ends inside RL then we need a new portion
564 case RES_TXTATR_REFMARK
:
565 case RES_TXTATR_TOXMARK
:
566 case RES_TXTATR_META
: // actually these 2 aren't allowed to overlap ???
567 case RES_TXTATR_METAFIELD
:
568 case RES_TXTATR_INETFMT
:
569 case RES_TXTATR_CJK_RUBY
:
570 case RES_TXTATR_INPUTFIELD
:
571 case RES_TXTATR_CONTENTCONTROL
:
573 if (!isTheAnswerYes
) return false; // always break
576 // these are guaranteed not to overlap
577 // and come in order of application
578 case RES_TXTATR_AUTOFMT
:
579 case RES_TXTATR_CHARFMT
:
581 if (pAttr
->Which() == RES_TXTATR_CHARFMT
)
583 activeCharFmts
.push_back(pAttr
);
585 // pure formatting hints may end inside the redline &
586 // start again inside the redline, which must not cause
587 // a new text portion if they have the same items - so
588 // store the effective items & compare all at the end
589 SfxItemSet
const& rSet((pAttr
->Which() == RES_TXTATR_CHARFMT
)
590 ? static_cast<SfxItemSet
const&>(pAttr
->GetCharFormat().GetCharFormat()->GetAttrSet())
591 : *pAttr
->GetAutoFormat().GetStyleHandle());
592 InsertCharAttrs(activeCharAttrsStart
, rSet
);
595 // SwTextNode::SetAttr puts it into AUTOFMT which is quite
596 // sensible so it doesn't actually exist as a hint
597 case RES_TXTATR_UNKNOWN_CONTAINER
:
598 default: assert(false);
601 assert(nEndIndex
== pStartHints
->Count() ||
602 pRLEnd
->GetContentIndex() < pStartHints
->GetSortedByEnd(nEndIndex
)->GetAnyEnd());
605 if (rStartNode
!= pRLEnd
->GetNode())
611 // treat para properties as text properties
612 // ... with the FormatToTextAttr we get autofmts that correspond to the *effective* attr set difference
613 // effective attr set: para props + charfmts + autofmt *in that order*
614 // ... and the charfmt must be *nominally* the same
616 SfxPoolItem
const* activeCharAttrsEnd
[RES_CHRATR_END
- RES_CHRATR_BEGIN
+ 1] = { nullptr, };
617 if (rStartNode
!= pRLEnd
->GetNode())
618 { // nodes' attributes are only needed if there are different nodes
619 InsertCharAttrs(activeCharAttrsEnd
,
620 pRLEnd
->GetNode().GetTextNode()->GetSwAttrSet());
623 if (SwpHints
*const pEndHints
= pRLEnd
->GetNode().GetTextNode()->GetpSwpHints())
625 // check hint starts of hints that start within and end after
627 sal_Int32
const nRedlineStart(rStartNode
== pRLEnd
->GetNode()
631 for ( ; nStartIndex
< pEndHints
->Count(); ++nStartIndex
)
633 SwTextAttr
*const pAttr(pEndHints
->Get(nStartIndex
));
634 // compare with < here, not <=, to get the effective formatting
635 // of the 1st char after the redline; should not cause problems
636 // with consecutive delete redlines because those are handed by
637 // GetNextRedln() and here we have the last end pos.
638 if (pRLEnd
->GetContentIndex() < pAttr
->GetStart())
644 if (pAttr
->IsFormatIgnoreStart())
648 assert(nRedlineStart
<= pAttr
->GetStart()); // we wouldn't be here otherwise?
649 if (*pAttr
->End() <= pRLEnd
->GetContentIndex())
653 switch (pAttr
->Which())
655 case RES_TXTATR_REFMARK
:
656 case RES_TXTATR_TOXMARK
:
657 case RES_TXTATR_META
: // actually these 2 aren't allowed to overlap ???
658 case RES_TXTATR_METAFIELD
:
659 case RES_TXTATR_INETFMT
:
660 case RES_TXTATR_CJK_RUBY
:
661 case RES_TXTATR_INPUTFIELD
:
662 case RES_TXTATR_CONTENTCONTROL
:
664 if (!isTheAnswerYes
) return false;
667 case RES_TXTATR_AUTOFMT
:
668 case RES_TXTATR_CHARFMT
:
670 // char formats must be *nominally* the same
671 if (pAttr
->Which() == RES_TXTATR_CHARFMT
)
673 auto iter
= std::find_if(activeCharFmts
.begin(), activeCharFmts
.end(),
674 [&pAttr
](const SwTextAttr
* pCharFmt
) { return *pCharFmt
== *pAttr
; });
675 if (iter
!= activeCharFmts
.end())
676 activeCharFmts
.erase(iter
);
677 else if (!isTheAnswerYes
)
680 SfxItemSet
const& rSet((pAttr
->Which() == RES_TXTATR_CHARFMT
)
681 ? static_cast<SfxItemSet
const&>(pAttr
->GetCharFormat().GetCharFormat()->GetAttrSet())
682 : *pAttr
->GetAutoFormat().GetStyleHandle());
683 InsertCharAttrs(activeCharAttrsEnd
, rSet
);
687 // SwTextNode::SetAttr puts it into AUTOFMT which is quite
688 // sensible so it doesn't actually exist as a hint
689 case RES_TXTATR_UNKNOWN_CONTAINER
:
690 default: assert(false);
693 if (rStartNode
!= pRLEnd
->GetNode())
695 // need to iterate the nEndIndex forward too so the loop in the
696 // caller can look for the right ends in the next iteration
697 for (nEndIndex
= 0; nEndIndex
< pEndHints
->Count(); ++nEndIndex
)
699 SwTextAttr
*const pAttr(pEndHints
->GetSortedByEnd(nEndIndex
));
702 if (pRLEnd
->GetContentIndex() < *pAttr
->End())
710 // if we didn't find a matching start for any end, then it really ends inside
711 if (!activeCharFmts
.empty())
713 if (!isTheAnswerYes
) return false;
715 for (size_t i
= 0; i
< SAL_N_ELEMENTS(activeCharAttrsStart
); ++i
)
717 // all of these should be shareable (but we have no SfxItemPool to check it here)
718 // assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->Shareable(*activeCharAttrsStart[i]));
719 if (!SfxPoolItem::areSame(activeCharAttrsStart
[i
], activeCharAttrsEnd
[i
]))
721 if (!isTheAnswerYes
) return false;
724 rStartIndex
= nStartIndex
;
725 rEndIndex
= nEndIndex
;
729 static sal_Int32
GetNextAttrImpl(SwTextNode
const*const pTextNode
,
730 size_t const nStartIndex
, size_t const nEndIndex
,
731 sal_Int32
const nPosition
)
733 // note: this used to be COMPLETE_STRING, but was set to Len() + 1 below,
734 // which is rather silly, so set it to Len() instead
735 sal_Int32 nNext
= pTextNode
->Len();
736 if (SwpHints
const*const pHints
= pTextNode
->GetpSwpHints())
738 // are there attribute starts left?
739 for (size_t i
= nStartIndex
; i
< pHints
->Count(); ++i
)
741 SwTextAttr
*const pAttr(pHints
->Get(i
));
742 if (!pAttr
->IsFormatIgnoreStart())
744 nNext
= pAttr
->GetStart();
748 // are there attribute ends left?
749 for (size_t i
= nEndIndex
; i
< pHints
->Count(); ++i
)
751 SwTextAttr
*const pAttr(pHints
->GetSortedByEnd(i
));
752 if (!pAttr
->IsFormatIgnoreEnd())
754 sal_Int32
const nNextEnd
= pAttr
->GetAnyEnd();
755 nNext
= std::min(nNext
, nNextEnd
); // pick nearest one
760 // TODO: maybe use hints like FieldHints for this instead of looking at the text...
761 const sal_Int32 l
= std::min(nNext
, pTextNode
->Len());
762 sal_Int32 p
= nPosition
;
763 const sal_Unicode
* pStr
= pTextNode
->GetText().getStr();
766 sal_Unicode aChar
= pStr
[p
];
769 case CH_TXT_ATR_FORMELEMENT
:
770 case CH_TXT_ATR_FIELDSTART
:
771 case CH_TXT_ATR_FIELDSEP
:
772 case CH_TXT_ATR_FIELDEND
:
773 goto break_
; // sigh...
782 // found a CH_TXT_ATR_FIELD*: if it's same as current position,
783 // skip behind it so that both before- and after-positions are returned
784 nNext
= (nPosition
< p
) ? p
: p
+ 1;
789 TextFrameIndex
SwAttrIter::GetNextAttr() const
791 size_t nStartIndex(m_nStartIndex
);
792 size_t nEndIndex(m_nEndIndex
);
793 size_t nPosition(m_nPosition
);
794 SwTextNode
const* pTextNode(m_pTextNode
);
795 SwRedlineTable::size_type
nActRedline(m_pRedline
? m_pRedline
->GetAct() : SwRedlineTable::npos
);
799 sal_Int32 nNext
= GetNextAttrImpl(pTextNode
, nStartIndex
, nEndIndex
, nPosition
);
802 std::pair
<sal_Int32
, std::pair
<SwRangeRedline
const*, size_t>> const redline(
803 m_pRedline
->GetNextRedln(nNext
, pTextNode
, nActRedline
));
804 if (redline
.second
.first
)
806 assert(m_pMergedPara
);
807 assert(redline
.second
.first
->End()->GetNodeIndex() <= m_pMergedPara
->pLastNode
->GetIndex()
808 || !redline
.second
.first
->End()->GetNode().IsTextNode());
809 if (CanSkipOverRedline(*pTextNode
, redline
.first
, *redline
.second
.first
,
810 nStartIndex
, nEndIndex
, m_nPosition
== redline
.first
))
811 { // if current position is start of the redline, must skip!
812 nActRedline
+= redline
.second
.second
;
813 if (&redline
.second
.first
->End()->GetNode() != pTextNode
)
815 pTextNode
= redline
.second
.first
->End()->GetNode().GetTextNode();
816 nPosition
= redline
.second
.first
->End()->GetContentIndex();
820 nPosition
= redline
.second
.first
->End()->GetContentIndex();
825 return sw::MapModelToView(*m_pMergedPara
, pTextNode
, redline
.first
);
831 ? sw::MapModelToView(*m_pMergedPara
, pTextNode
, redline
.first
)
832 : TextFrameIndex(redline
.first
);
837 return TextFrameIndex(nNext
);
844 class FormatBreakTracker
847 std::optional
<SvxCaseMap
> m_nCaseMap
;
849 bool m_bNeedsBreak
= false;
851 void SetCaseMap(SvxCaseMap nValue
)
853 if (m_nCaseMap
!= nValue
)
854 m_bNeedsBreak
= true;
860 void HandleItemSet(const SfxItemSet
& rSet
)
862 if (const SvxCaseMapItem
* pItem
= rSet
.GetItem(RES_CHRATR_CASEMAP
))
863 SetCaseMap(pItem
->GetCaseMap());
866 void Reset() { m_bNeedsBreak
= false; }
868 bool NeedsBreak() const { return m_bNeedsBreak
; }
871 bool HasFormatBreakAttribute(FormatBreakTracker
* pTracker
, const SwTextAttr
* pAttr
)
875 switch (pAttr
->Which())
877 case RES_TXTATR_AUTOFMT
:
878 case RES_TXTATR_CHARFMT
:
880 const SfxItemSet
& rSet((pAttr
->Which() == RES_TXTATR_CHARFMT
)
881 ? static_cast<SfxItemSet
const&>(
882 pAttr
->GetCharFormat().GetCharFormat()->GetAttrSet())
883 : *pAttr
->GetAutoFormat().GetStyleHandle());
885 pTracker
->HandleItemSet(rSet
);
890 if (pAttr
->IsFormatIgnoreStart() || pAttr
->IsFormatIgnoreEnd())
893 return pTracker
->NeedsBreak();
897 TextFrameIndex
SwAttrIter::GetNextLayoutBreakAttr() const
899 size_t nStartIndex(m_nStartIndex
);
900 SwTextNode
const* pTextNode(m_pTextNode
);
902 sal_Int32 nNext
= std::numeric_limits
<sal_Int32
>::max();
904 auto* pHints
= pTextNode
->GetpSwpHints();
907 return TextFrameIndex
{ nNext
};
910 FormatBreakTracker stTracker
;
911 stTracker
.HandleItemSet(pTextNode
->GetSwAttrSet());
913 for (size_t i
= 0; i
< pHints
->Count(); ++i
)
915 SwTextAttr
* const pAttr(pHints
->Get(i
));
916 if (HasFormatBreakAttribute(&stTracker
, pAttr
))
918 if (i
>= nStartIndex
)
920 nNext
= pAttr
->GetStart();
926 return TextFrameIndex
{ nNext
};
934 VclPtr
<OutputDevice
> m_pOut
;
935 SwViewShell
const* m_pSh
;
937 sal_uLong
& m_rAbsMin
;
938 tools::Long m_nRowWidth
;
939 tools::Long m_nWordWidth
;
940 tools::Long m_nWordAdd
;
941 sal_Int32 m_nNoLineBreak
;
942 SwMinMaxArgs(OutputDevice
* pOutI
, SwViewShell
const* pShI
, sal_uLong
& rMinI
, sal_uLong
& rAbsI
)
950 , m_nNoLineBreak(COMPLETE_STRING
)
952 void Minimum( tools::Long nNew
) const {
953 if (static_cast<tools::Long
>(m_rMin
) < nNew
)
956 void NewWord() { m_nWordAdd
= m_nWordWidth
= 0; }
961 static bool lcl_MinMaxString( SwMinMaxArgs
& rArg
, SwFont
* pFnt
, const OUString
&rText
,
962 sal_Int32 nIdx
, sal_Int32 nEnd
)
967 sal_Int32 nStop
= nIdx
;
968 LanguageType eLang
= pFnt
->GetLanguage();
969 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
971 bool bClear
= CH_BLANK
== rText
[ nStop
];
972 Boundary
aBndry( g_pBreakIt
->GetBreakIter()->getWordBoundary( rText
, nIdx
,
973 g_pBreakIt
->GetLocale( eLang
),
974 WordType::DICTIONARY_WORD
, true ) );
975 nStop
= aBndry
.endPos
;
976 if (nIdx
<= aBndry
.startPos
&& nIdx
&& nIdx
- 1 != rArg
.m_nNoLineBreak
)
983 SwDrawTextInfo
aDrawInf(rArg
.m_pSh
, *rArg
.m_pOut
, rText
, nIdx
, nStop
- nIdx
);
984 tools::Long nCurrentWidth
= pFnt
->GetTextSize_( aDrawInf
).Width();
985 rArg
.m_nRowWidth
+= nCurrentWidth
;
990 rArg
.m_nWordWidth
+= nCurrentWidth
;
991 if (static_cast<tools::Long
>(rArg
.m_rAbsMin
) < rArg
.m_nWordWidth
)
992 rArg
.m_rAbsMin
= rArg
.m_nWordWidth
;
993 rArg
.Minimum(rArg
.m_nWordWidth
+ rArg
.m_nWordAdd
);
1001 bool SwTextNode::IsSymbolAt(const sal_Int32 nBegin
) const
1003 SwScriptInfo aScriptInfo
;
1004 SwAttrIter
aIter( *const_cast<SwTextNode
*>(this), aScriptInfo
);
1005 aIter
.Seek( TextFrameIndex(nBegin
) );
1006 return aIter
.GetFnt()->IsSymbol( getIDocumentLayoutAccess().GetCurrentViewShell() );
1011 class SwMinMaxNodeArgs
1014 sal_uLong m_nMaxWidth
; // sum of all frame widths
1015 tools::Long m_nMinWidth
; // biggest frame
1016 tools::Long m_nLeftRest
; // space not already covered by frames in the left margin
1017 tools::Long m_nRightRest
; // space not already covered by frames in the right margin
1018 tools::Long m_nLeftDiff
; // Min/Max-difference of the frame in the left margin
1019 tools::Long m_nRightDiff
; // Min/Max-difference of the frame in the right margin
1020 SwNodeOffset m_nIndex
; // index of the node
1021 void Minimum( tools::Long nNew
) {
1022 if (nNew
> m_nMinWidth
)
1029 static void lcl_MinMaxNode(SwFrameFormat
* pNd
, SwMinMaxNodeArgs
& rIn
)
1031 const SwFormatAnchor
& rFormatA
= pNd
->GetAnchor();
1033 if ((RndStdIds::FLY_AT_PARA
!= rFormatA
.GetAnchorId()) &&
1034 (RndStdIds::FLY_AT_CHAR
!= rFormatA
.GetAnchorId()))
1039 const SwNode
*pAnchorNode
= rFormatA
.GetAnchorNode();
1040 OSL_ENSURE(pAnchorNode
, "Unexpected NULL arguments");
1041 if (!pAnchorNode
|| rIn
.m_nIndex
!= pAnchorNode
->GetIndex())
1044 tools::Long nMin
, nMax
;
1045 SwHTMLTableLayout
*pLayout
= nullptr;
1046 const bool bIsDrawFrameFormat
= pNd
->Which()==RES_DRAWFRMFMT
;
1047 if( !bIsDrawFrameFormat
)
1049 // Does the frame contain a table at the start or the end?
1050 const SwNodes
& rNodes
= pNd
->GetDoc()->GetNodes();
1051 const SwFormatContent
& rFlyContent
= pNd
->GetContent();
1052 SwNodeOffset nStt
= rFlyContent
.GetContentIdx()->GetIndex();
1053 SwTableNode
* pTableNd
= rNodes
[nStt
+1]->GetTableNode();
1056 SwNode
*pNd2
= rNodes
[nStt
];
1057 pNd2
= rNodes
[pNd2
->EndOfSectionIndex()-1];
1058 if( pNd2
->IsEndNode() )
1059 pTableNd
= pNd2
->StartOfSectionNode()->GetTableNode();
1063 pLayout
= pTableNd
->GetTable().GetHTMLTableLayout();
1066 const SwFormatHoriOrient
& rOrient
= pNd
->GetHoriOrient();
1067 sal_Int16 eHoriOri
= rOrient
.GetHoriOrient();
1072 nMin
= pLayout
->GetMin();
1073 nMax
= pLayout
->GetMax();
1074 nDiff
= nMax
- nMin
;
1078 if( bIsDrawFrameFormat
)
1080 const SdrObject
* pSObj
= pNd
->FindSdrObject();
1082 nMin
= pSObj
->GetCurrentBoundRect().GetWidth();
1089 const SwFormatFrameSize
&rSz
= pNd
->GetFrameSize();
1090 nMin
= rSz
.GetWidth();
1096 const SvxLRSpaceItem
&rLR
= pNd
->GetLRSpace();
1097 nMin
+= rLR
.ResolveLeft({});
1098 nMin
+= rLR
.ResolveRight({});
1099 nMax
+= rLR
.ResolveLeft({});
1100 nMax
+= rLR
.ResolveRight({});
1102 if( css::text::WrapTextMode_THROUGH
== pNd
->GetSurround().GetSurround() )
1104 rIn
.Minimum( nMin
);
1108 // Frames, which are left- or right-aligned are only party considered
1109 // when calculating the maximum, since the border is already being considered.
1110 // Only if the frame extends into the text body, this part is being added
1113 case text::HoriOrientation::RIGHT
:
1117 rIn
.m_nRightRest
-= rIn
.m_nRightDiff
;
1118 rIn
.m_nRightDiff
= nDiff
;
1120 if( text::RelOrientation::FRAME
!= rOrient
.GetRelationOrient() )
1122 if (rIn
.m_nRightRest
> 0)
1123 rIn
.m_nRightRest
= 0;
1125 rIn
.m_nRightRest
-= nMin
;
1128 case text::HoriOrientation::LEFT
:
1132 rIn
.m_nLeftRest
-= rIn
.m_nLeftDiff
;
1133 rIn
.m_nLeftDiff
= nDiff
;
1135 if (text::RelOrientation::FRAME
!= rOrient
.GetRelationOrient() && rIn
.m_nLeftRest
< 0)
1136 rIn
.m_nLeftRest
= 0;
1137 rIn
.m_nLeftRest
-= nMin
;
1142 rIn
.m_nMaxWidth
+= nMax
;
1148 #define FLYINCNT_MIN_WIDTH 284
1151 * Changing this method very likely requires changing of GetScalingOfSelectedText
1152 * This one is called exclusively from import filters, so there is no layout.
1154 void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex
, sal_uLong
& rMin
, sal_uLong
&rMax
,
1155 sal_uLong
& rAbsMin
) const
1157 SwViewShell
const * pSh
= GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
1158 OutputDevice
* pOut
= nullptr;
1160 pOut
= pSh
->GetWin()->GetOutDev();
1162 pOut
= Application::GetDefaultDevice();
1164 MapMode
aOldMap( pOut
->GetMapMode() );
1165 pOut
->SetMapMode( MapMode( MapUnit::MapTwip
) );
1171 SvxTextLeftMarginItem
const& rTextLeftMargin(GetSwAttrSet().GetTextLeftMargin());
1172 SvxRightMarginItem
const& rRightMargin(GetSwAttrSet().GetRightMargin());
1173 tools::Long nLROffset
= rTextLeftMargin
.ResolveTextLeft({}) + GetLeftMarginWithNum(true);
1175 // For enumerations a negative first line indentation is probably filled already
1176 if (!GetFirstLineOfsWithNum(nFLOffs
, {}) || nFLOffs
> nLROffset
)
1177 nLROffset
= nFLOffs
;
1179 SwMinMaxNodeArgs aNodeArgs
;
1180 aNodeArgs
.m_nMinWidth
= 0;
1181 aNodeArgs
.m_nMaxWidth
= 0;
1182 aNodeArgs
.m_nLeftRest
= nLROffset
;
1183 aNodeArgs
.m_nRightRest
= rRightMargin
.ResolveRight({});
1184 aNodeArgs
.m_nLeftDiff
= 0;
1185 aNodeArgs
.m_nRightDiff
= 0;
1188 sw::SpzFrameFormats
* pSpzs
= const_cast<sw::SpzFrameFormats
*>(GetDoc().GetSpzFrameFormats());
1191 aNodeArgs
.m_nIndex
= nIndex
;
1192 for(auto pFormat
: *pSpzs
)
1193 lcl_MinMaxNode(pFormat
, aNodeArgs
);
1196 if (aNodeArgs
.m_nLeftRest
< 0)
1197 aNodeArgs
.Minimum(nLROffset
- aNodeArgs
.m_nLeftRest
);
1198 aNodeArgs
.m_nLeftRest
-= aNodeArgs
.m_nLeftDiff
;
1199 if (aNodeArgs
.m_nLeftRest
< 0)
1200 aNodeArgs
.m_nMaxWidth
-= aNodeArgs
.m_nLeftRest
;
1202 if (aNodeArgs
.m_nRightRest
< 0)
1203 aNodeArgs
.Minimum(rRightMargin
.ResolveRight({}) - aNodeArgs
.m_nRightRest
);
1204 aNodeArgs
.m_nRightRest
-= aNodeArgs
.m_nRightDiff
;
1205 if (aNodeArgs
.m_nRightRest
< 0)
1206 aNodeArgs
.m_nMaxWidth
-= aNodeArgs
.m_nRightRest
;
1208 SwScriptInfo aScriptInfo
;
1209 SwAttrIter
aIter( *const_cast<SwTextNode
*>(this), aScriptInfo
);
1210 TextFrameIndex
nIdx(0);
1211 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1212 TextFrameIndex
nLen(m_Text
.getLength());
1213 tools::Long nCurrentWidth
= 0;
1214 tools::Long nAdd
= 0;
1215 SwMinMaxArgs
aArg( pOut
, pSh
, rMin
, rAbsMin
);
1216 while( nIdx
< nLen
)
1218 TextFrameIndex nNextChg
= aIter
.GetNextAttr();
1219 TextFrameIndex nStop
= aScriptInfo
.NextScriptChg( nIdx
);
1220 if( nNextChg
> nStop
)
1222 SwTextAttr
*pHint
= nullptr;
1223 sal_Unicode cChar
= CH_BLANK
;
1225 while( nStop
< nLen
&& nStop
< nNextChg
&&
1226 CH_TAB
!= (cChar
= m_Text
[sal_Int32(nStop
)]) &&
1227 CH_BREAK
!= cChar
&& CHAR_HARDBLANK
!= cChar
&&
1228 CHAR_HARDHYPHEN
!= cChar
&& CHAR_SOFTHYPHEN
!= cChar
&&
1229 CH_TXT_ATR_INPUTFIELDSTART
!= cChar
&&
1230 CH_TXT_ATR_INPUTFIELDEND
!= cChar
&&
1231 CH_TXT_ATR_FORMELEMENT
!= cChar
&&
1232 CH_TXT_ATR_FIELDSTART
!= cChar
&&
1233 CH_TXT_ATR_FIELDSEP
!= cChar
&&
1234 CH_TXT_ATR_FIELDEND
!= cChar
&&
1237 // this looks like some defensive programming to handle dummy char
1238 // with missing hint? but it's rather silly because it may pass the
1239 // dummy char to lcl_MinMaxString in that case...
1240 if( ( CH_TXTATR_BREAKWORD
!= cChar
&& CH_TXTATR_INWORD
!= cChar
)
1241 || ( nullptr == ( pHint
= aIter
.GetAttr( nStop
) ) ) )
1244 if (lcl_MinMaxString(aArg
, aIter
.GetFnt(), m_Text
, sal_Int32(nIdx
), sal_Int32(nStop
)))
1249 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1254 if (static_cast<tools::Long
>(rMax
) < aArg
.m_nRowWidth
)
1255 rMax
= aArg
.m_nRowWidth
;
1256 aArg
.m_nRowWidth
= 0;
1258 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1264 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1267 case CHAR_SOFTHYPHEN
:
1270 case CHAR_HARDBLANK
:
1271 case CHAR_HARDHYPHEN
:
1273 OUString
sTmp( cChar
);
1274 SwDrawTextInfo
aDrawInf( pSh
,
1275 *pOut
, sTmp
, 0, 1, 0, false );
1276 nCurrentWidth
= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1277 aArg
.m_nWordWidth
+= nCurrentWidth
;
1278 aArg
.m_nRowWidth
+= nCurrentWidth
;
1279 if (static_cast<tools::Long
>(rAbsMin
) < aArg
.m_nWordWidth
)
1280 rAbsMin
= aArg
.m_nWordWidth
;
1281 aArg
.Minimum(aArg
.m_nWordWidth
+ aArg
.m_nWordAdd
);
1282 aArg
.m_nNoLineBreak
= sal_Int32(nIdx
++);
1285 case CH_TXTATR_BREAKWORD
:
1286 case CH_TXTATR_INWORD
:
1290 tools::Long nOldWidth
= aArg
.m_nWordWidth
;
1291 tools::Long nOldAdd
= aArg
.m_nWordAdd
;
1294 switch( pHint
->Which() )
1296 case RES_TXTATR_FLYCNT
:
1298 SwFrameFormat
*pFrameFormat
= pHint
->GetFlyCnt().GetFrameFormat();
1299 const SvxLRSpaceItem
&rLR
= pFrameFormat
->GetLRSpace();
1300 if( RES_DRAWFRMFMT
== pFrameFormat
->Which() )
1302 const SdrObject
* pSObj
= pFrameFormat
->FindSdrObject();
1304 nCurrentWidth
= pSObj
->GetCurrentBoundRect().GetWidth();
1310 const SwFormatFrameSize
& rTmpSize
= pFrameFormat
->GetFrameSize();
1311 if( RES_FLYFRMFMT
== pFrameFormat
->Which()
1312 && rTmpSize
.GetWidthPercent() )
1314 // This is a hack for the following situation: In the paragraph there's a
1315 // text frame with relative size. Then let's take 0.5 cm as minimum width
1316 // and USHRT_MAX as maximum width
1317 // It were cleaner and maybe necessary later on to iterate over the content
1318 // of the text frame and call GetMinMaxSize recursively
1319 nCurrentWidth
= FLYINCNT_MIN_WIDTH
; // 0.5 cm
1320 rMax
= std::max(rMax
, sal_uLong(USHRT_MAX
));
1323 nCurrentWidth
= pFrameFormat
->GetFrameSize().GetWidth();
1325 nCurrentWidth
+= rLR
.ResolveLeft({});
1326 nCurrentWidth
+= rLR
.ResolveRight({});
1327 aArg
.m_nWordAdd
= nOldWidth
+ nOldAdd
;
1328 aArg
.m_nWordWidth
= nCurrentWidth
;
1329 aArg
.m_nRowWidth
+= nCurrentWidth
;
1330 if (static_cast<tools::Long
>(rAbsMin
) < aArg
.m_nWordWidth
)
1331 rAbsMin
= aArg
.m_nWordWidth
;
1332 aArg
.Minimum(aArg
.m_nWordWidth
+ aArg
.m_nWordAdd
);
1335 case RES_TXTATR_FTN
:
1337 const OUString aText
= pHint
->GetFootnote().GetNumStr();
1338 if( lcl_MinMaxString( aArg
, aIter
.GetFnt(), aText
, 0,
1339 aText
.getLength() ) )
1344 case RES_TXTATR_FIELD
:
1345 case RES_TXTATR_ANNOTATION
:
1347 SwField
*pField
= const_cast<SwField
*>(pHint
->GetFormatField().GetField());
1348 const OUString aText
= pField
->ExpandField(true, nullptr);
1349 if( lcl_MinMaxString( aArg
, aIter
.GetFnt(), aText
, 0,
1350 aText
.getLength() ) )
1355 aArg
.m_nWordWidth
= nOldWidth
;
1356 aArg
.m_nWordAdd
= nOldAdd
;
1358 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1361 case CH_TXT_ATR_INPUTFIELDSTART
:
1362 case CH_TXT_ATR_INPUTFIELDEND
:
1363 case CH_TXT_ATR_FORMELEMENT
:
1364 case CH_TXT_ATR_FIELDSTART
:
1365 case CH_TXT_ATR_FIELDSEP
:
1366 case CH_TXT_ATR_FIELDEND
:
1367 { // just skip it and continue with the content...
1368 aIter
.SeekAndChgAttrIter( ++nIdx
, pOut
);
1373 if (static_cast<tools::Long
>(rMax
) < aArg
.m_nRowWidth
)
1374 rMax
= aArg
.m_nRowWidth
;
1376 nLROffset
+= rRightMargin
.ResolveRight({});
1378 rAbsMin
+= nLROffset
;
1382 if (static_cast<tools::Long
>(rMin
) < aNodeArgs
.m_nMinWidth
)
1383 rMin
= aNodeArgs
.m_nMinWidth
;
1384 if (static_cast<tools::Long
>(rAbsMin
) < aNodeArgs
.m_nMinWidth
)
1385 rAbsMin
= aNodeArgs
.m_nMinWidth
;
1386 rMax
+= aNodeArgs
.m_nMaxWidth
;
1389 if( rMax
< rMin
) // e.g. Frames with flow through only contribute to the minimum
1391 pOut
->SetMapMode( aOldMap
);
1395 * Calculates the width of the text part specified by nStart and nEnd,
1396 * the height of the line containing nStart is divided by this width,
1397 * indicating the scaling factor, if the text part is rotated.
1398 * Having CH_BREAKs in the text part, this method returns the scaling
1399 * factor for the longest of the text parts separated by the CH_BREAK
1401 * Changing this method very likely requires changing of "GetMinMaxSize"
1403 sal_uInt16
SwTextFrame::GetScalingOfSelectedText(
1404 TextFrameIndex nStart
, TextFrameIndex nEnd
)
1406 assert(GetOffset() <= nStart
&& (!GetFollow() || nStart
< GetFollow()->GetOffset()));
1407 SwViewShell
const*const pSh
= getRootFrame()->GetCurrShell();
1409 OutputDevice
*const pOut
= &pSh
->GetRefDev();
1412 MapMode
aOldMap( pOut
->GetMapMode() );
1413 pOut
->SetMapMode( MapMode( MapUnit::MapTwip
) );
1417 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
1419 SwScriptInfo aScriptInfo
;
1420 SwAttrIter
aIter(*GetTextNodeFirst(), aScriptInfo
, this);
1421 aIter
.SeekAndChgAttrIter( nStart
, pOut
);
1423 Boundary aBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1424 GetText(), sal_Int32(nStart
),
1425 g_pBreakIt
->GetLocale( aIter
.GetFnt()->GetLanguage() ),
1426 WordType::DICTIONARY_WORD
, true );
1428 if (sal_Int32(nStart
) == aBound
.startPos
)
1430 // cursor is at left or right border of word
1431 pOut
->SetMapMode( aOldMap
);
1435 nStart
= TextFrameIndex(aBound
.startPos
);
1436 nEnd
= TextFrameIndex(aBound
.endPos
);
1440 pOut
->SetMapMode( aOldMap
);
1445 SwScriptInfo aScriptInfo
;
1446 SwAttrIter
aIter(*GetTextNodeFirst(), aScriptInfo
, this);
1448 // We do not want scaling attributes to be considered during this
1449 // calculation. For this, we push a temporary scaling attribute with
1450 // scaling value 100 and priority flag on top of the scaling stack
1451 SwAttrHandler
& rAH
= aIter
.GetAttrHandler();
1452 SvxCharScaleWidthItem
aItem(100, RES_CHRATR_SCALEW
);
1453 SwTextAttrEnd
aAttr( SfxPoolItemHolder(getRootFrame()->GetCurrShell()->GetAttrPool(), &aItem
), 0, COMPLETE_STRING
);
1454 aAttr
.SetPriorityAttr( true );
1455 rAH
.PushAndChg( aAttr
, *(aIter
.GetFnt()) );
1457 TextFrameIndex nIdx
= nStart
;
1459 sal_uLong nWidth
= 0;
1460 sal_uLong nProWidth
= 0;
1462 while( nIdx
< nEnd
)
1464 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1466 // scan for end of portion
1467 TextFrameIndex
const nNextChg
= std::min(aIter
.GetNextAttr(), aScriptInfo
.NextScriptChg(nIdx
));
1469 TextFrameIndex nStop
= nIdx
;
1470 sal_Unicode cChar
= CH_BLANK
;
1471 SwTextAttr
* pHint
= nullptr;
1473 // stop at special characters in [ nIdx, nNextChg ]
1474 while( nStop
< nEnd
&& nStop
< nNextChg
)
1476 cChar
= GetText()[sal_Int32(nStop
)];
1479 CH_BREAK
== cChar
||
1480 CHAR_HARDBLANK
== cChar
||
1481 CHAR_HARDHYPHEN
== cChar
||
1482 CHAR_SOFTHYPHEN
== cChar
||
1483 CH_TXT_ATR_INPUTFIELDSTART
== cChar
||
1484 CH_TXT_ATR_INPUTFIELDEND
== cChar
||
1485 CH_TXT_ATR_FORMELEMENT
== cChar
||
1486 CH_TXT_ATR_FIELDSTART
== cChar
||
1487 CH_TXT_ATR_FIELDSEP
== cChar
||
1488 CH_TXT_ATR_FIELDEND
== cChar
||
1490 (CH_TXTATR_BREAKWORD
== cChar
|| CH_TXTATR_INWORD
== cChar
) &&
1491 (nullptr == (pHint
= aIter
.GetAttr(nStop
)))
1501 // calculate text widths up to cChar
1504 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, GetText(), sal_Int32(nIdx
), sal_Int32(nStop
- nIdx
));
1505 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1509 aIter
.SeekAndChgAttrIter( nIdx
, pOut
);
1511 if ( cChar
== CH_BREAK
)
1513 nWidth
= std::max( nWidth
, nProWidth
);
1517 else if ( cChar
== CH_TAB
)
1519 // tab receives width of one space
1520 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, OUStringChar(CH_BLANK
), 0, 1);
1521 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1524 else if ( cChar
== CHAR_SOFTHYPHEN
)
1526 else if ( cChar
== CHAR_HARDBLANK
|| cChar
== CHAR_HARDHYPHEN
)
1528 OUString
sTmp( cChar
);
1529 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, sTmp
, 0, 1);
1530 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1533 else if ( pHint
&& ( cChar
== CH_TXTATR_BREAKWORD
|| cChar
== CH_TXTATR_INWORD
) )
1535 switch( pHint
->Which() )
1537 case RES_TXTATR_FTN
:
1539 const OUString aText
= pHint
->GetFootnote().GetNumStr();
1540 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, aText
, 0, aText
.getLength());
1542 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1546 case RES_TXTATR_FIELD
:
1547 case RES_TXTATR_ANNOTATION
:
1549 SwField
*pField
= const_cast<SwField
*>(pHint
->GetFormatField().GetField());
1550 OUString
const aText
= pField
->ExpandField(true, getRootFrame());
1551 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, aText
, 0, aText
.getLength());
1553 nProWidth
+= aIter
.GetFnt()->GetTextSize_( aDrawInf
).Width();
1559 // any suggestions for a default action?
1564 else if (CH_TXT_ATR_INPUTFIELDSTART
== cChar
||
1565 CH_TXT_ATR_INPUTFIELDEND
== cChar
||
1566 CH_TXT_ATR_FORMELEMENT
== cChar
||
1567 CH_TXT_ATR_FIELDSTART
== cChar
||
1568 CH_TXT_ATR_FIELDSEP
== cChar
||
1569 CH_TXT_ATR_FIELDEND
== cChar
)
1570 { // just skip it and continue with the content...
1575 nWidth
= std::max( nWidth
, nProWidth
);
1577 // search for the line containing nStart
1580 SwTextInfo
aInf(this);
1581 SwTextIter
aLine(this, &aInf
);
1582 aLine
.CharToLine( nStart
);
1583 pOut
->SetMapMode( aOldMap
);
1584 return o3tl::narrowing
<sal_uInt16
>( nWidth
?
1585 ( ( 100 * aLine
.GetCurr()->Height() ) / nWidth
) : 0 );
1587 // no frame or no paragraph, we take the height of the character
1588 // at nStart as line height
1590 aIter
.SeekAndChgAttrIter( nStart
, pOut
);
1591 pOut
->SetMapMode( aOldMap
);
1593 SwDrawTextInfo
aDrawInf(pSh
, *pOut
, GetText(), sal_Int32(nStart
), 1);
1594 return o3tl::narrowing
<sal_uInt16
>( nWidth
? ((100 * aIter
.GetFnt()->GetTextSize_( aDrawInf
).Height()) / nWidth
) : 0 );
1597 std::vector
<SwFlyAtContentFrame
*> SwTextFrame::GetSplitFlyDrawObjs() const
1599 std::vector
<SwFlyAtContentFrame
*> aObjs
;
1600 const SwSortedObjs
* pSortedObjs
= GetDrawObjs();
1606 for (const auto& pSortedObj
: *pSortedObjs
)
1608 SwFlyFrame
* pFlyFrame
= pSortedObj
->DynCastFlyFrame();
1614 if (!pFlyFrame
->IsFlySplitAllowed())
1619 aObjs
.push_back(static_cast<SwFlyAtContentFrame
*>(pFlyFrame
));
1625 bool SwTextFrame::HasSplitFlyDrawObjs() const
1627 return !GetSplitFlyDrawObjs().empty();
1630 SwFlyAtContentFrame
* SwTextFrame::HasNonLastSplitFlyDrawObj() const
1632 const SwTextFrame
* pFollow
= GetFollow();
1638 if (mnOffset
!= pFollow
->GetOffset())
1643 // At this point we know what we're part of a chain that is an anchor for split fly frames, but
1644 // we're not the last one. See if we have a matching fly.
1646 // Look up the master of the anchor.
1647 const SwTextFrame
* pAnchor
= this;
1648 while (pAnchor
->IsFollow())
1650 pAnchor
= pAnchor
->FindMaster();
1652 for (const auto& pFly
: pAnchor
->GetSplitFlyDrawObjs())
1654 // Nominally all flys are anchored in the master; see if this fly is effectively anchored in
1656 SwTextFrame
* pFlyAnchor
= pFly
->FindAnchorCharFrame();
1657 if (pFlyAnchor
!= this)
1661 if (pFly
->GetFollow())
1670 bool SwTextFrame::IsEmptyMasterWithSplitFly() const
1672 if (!IsEmptyMaster())
1677 if (!m_pDrawObjs
|| m_pDrawObjs
->size() != 1)
1682 SwFlyFrame
* pFlyFrame
= (*m_pDrawObjs
)[0]->DynCastFlyFrame();
1683 if (!pFlyFrame
|| !pFlyFrame
->IsFlySplitAllowed())
1688 if (mnOffset
!= GetFollow()->GetOffset())
1696 bool SwTextFrame::IsEmptyWithSplitFly() const
1703 if (SvxBreak
const eBreak
= GetBreakItem().GetBreak();
1704 eBreak
== SvxBreak::ColumnBefore
|| eBreak
== SvxBreak::ColumnBoth
1705 || eBreak
== SvxBreak::PageBefore
|| eBreak
== SvxBreak::PageBoth
1706 || GetPageDescItem().GetPageDesc() != nullptr)
1711 SwRectFnSet
fnUpper(GetUpper());
1712 if (fnUpper
.YDiff(fnUpper
.GetBottom(getFrameArea()), fnUpper
.GetPrtBottom(*GetUpper())) <= 0)
1717 // This is a master that doesn't fit the current parent.
1718 if (!m_pDrawObjs
|| m_pDrawObjs
->size() != 1)
1723 SwFlyFrame
* pFlyFrame
= (*m_pDrawObjs
)[0]->DynCastFlyFrame();
1724 if (!pFlyFrame
|| !pFlyFrame
->IsFlySplitAllowed())
1729 // It has a split fly anchored to it.
1730 if (pFlyFrame
->GetFrameFormat()->GetVertOrient().GetPos() >= 0)
1735 // Negative vertical offset means that visually it already may have a first line.
1736 // Consider that, we may need to split the frame, so the fly frame is on one page and the empty
1737 // paragraph's frame is on a next page.
1741 SwTwips
SwTextNode::GetWidthOfLeadingTabs() const
1747 while ( nIdx
< GetText().getLength() )
1749 const sal_Unicode cCh
= GetText()[nIdx
];
1750 if ( cCh
!='\t' && cCh
!=' ' )
1759 SwPosition
aPos( *this, nIdx
);
1761 // Find the non-follow text frame:
1762 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*this);
1763 for( SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next() )
1765 // Only consider master frames:
1766 if (!pFrame
->IsFollow() &&
1767 pFrame
->GetTextNodeForFirstText() == this)
1769 SwRectFnSet
aRectFnSet(pFrame
);
1771 pFrame
->GetCharRect( aRect
, aPos
);
1772 nRet
= pFrame
->IsRightToLeft() ?
1773 aRectFnSet
.GetPrtRight(*pFrame
) - aRectFnSet
.GetRight(aRect
) :
1774 aRectFnSet
.GetLeft(aRect
) - aRectFnSet
.GetPrtLeft(*pFrame
);
1783 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */