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 <editeng/lspcitem.hxx>
21 #include <editeng/adjustitem.hxx>
22 #include <editeng/escapementitem.hxx>
23 #include <editeng/lrspitem.hxx>
24 #include <editeng/pgrditem.hxx>
25 #include <vcl/svapp.hxx>
26 #include <comphelper/scopeguard.hxx>
29 #include <viewopt.hxx>
31 #include <pagefrm.hxx>
33 #include <SwPortionHandler.hxx>
36 #include "txtpaint.hxx"
37 #include <swfntcch.hxx>
38 #include <tgrditem.hxx>
39 #include <pagedesc.hxx>
41 #include "redlnitr.hxx"
42 #include "atrhndl.hxx"
43 #include <rootfrm.hxx>
44 #include <formatlinebreak.hxx>
45 #include <txatbase.hxx>
47 #include <IDocumentRedlineAccess.hxx>
48 #include <IDocumentSettingAccess.hxx>
49 #include <IDocumentDeviceAccess.hxx>
52 #include <swtypes.hxx>
53 #include <strings.hrc>
54 #include <flyfrms.hxx>
55 #include <bodyfrm.hxx>
57 SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion
&rPortion
,
58 const FontLineStyle eUL
,
59 const FontStrikeout eStrkout
,
61 m_eUnderline( eUL
), m_eStrikeout( eStrkout
), m_aColor( rCol
)
63 Height( rPortion
.Height() );
64 SetAscent( rPortion
.GetAscent() );
65 SetWhichPor( PortionType::TempEnd
);
68 void SwTmpEndPortion::Paint( const SwTextPaintInfo
&rInf
) const
70 if (!(rInf
.OnWin() && rInf
.GetOpt().IsParagraph()))
73 const SwFont
* pOldFnt
= rInf
.GetFont();
75 SwFont
aFont(*pOldFnt
);
77 // Paint strikeout/underline based on redline color and settings
78 // (with an extra pilcrow in the background, because there is
79 // no SetStrikeoutColor(), also SetUnderColor() doesn't work()).
80 if ( m_eUnderline
!= LINESTYLE_NONE
|| m_eStrikeout
!= STRIKEOUT_NONE
)
82 aFont
.SetColor( m_aColor
);
83 aFont
.SetUnderline( m_eUnderline
);
84 aFont
.SetStrikeout( m_eStrikeout
);
86 const_cast<SwTextPaintInfo
&>(rInf
).SetFont(&aFont
);
88 // draw the pilcrow with strikeout/underline in redline color
89 rInf
.DrawText(CH_PAR
, *this);
93 aFont
.SetColor( NON_PRINTING_CHARACTER_COLOR
);
94 aFont
.SetStrikeout( STRIKEOUT_NONE
);
95 aFont
.SetUnderline( LINESTYLE_NONE
);
96 const_cast<SwTextPaintInfo
&>(rInf
).SetFont(&aFont
);
99 rInf
.DrawText(CH_PAR
, *this);
101 const_cast<SwTextPaintInfo
&>(rInf
).SetFont(const_cast<SwFont
*>(pOldFnt
));
104 SwBreakPortion::SwBreakPortion( const SwLinePortion
&rPortion
, const SwTextAttr
* pAttr
)
105 : SwLinePortion( rPortion
)
107 mnLineLength
= TextFrameIndex(1);
108 m_eRedline
= RedlineType::None
;
109 SetWhichPor( PortionType::Break
);
111 m_eClear
= SwLineBreakClear::NONE
;
112 if (pAttr
&& pAttr
->Which() == RES_TXTATR_LINEBREAK
)
114 m_eClear
= pAttr
->GetLineBreak().GetValue();
119 TextFrameIndex
SwBreakPortion::GetModelPositionForViewPoint(const sal_uInt16
) const
121 return TextFrameIndex(0);
124 sal_uInt16
SwBreakPortion::GetViewWidth( const SwTextSizeInfo
& ) const
127 SwLinePortion
*SwBreakPortion::Compress()
128 { return (GetNextPortion() && GetNextPortion()->InTextGrp() ? nullptr : this); }
130 void SwBreakPortion::Paint( const SwTextPaintInfo
&rInf
) const
132 if( !(rInf
.OnWin() && rInf
.GetOpt().IsLineBreak()) )
135 // Reduce height to text height for the duration of the print, so the vertical height will look
136 // correct for the line break character, even for clearing breaks.
137 SwTwips nHeight
= Height();
138 SwTwips nVertPosOffset
= (nHeight
- m_nTextHeight
) / 2;
139 auto pPortion
= const_cast<SwBreakPortion
*>(this);
140 pPortion
->Height(m_nTextHeight
, false);
141 if (rInf
.GetTextFrame()->IsVertical())
143 // Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case.
144 const_cast<SwTextPaintInfo
&>(rInf
).Y(rInf
.Y() + nVertPosOffset
);
146 comphelper::ScopeGuard
g(
147 [pPortion
, nHeight
, &rInf
, nVertPosOffset
]
149 if (rInf
.GetTextFrame()->IsVertical())
151 const_cast<SwTextPaintInfo
&>(rInf
).Y(rInf
.Y() - nVertPosOffset
);
153 pPortion
->Height(nHeight
, false);
156 rInf
.DrawLineBreak( *this );
159 if (m_eRedline
== RedlineType::None
)
162 sal_Int16 nNoBreakWidth
= rInf
.GetTextSize(S_NOBREAK_FOR_REDLINE
).Width();
163 if ( nNoBreakWidth
> 0 )
165 // approximate portion size with multiple no-break spaces
166 // and draw these spaces (at least a single one) by DrawText
167 // painting the requested redline underline/strikeout
168 sal_Int16 nSpaces
= (LINE_BREAK_WIDTH
+ nNoBreakWidth
/2) / nNoBreakWidth
;
169 OUStringBuffer
aBuf(S_NOBREAK_FOR_REDLINE
);
170 for (sal_Int16 i
= 1; i
< nSpaces
; ++i
)
171 aBuf
.append(S_NOBREAK_FOR_REDLINE
);
173 const SwFont
* pOldFnt
= rInf
.GetFont();
175 SwFont
aFont(*pOldFnt
);
177 if (m_eRedline
== RedlineType::Delete
)
178 aFont
.SetUnderline( LINESTYLE_NONE
);
180 aFont
.SetStrikeout( STRIKEOUT_NONE
);
182 const_cast<SwTextPaintInfo
&>(rInf
).SetFont(&aFont
);
184 rInf
.DrawText(aBuf
.makeStringAndClear(), *this);
186 const_cast<SwTextPaintInfo
&>(rInf
).SetFont(const_cast<SwFont
*>(pOldFnt
));
190 bool SwBreakPortion::Format( SwTextFormatInfo
&rInf
)
192 const SwLinePortion
*pRoot
= rInf
.GetRoot();
194 Height( pRoot
->Height() );
195 m_nTextHeight
= Height();
197 // See if this is a clearing break. If so, calculate how much we need to "jump down" so the next
198 // line can again use the full text width.
199 SwLineBreakClear eClear
= m_eClear
;
200 if (rInf
.GetTextFrame()->IsRightToLeft() && eClear
!= SwLineBreakClear::ALL
)
202 // RTL ignores left/right breaks.
203 eClear
= SwLineBreakClear::NONE
;
205 if (eClear
!= SwLineBreakClear::NONE
)
207 SwTextFly
& rTextFly
= rInf
.GetTextFly();
210 SwTwips nHeight
= rTextFly
.GetMaxBottom(*this, rInf
) - rInf
.Y();
211 if (nHeight
> Height())
213 Height(nHeight
, /*bText=*/false);
218 SetAscent( pRoot
->GetAscent() );
219 if (rInf
.GetIdx() + TextFrameIndex(1) == TextFrameIndex(rInf
.GetText().getLength()))
220 rInf
.SetNewLine( true );
224 void SwBreakPortion::HandlePortion( SwPortionHandler
& rPH
) const
226 rPH
.Text( GetLen(), GetWhichPor() );
229 void SwBreakPortion::dumpAsXml(xmlTextWriterPtr pWriter
, const OUString
& rText
, TextFrameIndex
&
232 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwBreakPortion"));
233 dumpAsXmlAttributes(pWriter
, rText
, nOffset
);
236 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("text-height"),
237 BAD_CAST(OString::number(m_nTextHeight
).getStr()));
239 (void)xmlTextWriterEndElement(pWriter
);
242 SwLineBreakClear
SwBreakPortion::GetClear() const { return m_eClear
; }
244 SwKernPortion::SwKernPortion( SwLinePortion
&rPortion
, short nKrn
,
245 bool bBG
, bool bGK
) :
246 m_nKern( nKrn
), m_bBackground( bBG
), m_bGridKern( bGK
)
248 Height( rPortion
.Height() );
249 SetAscent( rPortion
.GetAscent() );
250 mnLineLength
= TextFrameIndex(0);
251 SetWhichPor( PortionType::Kern
);
254 rPortion
.Insert( this );
257 SwKernPortion::SwKernPortion( const SwLinePortion
& rPortion
) :
258 m_nKern( 0 ), m_bBackground( false ), m_bGridKern( true )
260 Height( rPortion
.Height() );
261 SetAscent( rPortion
.GetAscent() );
263 mnLineLength
= TextFrameIndex(0);
264 SetWhichPor( PortionType::Kern
);
267 void SwKernPortion::Paint( const SwTextPaintInfo
&rInf
) const
272 // bBackground is set for Kerning Portions between two fields
274 rInf
.DrawViewOpt( *this, PortionType::Field
);
276 rInf
.DrawBackBrush( *this );
277 if (GetJoinBorderWithNext() ||GetJoinBorderWithPrev())
278 rInf
.DrawBorder( *this );
280 // do we have to repaint a post it portion?
281 if( rInf
.OnWin() && mpNextPortion
&& !mpNextPortion
->Width() )
282 mpNextPortion
->PrePaint( rInf
, this );
284 if( rInf
.GetFont()->IsPaintBlank() )
287 rInf
.CalcRect( *this, &aClipRect
);
288 SwSaveClip
aClip( const_cast<OutputDevice
*>(rInf
.GetOut()) );
289 aClip
.ChgClip( aClipRect
);
290 rInf
.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(2), true );
294 void SwKernPortion::FormatEOL( SwTextFormatInfo
&rInf
)
299 if( rInf
.GetLast() == this )
300 rInf
.SetLast( FindPrevPortion( rInf
.GetRoot() ) );
305 rInf
.GetLast()->FormatEOL( rInf
);
308 SwArrowPortion::SwArrowPortion( const SwLinePortion
&rPortion
) :
311 Height( rPortion
.Height() );
312 SetAscent( rPortion
.GetAscent() );
313 mnLineLength
= TextFrameIndex(0);
314 SetWhichPor( PortionType::Arrow
);
317 SwArrowPortion::SwArrowPortion( const SwTextPaintInfo
&rInf
)
320 Height( o3tl::narrowing
<sal_uInt16
>(rInf
.GetTextFrame()->getFramePrintArea().Height()) );
321 m_aPos
.setX( rInf
.GetTextFrame()->getFrameArea().Left() +
322 rInf
.GetTextFrame()->getFramePrintArea().Right() );
323 m_aPos
.setY( rInf
.GetTextFrame()->getFrameArea().Top() +
324 rInf
.GetTextFrame()->getFramePrintArea().Bottom() );
325 SetWhichPor( PortionType::Arrow
);
328 void SwArrowPortion::Paint( const SwTextPaintInfo
&rInf
) const
330 const_cast<SwArrowPortion
*>(this)->m_aPos
= rInf
.GetPos();
333 SwLinePortion
*SwArrowPortion::Compress() { return this; }
335 SwTwips
SwTextFrame::EmptyHeight() const
338 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
339 if ( auto pCrSh
= dynamic_cast<SwCursorShell
*>( pSh
) ) {
340 // this is called during formatting so avoid recursive layout
341 SwContentFrame
const*const pCurrFrame
= pCrSh
->GetCurrFrame(false);
342 if (pCurrFrame
==static_cast<SwContentFrame
const *>(this)) {
351 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::EmptyHeight with swapped frame" );
353 std::unique_ptr
<SwFont
> pFnt
;
354 const SwTextNode
& rTextNode
= *GetTextNodeForParaProps();
355 const IDocumentSettingAccess
* pIDSA
= rTextNode
.getIDocumentSettingAccess();
356 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
357 if ( rTextNode
.HasSwAttrSet() )
359 const SwAttrSet
*pAttrSet
= &( rTextNode
.GetSwAttrSet() );
360 pFnt
.reset(new SwFont( pAttrSet
, pIDSA
));
364 SwFontAccess
aFontAccess( &rTextNode
.GetAnyFormatColl(), pSh
);
365 pFnt
.reset(new SwFont( aFontAccess
.Get()->GetFont() ));
366 pFnt
->CheckFontCacheId( pSh
, pFnt
->GetActual() );
370 pFnt
->SetVertical( 2700_deg10
);
372 OutputDevice
* pOut
= pSh
? pSh
->GetOut() : nullptr;
373 if ( !pOut
|| !pSh
->GetViewOptions()->getBrowseMode() ||
374 pSh
->GetViewOptions()->IsPrtFormat() )
376 pOut
= rTextNode
.getIDocumentDeviceAccess().getReferenceDevice(true);
379 const IDocumentRedlineAccess
& rIDRA
= rTextNode
.getIDocumentRedlineAccess();
380 if (IDocumentRedlineAccess::IsShowChanges(rIDRA
.GetRedlineFlags())
381 && !getRootFrame()->IsHideRedlines())
383 const SwRedlineTable::size_type nRedlPos
= rIDRA
.GetRedlinePos( rTextNode
, RedlineType::Any
);
384 if( SwRedlineTable::npos
!= nRedlPos
)
386 SwAttrHandler aAttrHandler
;
387 aAttrHandler
.Init(rTextNode
.GetSwAttrSet(),
388 *rTextNode
.getIDocumentSettingAccess());
389 SwRedlineItr
aRedln( rTextNode
, *pFnt
, aAttrHandler
,
390 nRedlPos
, SwRedlineItr::Mode::Show
);
396 nRet
= IsVertical() ?
397 getFramePrintArea().SSize().Width() + 1 :
398 getFramePrintArea().SSize().Height() + 1;
401 pFnt
->SetFntChg( true );
402 pFnt
->ChgPhysFnt( pSh
, *pOut
);
403 nRet
= pFnt
->GetHeight( pSh
, *pOut
);
408 bool SwTextFrame::FormatEmpty()
410 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::FormatEmpty with swapped frame" );
412 bool bCollapse
= EmptyHeight( ) == 1 && IsCollapse( );
414 // sw_redlinehide: just disable FormatEmpty optimisation for now
415 // Split fly frames: non-last parts of the anchor want this optimization to clear the old
417 SwFlyAtContentFrame
* pNonLastSplitFlyDrawObj
= HasNonLastSplitFlyDrawObj();
418 bool bHasNonLastSplitFlyDrawObj
= pNonLastSplitFlyDrawObj
!= nullptr;
419 if ((HasFollow() && !bHasNonLastSplitFlyDrawObj
) || GetMergedPara() || (GetTextNodeFirst()->GetpSwpHints() && !bHasNonLastSplitFlyDrawObj
) ||
420 nullptr != GetTextNodeForParaProps()->GetNumRule() ||
421 GetTextNodeFirst()->HasHiddenCharAttribute(true) ||
422 IsInFootnote() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
424 const SwAttrSet
& aSet
= GetTextNodeForParaProps()->GetSwAttrSet();
425 const SvxAdjust nAdjust
= aSet
.GetAdjust().GetAdjust();
426 if( !bCollapse
&& ( ( ( ! IsRightToLeft() && ( SvxAdjust::Left
!= nAdjust
) ) ||
427 ( IsRightToLeft() && ( SvxAdjust::Right
!= nAdjust
) ) ) ||
428 aSet
.GetRegister().GetValue() ) )
430 const SvxLineSpacingItem
&rSpacing
= aSet
.GetLineSpacing();
431 if( !bCollapse
&& ( SvxLineSpaceRule::Min
== rSpacing
.GetLineSpaceRule() ||
432 SvxLineSpaceRule::Fix
== rSpacing
.GetLineSpaceRule() ||
433 aSet
.GetFirstLineIndent().IsAutoFirst()))
438 SwTextFly
aTextFly( this );
440 bool bFirstFlyCheck
= 0 != getFramePrintArea().Height();
441 if ( !bCollapse
&& bFirstFlyCheck
&&
442 aTextFly
.IsOn() && aTextFly
.IsAnyObj( aRect
) && !bHasNonLastSplitFlyDrawObj
)
445 if (IsEmptyWithSplitFly())
447 // We don't want this optimization in case the paragraph is not really empty, because it has
448 // a fly frame and it also needs space for the empty paragraph in a next line.
452 // only need to check one node because of early return on GetMerged()
453 for (SwContentIndex
const* pIndex
= GetTextNodeFirst()->GetFirstIndex();
454 pIndex
; pIndex
= pIndex
->GetNext())
456 sw::mark::IMark
const*const pMark
= pIndex
->GetMark();
457 if (dynamic_cast<const sw::mark::IBookmark
*>(pMark
) != nullptr)
458 { // need bookmark portions!
463 SwTwips nHeight
= EmptyHeight();
465 if (aSet
.GetParaGrid().GetValue() &&
468 SwTextGridItem
const*const pGrid(GetGridItem(FindPageFrame()));
470 nHeight
= pGrid
->GetBaseHeight() + pGrid
->GetRubyHeight();
473 SwRectFnSet
aRectFnSet(this);
474 SwTwips nChg
= nHeight
- aRectFnSet
.GetHeight(getFramePrintArea());
475 const SwBodyFrame
* pBody
= FindBodyFrame();
476 if (pNonLastSplitFlyDrawObj
&& pBody
)
478 // See if we need to increase the text frame height due to split flys. This is necessary for
479 // anchors of inner floating tables, where moving to a next page moves indirectly, so we
480 // want a correct text frame height.
481 SwTwips nFrameBottom
= aRectFnSet
.GetBottom(getFrameArea()) + nChg
;
482 SwTwips nFlyBottom
= aRectFnSet
.GetBottom(pNonLastSplitFlyDrawObj
->getFrameArea());
483 SwTwips nBodyBottom
= aRectFnSet
.GetBottom(pBody
->getFrameArea());
484 if (nFlyBottom
> nBodyBottom
)
486 // This is the legacy case where flys may overlap with footer frames.
487 nFlyBottom
= nBodyBottom
;
489 if (pNonLastSplitFlyDrawObj
->isFrameAreaPositionValid() && nFlyBottom
> nFrameBottom
)
491 nChg
+= (nFlyBottom
- nFrameBottom
);
496 SetUndersized( false );
499 if (GetHasRotatedPortions())
502 SetHasRotatedPortions(false);
511 if( !bCollapse
&& !bFirstFlyCheck
&&
512 aTextFly
.IsOn() && aTextFly
.IsAnyObj( aRect
) )
515 // #i35635# - call method <HideAndShowObjects()>
516 // to assure that objects anchored at the empty paragraph are
517 // correctly visible resp. invisible.
518 HideAndShowObjects();
522 bool SwTextFrame::FillRegister( SwTwips
& rRegStart
, sal_uInt16
& rRegDiff
)
524 const SwFrame
*pFrame
= this;
526 while( !( ( SwFrameType::Body
| SwFrameType::Fly
)
527 & pFrame
->GetType() ) && pFrame
->GetUpper() )
528 pFrame
= pFrame
->GetUpper();
529 if( ( SwFrameType::Body
| SwFrameType::Fly
) & pFrame
->GetType() )
531 SwRectFnSet
aRectFnSet(pFrame
);
532 rRegStart
= aRectFnSet
.GetPrtTop(*pFrame
);
533 pFrame
= pFrame
->FindPageFrame();
534 if( pFrame
->IsPageFrame() )
536 SwPageDesc
* pDesc
= const_cast<SwPageFrame
*>(static_cast<const SwPageFrame
*>(pFrame
))->FindPageDesc();
539 rRegDiff
= pDesc
->GetRegHeight();
542 const SwTextFormatColl
*pFormat
= pDesc
->GetRegisterFormatColl();
545 const SvxLineSpacingItem
&rSpace
= pFormat
->GetLineSpacing();
546 if( SvxLineSpaceRule::Fix
== rSpace
.GetLineSpaceRule() )
548 rRegDiff
= rSpace
.GetLineHeight();
549 pDesc
->SetRegHeight( rRegDiff
);
550 pDesc
->SetRegAscent( ( 4 * rRegDiff
) / 5 );
554 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
555 SwFontAccess
aFontAccess( pFormat
, pSh
);
556 SwFont
aFnt( aFontAccess
.Get()->GetFont() );
558 OutputDevice
*pOut
= nullptr;
559 if( !pSh
|| !pSh
->GetViewOptions()->getBrowseMode() ||
560 pSh
->GetViewOptions()->IsPrtFormat() )
561 pOut
= GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
564 pOut
= pSh
->GetWin()->GetOutDev();
567 pOut
= Application::GetDefaultDevice();
569 MapMode
aOldMap( pOut
->GetMapMode() );
570 pOut
->SetMapMode( MapMode( MapUnit::MapTwip
) );
572 aFnt
.ChgFnt( pSh
, *pOut
);
573 rRegDiff
= aFnt
.GetHeight( pSh
, *pOut
);
574 sal_uInt16 nNetHeight
= rRegDiff
;
576 switch( rSpace
.GetLineSpaceRule() )
578 case SvxLineSpaceRule::Auto
:
580 case SvxLineSpaceRule::Min
:
582 if( rRegDiff
< rSpace
.GetLineHeight() )
583 rRegDiff
= rSpace
.GetLineHeight();
587 OSL_FAIL( ": unknown LineSpaceRule" );
589 switch( rSpace
.GetInterLineSpaceRule() )
591 case SvxInterLineSpaceRule::Off
:
593 case SvxInterLineSpaceRule::Prop
:
595 tools::Long nTmp
= rSpace
.GetPropLineSpace();
597 nTmp
= nTmp
? 50 : 100;
602 rRegDiff
= o3tl::narrowing
<sal_uInt16
>(nTmp
);
603 nNetHeight
= rRegDiff
;
606 case SvxInterLineSpaceRule::Fix
:
608 rRegDiff
= rRegDiff
+ rSpace
.GetInterLineSpace();
609 nNetHeight
= rRegDiff
;
612 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
614 pDesc
->SetRegHeight( rRegDiff
);
615 pDesc
->SetRegAscent( rRegDiff
- nNetHeight
+
616 aFnt
.GetAscent( pSh
, *pOut
) );
617 pOut
->SetMapMode( aOldMap
);
621 const tools::Long nTmpDiff
= pDesc
->GetRegAscent() - rRegDiff
;
622 if ( aRectFnSet
.IsVert() )
623 rRegStart
-= nTmpDiff
;
625 rRegStart
+= nTmpDiff
;
629 return ( 0 != rRegDiff
);
632 void SwHiddenTextPortion::Paint( const SwTextPaintInfo
& rInf
) const
635 OutputDevice
* pOut
= const_cast<OutputDevice
*>(rInf
.GetOut());
636 Color
aCol( rInf
.GetOpt().GetFieldShadingsColor() );
637 Color
aOldColor( pOut
->GetFillColor() );
638 pOut
->SetFillColor( aCol
);
639 Point
aPos( rInf
.GetPos() );
640 aPos
.AdjustY( -150 );
642 SwRect
aRect( aPos
, Size( 100, 200 ) );
643 pOut
->DrawRect( aRect
.SVRect() );
644 pOut
->SetFillColor( aOldColor
);
650 bool SwHiddenTextPortion::Format( SwTextFormatInfo
&rInf
)
653 rInf
.GetTextFrame()->HideFootnotes( rInf
.GetIdx(), rInf
.GetIdx() + GetLen() );
658 bool SwControlCharPortion::DoPaint(SwTextPaintInfo
const& rTextPaintInfo
,
659 OUString
& rOutString
, SwFont
& rTmpFont
, int &) const
661 if (mcChar
== CHAR_WJ
|| !rTextPaintInfo
.GetOpt().IsFieldShadings())
669 rOutString
= "/"; break;
671 // rText = sal_Unicode(0x2514); break;
673 // rText = sal_Unicode(0x2518); break;
679 rTmpFont
.SetEscapement( CHAR_ZWSP
== mcChar
? DFLT_ESC_AUTO_SUB
: -25 );
680 const sal_uInt16 nProp
= 40;
681 rTmpFont
.SetProportion( nProp
); // a smaller font
686 bool SwBookmarkPortion::DoPaint(SwTextPaintInfo
const& rTextPaintInfo
,
687 OUString
& rOutString
, SwFont
& rFont
, int & rDeltaY
) const
689 // custom color is visible without field shading, too
690 if (!rTextPaintInfo
.GetOpt().IsShowBookmarks())
695 rOutString
= OUStringChar(mcChar
);
697 // init font: we want OpenSymbol to ensure it doesn't look too crazy;
698 // thin and a bit higher than the surrounding text
699 auto const nOrigAscent(rFont
.GetAscent(rTextPaintInfo
.GetVsh(), *rTextPaintInfo
.GetOut()));
700 rFont
.SetName("OpenSymbol", rFont
.GetActual());
701 Size
aSize(rFont
.GetSize(rFont
.GetActual()));
702 // use also the external leading (line gap) of the portion, but don't use
703 // 100% of it because i can't figure out how to baseline align that
704 assert(aSize
.Height() != 0);
705 auto const nFactor
= aSize
.Height() > 0 ? (Height() * 95) / aSize
.Height() : Height();
706 rFont
.SetProportion(nFactor
);
707 rFont
.SetWeight(WEIGHT_THIN
, rFont
.GetActual());
708 rFont
.SetColor(rTextPaintInfo
.GetOpt().GetFieldShadingsColor());
709 // reset these to default...
710 rFont
.SetAlign(ALIGN_BASELINE
);
711 rFont
.SetUnderline(LINESTYLE_NONE
);
712 rFont
.SetOverline(LINESTYLE_NONE
);
713 rFont
.SetStrikeout(STRIKEOUT_NONE
);
714 rFont
.SetOutline(false);
715 rFont
.SetShadow(false);
716 rFont
.SetTransparent(false);
717 rFont
.SetEmphasisMark(FontEmphasisMark::NONE
);
718 rFont
.SetEscapement(0);
719 rFont
.SetPitch(PITCH_DONTKNOW
, rFont
.GetActual());
720 rFont
.SetRelief(FontRelief::NONE
);
722 // adjust Y position to account for different baselines of the fonts
723 auto const nOSAscent(rFont
.GetAscent(rTextPaintInfo
.GetVsh(), *rTextPaintInfo
.GetOut()));
724 rDeltaY
= nOSAscent
- nOrigAscent
;
729 void SwControlCharPortion::Paint( const SwTextPaintInfo
&rInf
) const
731 if ( !Width() ) // is only set during prepaint mode
734 rInf
.DrawViewOpt(*this, GetWhichPor());
737 SwFont
aTmpFont( *rInf
.GetFont() );
741 && !rInf
.GetOpt().IsPagePreview()
742 && !rInf
.GetOpt().IsReadonly()
743 && DoPaint(rInf
, aOutString
, aTmpFont
, deltaY
)))
746 SwFontSave
aFontSave( rInf
, &aTmpFont
);
748 if ( !mnHalfCharWidth
)
749 mnHalfCharWidth
= rInf
.GetTextSize( aOutString
).Width() / 2;
751 Point aOldPos
= rInf
.GetPos();
752 Point
aNewPos( aOldPos
);
753 auto const deltaX((Width() / 2) - mnHalfCharWidth
);
754 switch (rInf
.GetFont()->GetOrientation(rInf
.GetTextFrame()->IsVertical()).get())
757 aNewPos
.AdjustX(deltaX
);
758 aNewPos
.AdjustY(deltaY
);
761 aNewPos
.AdjustY(-deltaX
);
762 aNewPos
.AdjustX(deltaY
);
765 aNewPos
.AdjustY(deltaX
);
766 aNewPos
.AdjustX(-deltaY
);
772 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aNewPos
);
774 rInf
.DrawText( aOutString
, *this );
776 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aOldPos
);
779 void SwBookmarkPortion::Paint( const SwTextPaintInfo
&rInf
) const
781 if ( !Width() ) // is only set during prepaint mode
784 rInf
.DrawViewOpt(*this, GetWhichPor());
787 SwFont
aTmpFont( *rInf
.GetFont() );
791 && !rInf
.GetOpt().IsPagePreview()
792 && !rInf
.GetOpt().IsReadonly()
793 && DoPaint(rInf
, aOutString
, aTmpFont
, deltaY
)))
796 SwFontSave
aFontSave( rInf
, &aTmpFont
);
798 if ( !mnHalfCharWidth
)
799 mnHalfCharWidth
= rInf
.GetTextSize( aOutString
).Width() / 2;
801 Point aOldPos
= rInf
.GetPos();
802 Point
aNewPos( aOldPos
);
803 auto const deltaX((Width() / 2) - mnHalfCharWidth
);
804 switch (rInf
.GetFont()->GetOrientation(rInf
.GetTextFrame()->IsVertical()).get())
807 aNewPos
.AdjustX(deltaX
);
808 aNewPos
.AdjustY(deltaY
);
811 aNewPos
.AdjustY(-deltaX
);
812 aNewPos
.AdjustX(deltaY
);
815 aNewPos
.AdjustY(deltaX
);
816 aNewPos
.AdjustX(-deltaY
);
823 // draw end marks before the character position
824 if ( m_nStart
== 0 || m_nEnd
== 0 )
826 // single type boundary marks are there outside of the bookmark text
830 aNewPos
.AdjustX(static_cast<tools::Long
>(mnHalfCharWidth
) * -2 * (m_oColors
.size() - 1));
832 else if ( m_nStart
!= 0 && m_nEnd
!= 0 )
833 // both end and start boundary marks: adjust them around the bookmark position
836 aNewPos
.AdjustX(static_cast<tools::Long
>(mnHalfCharWidth
) * -(2 * m_nEnd
- 1 + m_nPoint
) );
838 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aNewPos
);
840 for ( const auto& it
: m_oColors
)
842 // set bold for custom colored bookmark symbol
843 // and draw multiple symbols showing all custom colors
844 aTmpFont
.SetWeight( COL_TRANSPARENT
== std::get
<1>(it
) ? WEIGHT_THIN
: WEIGHT_BOLD
, aTmpFont
.GetActual() );
845 aTmpFont
.SetColor( COL_TRANSPARENT
== std::get
<1>(it
) ? rInf
.GetOpt().GetFieldShadingsColor() : std::get
<1>(it
) );
846 aOutString
= OUString(std::get
<0>(it
) == SwScriptInfo::MarkKind::Start
? '[' : ']');
848 // MarkKind::Point: drawn I-beam (e.g. U+2336) as overlapping ][
849 if ( std::get
<0>(it
) == SwScriptInfo::MarkKind::Point
)
851 aNewPos
.AdjustX(-mnHalfCharWidth
* 5/16);
852 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aNewPos
);
853 rInf
.DrawText( aOutString
, *this );
855 // when the overlapping vertical lines are 50 pixel width on the screen,
856 // this distance (half width * 5/8) still results precise overlapping
857 aNewPos
.AdjustX(mnHalfCharWidth
* 5/8);
858 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aNewPos
);
859 aOutString
= OUString('[');
861 rInf
.DrawText( aOutString
, *this );
862 // place the next symbol after the previous one
863 // TODO: fix orientation and start/end
864 aNewPos
.AdjustX(mnHalfCharWidth
* 2);
865 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aNewPos
);
868 const_cast< SwTextPaintInfo
& >( rInf
).SetPos( aOldPos
);
871 void SwBookmarkPortion::HandlePortion( SwPortionHandler
& rPH
) const
874 for ( const auto& it
: m_oColors
)
876 aStr
.append("#" + std::get
<2>(it
) + " " + SwResId(STR_BOOKMARK_DEF_NAME
));
877 switch (std::get
<0>(it
))
879 case SwScriptInfo::MarkKind::Point
:
881 case SwScriptInfo::MarkKind::Start
:
882 aStr
.append(" " + SwResId(STR_CAPTION_BEGINNING
));
884 case SwScriptInfo::MarkKind::End
:
885 aStr
.append(" " + SwResId(STR_CAPTION_END
));
890 rPH
.Special( GetLen(), aStr
.makeStringAndClear(), GetWhichPor() );
893 void SwBookmarkPortion::dumpAsXml(xmlTextWriterPtr pWriter
, const OUString
& rText
, TextFrameIndex
& nOffset
) const
895 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwBookmarkPortion"));
896 dumpAsXmlAttributes(pWriter
, rText
, nOffset
);
899 if (!m_oColors
.empty())
902 for (const auto& rColor
: m_oColors
)
904 aStr
.append("#" + std::get
<2>(rColor
) + " " + SwResId(STR_BOOKMARK_DEF_NAME
));
905 switch (std::get
<0>(rColor
))
907 case SwScriptInfo::MarkKind::Point
:
909 case SwScriptInfo::MarkKind::Start
:
910 aStr
.append(" " + SwResId(STR_CAPTION_BEGINNING
));
912 case SwScriptInfo::MarkKind::End
:
913 aStr
.append(" " + SwResId(STR_CAPTION_END
));
917 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("colors"),
918 BAD_CAST(aStr
.makeStringAndClear().toUtf8().getStr()));
921 (void)xmlTextWriterEndElement(pWriter
);
924 bool SwControlCharPortion::Format( SwTextFormatInfo
&rInf
)
926 const SwLinePortion
* pRoot
= rInf
.GetRoot();
928 Height( pRoot
->Height() );
929 SetAscent( pRoot
->GetAscent() );
934 sal_uInt16
SwControlCharPortion::GetViewWidth( const SwTextSizeInfo
& rInf
) const
937 mnViewWidth
= rInf
.GetTextSize(OUString(' ')).Width();
942 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */