Revert "tdf#162268: Import - add zero width space after line break"
[LibreOffice.git] / sw / source / core / text / porrst.cxx
blobee409a4c80e2b65710ca4639b7379c0791ba8408
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <editeng/fontitem.hxx>
26 #include <vcl/svapp.hxx>
27 #include <comphelper/scopeguard.hxx>
29 #include <viewsh.hxx>
30 #include <viewopt.hxx>
31 #include <ndtxt.hxx>
32 #include <pagefrm.hxx>
33 #include <paratr.hxx>
34 #include <SwPortionHandler.hxx>
35 #include "porrst.hxx"
36 #include "inftxt.hxx"
37 #include "txtpaint.hxx"
38 #include <swfntcch.hxx>
39 #include <tgrditem.hxx>
40 #include <pagedesc.hxx>
41 #include <frmatr.hxx>
42 #include "redlnitr.hxx"
43 #include "atrhndl.hxx"
44 #include <rootfrm.hxx>
45 #include <formatlinebreak.hxx>
46 #include <txatbase.hxx>
48 #include <IDocumentRedlineAccess.hxx>
49 #include <IDocumentSettingAccess.hxx>
50 #include <IDocumentDeviceAccess.hxx>
51 #include <IDocumentLayoutAccess.hxx>
53 #include <crsrsh.hxx>
54 #include <swtypes.hxx>
55 #include <strings.hrc>
56 #include <flyfrms.hxx>
57 #include <bodyfrm.hxx>
59 SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion,
60 const FontLineStyle eUL,
61 const FontStrikeout eStrkout,
62 const Color& rCol ) :
63 m_eUnderline( eUL ), m_eStrikeout( eStrkout ), m_aColor( rCol )
65 Height( rPortion.Height() );
66 SetAscent( rPortion.GetAscent() );
67 SetWhichPor( PortionType::TempEnd );
70 void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
72 if (!(rInf.OnWin() && rInf.GetOpt().IsParagraph()))
73 return;
75 const SwFont* pOldFnt = rInf.GetFont();
77 SwFont aFont(*pOldFnt);
79 const SwDoc& rDoc = rInf.GetTextFrame()->GetDoc();
80 if (aFont.IsSymbol(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()))
82 const SvxFontItem& rFontItem = rDoc.GetDefault(RES_CHRATR_FONT);
83 aFont.SetName( rFontItem.GetFamilyName(), SwFontScript::Latin );
84 aFont.SetStyleName( rFontItem.GetStyleName(), SwFontScript::Latin );
85 aFont.SetFamily( rFontItem.GetFamily(), SwFontScript::Latin );
86 aFont.SetPitch( rFontItem.GetPitch(), SwFontScript::Latin );
87 aFont.SetCharSet( rFontItem.GetCharSet(), SwFontScript::Latin );
89 // Paint strikeout/underline based on redline color and settings
90 // (with an extra pilcrow in the background, because there is
91 // no SetStrikeoutColor(), also SetUnderColor() doesn't work()).
92 if ( m_eUnderline != LINESTYLE_NONE || m_eStrikeout != STRIKEOUT_NONE )
94 aFont.SetColor( m_aColor );
95 aFont.SetUnderline( m_eUnderline );
96 aFont.SetStrikeout( m_eStrikeout );
98 const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
100 // draw the pilcrow with strikeout/underline in redline color
101 rInf.DrawText(CH_PAR, *this);
105 aFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
106 aFont.SetStrikeout( STRIKEOUT_NONE );
107 aFont.SetUnderline( LINESTYLE_NONE );
108 const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
110 // draw the pilcrow
111 rInf.DrawText(CH_PAR, *this);
113 const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
116 SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion, const SwTextAttr* pAttr )
117 : SwLinePortion( rPortion )
119 mnLineLength = TextFrameIndex(1);
120 m_eRedline = RedlineType::None;
121 SetWhichPor( PortionType::Break );
123 m_eClear = SwLineBreakClear::NONE;
124 if (pAttr && pAttr->Which() == RES_TXTATR_LINEBREAK)
126 m_eClear = pAttr->GetLineBreak().GetValue();
128 m_nTextHeight = 0;
131 TextFrameIndex SwBreakPortion::GetModelPositionForViewPoint(const SwTwips) const
133 return TextFrameIndex(0);
136 SwTwips SwBreakPortion::GetViewWidth(const SwTextSizeInfo&) const { return 0; }
138 SwLinePortion *SwBreakPortion::Compress()
139 { return (GetNextPortion() && GetNextPortion()->InTextGrp() ? nullptr : this); }
141 void SwBreakPortion::Paint( const SwTextPaintInfo &rInf ) const
143 if( !(rInf.OnWin() && rInf.GetOpt().IsLineBreak()) )
144 return;
146 // Reduce height to text height for the duration of the print, so the vertical height will look
147 // correct for the line break character, even for clearing breaks.
148 SwTwips nHeight = Height();
149 SwTwips nVertPosOffset = (nHeight - m_nTextHeight) / 2;
150 auto pPortion = const_cast<SwBreakPortion*>(this);
151 pPortion->Height(m_nTextHeight, false);
152 if (rInf.GetTextFrame()->IsVertical())
154 // Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case.
155 const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() + nVertPosOffset);
157 comphelper::ScopeGuard g(
158 [pPortion, nHeight, &rInf, nVertPosOffset]
160 if (rInf.GetTextFrame()->IsVertical())
162 const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() - nVertPosOffset);
164 pPortion->Height(nHeight, false);
167 rInf.DrawLineBreak( *this );
169 // paint redlining
170 if (m_eRedline == RedlineType::None)
171 return;
173 sal_Int16 nNoBreakWidth = rInf.GetTextSize(S_NOBREAK_FOR_REDLINE).Width();
174 if ( nNoBreakWidth > 0 )
176 // approximate portion size with multiple no-break spaces
177 // and draw these spaces (at least a single one) by DrawText
178 // painting the requested redline underline/strikeout
179 sal_Int16 nSpaces = (LINE_BREAK_WIDTH + nNoBreakWidth/2) / nNoBreakWidth;
180 OUStringBuffer aBuf(S_NOBREAK_FOR_REDLINE);
181 for (sal_Int16 i = 1; i < nSpaces; ++i)
182 aBuf.append(S_NOBREAK_FOR_REDLINE);
184 const SwFont* pOldFnt = rInf.GetFont();
186 SwFont aFont(*pOldFnt);
188 if (m_eRedline == RedlineType::Delete)
189 aFont.SetUnderline( LINESTYLE_NONE );
190 else
191 aFont.SetStrikeout( STRIKEOUT_NONE );
193 const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
195 rInf.DrawText(aBuf.makeStringAndClear(), *this);
197 const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
201 bool SwBreakPortion::Format( SwTextFormatInfo &rInf )
203 const SwLinePortion *pRoot = rInf.GetRoot();
204 Width( 0 );
205 Height( pRoot->Height() );
206 m_nTextHeight = Height();
208 // See if this is a clearing break. If so, calculate how much we need to "jump down" so the next
209 // line can again use the full text width.
210 SwLineBreakClear eClear = m_eClear;
211 if (rInf.GetTextFrame()->IsRightToLeft() && eClear != SwLineBreakClear::ALL)
213 // RTL ignores left/right breaks.
214 eClear = SwLineBreakClear::NONE;
216 if (eClear != SwLineBreakClear::NONE)
218 SwTextFly& rTextFly = rInf.GetTextFly();
219 if (rTextFly.IsOn())
221 SwTwips nHeight = rTextFly.GetMaxBottom(*this, rInf) - rInf.Y();
222 if (nHeight > Height())
224 Height(nHeight, /*bText=*/false);
229 SetAscent( pRoot->GetAscent() );
230 if (rInf.GetIdx() + TextFrameIndex(1) == TextFrameIndex(rInf.GetText().getLength()))
231 rInf.SetNewLine( true );
232 return true;
235 void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const
237 rPH.Text( GetLen(), GetWhichPor() );
240 void SwBreakPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex&
241 nOffset) const
243 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBreakPortion"));
244 dumpAsXmlAttributes(pWriter, rText, nOffset);
245 nOffset += GetLen();
247 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("text-height"),
248 BAD_CAST(OString::number(m_nTextHeight).getStr()));
250 (void)xmlTextWriterEndElement(pWriter);
253 SwLineBreakClear SwBreakPortion::GetClear() const { return m_eClear; }
255 SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn,
256 bool bBG, bool bGK ) :
257 m_nKern( nKrn ), m_bBackground( bBG ), m_bGridKern( bGK )
259 Height( rPortion.Height() );
260 SetAscent( rPortion.GetAscent() );
261 mnLineLength = TextFrameIndex(0);
262 SetWhichPor( PortionType::Kern );
263 if( m_nKern > 0 )
264 Width( m_nKern );
265 rPortion.Insert( this );
268 SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) :
269 m_nKern( 0 ), m_bBackground( false ), m_bGridKern( true )
271 Height( rPortion.Height() );
272 SetAscent( rPortion.GetAscent() );
274 mnLineLength = TextFrameIndex(0);
275 SetWhichPor( PortionType::Kern );
278 void SwKernPortion::Paint( const SwTextPaintInfo &rInf ) const
280 if( !Width() )
281 return;
283 // bBackground is set for Kerning Portions between two fields
284 if ( m_bBackground )
285 rInf.DrawViewOpt( *this, PortionType::Field );
287 rInf.DrawBackBrush( *this );
288 if (GetJoinBorderWithNext() ||GetJoinBorderWithPrev())
289 rInf.DrawBorder( *this );
291 // do we have to repaint a post it portion?
292 if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
293 mpNextPortion->PrePaint( rInf, this );
295 if( rInf.GetFont()->IsPaintBlank() )
297 SwRect aClipRect;
298 rInf.CalcRect( *this, &aClipRect );
299 SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
300 aClip.ChgClip( aClipRect );
301 rInf.DrawText(u" "_ustr, *this, TextFrameIndex(0), TextFrameIndex(2), true );
305 void SwKernPortion::FormatEOL( SwTextFormatInfo &rInf )
307 if ( m_bGridKern )
308 return;
310 if( rInf.GetLast() == this )
311 rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
312 if( m_nKern < 0 )
313 Width( -m_nKern );
314 else
315 Width( 0 );
316 rInf.GetLast()->FormatEOL( rInf );
319 SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
320 m_bLeft( true )
322 Height( rPortion.Height() );
323 SetAscent( rPortion.GetAscent() );
324 mnLineLength = TextFrameIndex(0);
325 SetWhichPor( PortionType::Arrow );
328 SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf )
329 : m_bLeft( false )
331 Height(rInf.GetTextFrame()->getFramePrintArea().Height());
332 m_aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() +
333 rInf.GetTextFrame()->getFramePrintArea().Right() );
334 m_aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() +
335 rInf.GetTextFrame()->getFramePrintArea().Bottom() );
336 SetWhichPor( PortionType::Arrow );
339 void SwArrowPortion::Paint( const SwTextPaintInfo &rInf ) const
341 const_cast<SwArrowPortion*>(this)->m_aPos = rInf.GetPos();
344 SwLinePortion *SwArrowPortion::Compress() { return this; }
346 SwTwips SwTextFrame::EmptyHeight() const
348 if (IsCollapse()) {
349 SwViewShell *pSh = getRootFrame()->GetCurrShell();
350 if ( auto pCrSh = dynamic_cast<SwCursorShell*>( pSh ) ) {
351 // this is called during formatting so avoid recursive layout
352 SwContentFrame const*const pCurrFrame = pCrSh->GetCurrFrame(false);
353 if (pCurrFrame==static_cast<SwContentFrame const *>(this)) {
354 // do nothing
355 } else {
356 return 1;
358 } else {
359 return 1;
362 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::EmptyHeight with swapped frame" );
364 std::unique_ptr<SwFont> pFnt;
365 const SwTextNode& rTextNode = *GetTextNodeForParaProps();
366 const IDocumentSettingAccess* pIDSA = rTextNode.getIDocumentSettingAccess();
367 SwViewShell *pSh = getRootFrame()->GetCurrShell();
368 if ( rTextNode.HasSwAttrSet() )
370 const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() );
371 pFnt.reset(new SwFont( pAttrSet, pIDSA ));
373 else
375 SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh);
376 pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
377 pFnt->CheckFontCacheId( pSh, pFnt->GetActual() );
380 if ( IsVertical() )
381 pFnt->SetVertical( 2700_deg10 );
383 OutputDevice* pOut = pSh ? pSh->GetOut() : nullptr;
384 if ( !pOut || !pSh->GetViewOptions()->getBrowseMode() ||
385 pSh->GetViewOptions()->IsPrtFormat() )
387 pOut = rTextNode.getIDocumentDeviceAccess().getReferenceDevice(true);
390 const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
391 if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
392 && !getRootFrame()->IsHideRedlines())
394 const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
395 if( SwRedlineTable::npos != nRedlPos )
397 SwAttrHandler aAttrHandler;
398 aAttrHandler.Init(rTextNode.GetSwAttrSet(),
399 *rTextNode.getIDocumentSettingAccess());
400 SwRedlineItr aRedln( rTextNode, *pFnt, aAttrHandler,
401 nRedlPos, SwRedlineItr::Mode::Show);
405 SwTwips nRet;
406 if( !pOut )
407 nRet = IsVertical() ?
408 getFramePrintArea().SSize().Width() + 1 :
409 getFramePrintArea().SSize().Height() + 1;
410 else
412 pFnt->SetFntChg( true );
413 pFnt->ChgPhysFnt( pSh, *pOut );
414 nRet = pFnt->GetHeight( pSh, *pOut );
416 return nRet;
419 bool SwTextFrame::FormatEmpty()
421 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::FormatEmpty with swapped frame" );
423 bool bCollapse = EmptyHeight( ) == 1 && IsCollapse( );
425 // sw_redlinehide: just disable FormatEmpty optimisation for now
426 // Split fly frames: non-last parts of the anchor want this optimization to clear the old
427 // content.
428 SwFlyAtContentFrame* pNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
429 bool bHasNonLastSplitFlyDrawObj = pNonLastSplitFlyDrawObj != nullptr;
431 if (pNonLastSplitFlyDrawObj && pNonLastSplitFlyDrawObj->IsWrapOnAllPages())
433 // Split fly: the anchor is non-empty on all pages in the "wrap on all pages" case.
434 bHasNonLastSplitFlyDrawObj = false;
437 if ((HasFollow() && !bHasNonLastSplitFlyDrawObj) || GetMergedPara() || (GetTextNodeFirst()->GetpSwpHints() && !bHasNonLastSplitFlyDrawObj) ||
438 nullptr != GetTextNodeForParaProps()->GetNumRule() ||
439 GetTextNodeFirst()->HasHiddenCharAttribute(true) ||
440 IsInFootnote() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
441 return false;
442 const SwAttrSet& aSet = GetTextNodeForParaProps()->GetSwAttrSet();
443 const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust();
444 if( !bCollapse && ( ( ( ! IsRightToLeft() && ( SvxAdjust::Left != nAdjust ) ) ||
445 ( IsRightToLeft() && ( SvxAdjust::Right != nAdjust ) ) ) ||
446 aSet.GetRegister().GetValue() ) )
447 return false;
448 const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
449 if( !bCollapse && ( SvxLineSpaceRule::Min == rSpacing.GetLineSpaceRule() ||
450 SvxLineSpaceRule::Fix == rSpacing.GetLineSpaceRule() ||
451 aSet.GetFirstLineIndent().IsAutoFirst()))
453 return false;
456 SwTextFly aTextFly( this );
457 SwRect aRect;
458 bool bFirstFlyCheck = 0 != getFramePrintArea().Height();
459 if ( !bCollapse && bFirstFlyCheck &&
460 aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) && !bHasNonLastSplitFlyDrawObj )
461 return false;
463 if (IsEmptyWithSplitFly())
465 // We don't want this optimization in case the paragraph is not really empty, because it has
466 // a fly frame and it also needs space for the empty paragraph in a next line.
467 return false;
470 // only need to check one node because of early return on GetMerged()
471 for (SwContentIndex const* pIndex = GetTextNodeFirst()->GetFirstIndex();
472 pIndex; pIndex = pIndex->GetNext())
474 if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
475 continue;
476 auto const pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner());
477 if (dynamic_cast<const sw::mark::Bookmark*>(pMark) != nullptr)
478 { // need bookmark portions!
479 return false;
483 SwTwips nHeight = EmptyHeight();
485 if (aSet.GetParaGrid().GetValue() &&
486 IsInDocBody() )
488 SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
489 if ( pGrid )
490 nHeight = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
493 SwRectFnSet aRectFnSet(this);
494 SwTwips nChg = nHeight - aRectFnSet.GetHeight(getFramePrintArea());
495 const SwBodyFrame* pBody = FindBodyFrame();
496 if (pNonLastSplitFlyDrawObj && pBody)
498 // See if we need to increase the text frame height due to split flys. This is necessary for
499 // anchors of inner floating tables, where moving to a next page moves indirectly, so we
500 // want a correct text frame height.
501 SwTwips nFrameBottom = aRectFnSet.GetBottom(getFrameArea()) + nChg;
502 SwTwips nFlyBottom = aRectFnSet.GetBottom(pNonLastSplitFlyDrawObj->getFrameArea());
503 SwTwips nBodyBottom = aRectFnSet.GetBottom(pBody->getFrameArea());
504 if (nFlyBottom > nBodyBottom)
506 // This is the legacy case where flys may overlap with footer frames.
507 nFlyBottom = nBodyBottom;
509 if (pNonLastSplitFlyDrawObj->isFrameAreaPositionValid() && nFlyBottom > nFrameBottom)
511 nChg += (nFlyBottom - nFrameBottom);
515 if( !nChg )
516 SetUndersized( false );
517 AdjustFrame( nChg );
519 if (GetHasRotatedPortions())
521 ClearPara();
522 SetHasRotatedPortions(false);
525 RemoveFromCache();
526 if( !IsEmpty() )
528 SetEmpty( true );
529 SetCompletePaint();
531 if( !bCollapse && !bFirstFlyCheck &&
532 aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
533 return false;
535 // #i35635# - call method <HideAndShowObjects()>
536 // to assure that objects anchored at the empty paragraph are
537 // correctly visible resp. invisible.
538 HideAndShowObjects();
539 return true;
542 bool SwTextFrame::FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff )
544 const SwFrame *pFrame = this;
545 rRegDiff = 0;
546 while( !( ( SwFrameType::Body | SwFrameType::Fly )
547 & pFrame->GetType() ) && pFrame->GetUpper() )
548 pFrame = pFrame->GetUpper();
549 if( ( SwFrameType::Body| SwFrameType::Fly ) & pFrame->GetType() )
551 SwRectFnSet aRectFnSet(pFrame);
552 rRegStart = aRectFnSet.GetPrtTop(*pFrame);
553 pFrame = pFrame->FindPageFrame();
554 if( pFrame->IsPageFrame() )
556 SwPageDesc* pDesc = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pFrame))->FindPageDesc();
557 if( pDesc )
559 rRegDiff = pDesc->GetRegHeight();
560 if( !rRegDiff )
562 const SwTextFormatColl *pFormat = pDesc->GetRegisterFormatColl();
563 if( pFormat )
565 const SvxLineSpacingItem &rSpace = pFormat->GetLineSpacing();
566 if( SvxLineSpaceRule::Fix == rSpace.GetLineSpaceRule() )
568 rRegDiff = rSpace.GetLineHeight();
569 pDesc->SetRegHeight( rRegDiff );
570 pDesc->SetRegAscent( ( 4 * rRegDiff ) / 5 );
572 else
574 SwViewShell *pSh = getRootFrame()->GetCurrShell();
575 SwFontAccess aFontAccess( pFormat, pSh );
576 SwFont aFnt( aFontAccess.Get()->GetFont() );
578 OutputDevice *pOut = nullptr;
579 if( !pSh || !pSh->GetViewOptions()->getBrowseMode() ||
580 pSh->GetViewOptions()->IsPrtFormat() )
581 pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
583 if( pSh && !pOut )
584 pOut = pSh->GetWin()->GetOutDev();
586 if( !pOut )
587 pOut = Application::GetDefaultDevice();
589 MapMode aOldMap( pOut->GetMapMode() );
590 pOut->SetMapMode( MapMode( MapUnit::MapTwip ) );
592 aFnt.ChgFnt( pSh, *pOut );
593 rRegDiff = aFnt.GetHeight( pSh, *pOut );
594 sal_uInt16 nNetHeight = rRegDiff;
596 switch( rSpace.GetLineSpaceRule() )
598 case SvxLineSpaceRule::Auto:
599 break;
600 case SvxLineSpaceRule::Min:
602 if( rRegDiff < rSpace.GetLineHeight() )
603 rRegDiff = rSpace.GetLineHeight();
604 break;
606 default:
607 OSL_FAIL( ": unknown LineSpaceRule" );
609 switch( rSpace.GetInterLineSpaceRule() )
611 case SvxInterLineSpaceRule::Off:
612 break;
613 case SvxInterLineSpaceRule::Prop:
615 tools::Long nTmp = rSpace.GetPropLineSpace();
616 if( nTmp < 50 )
617 nTmp = nTmp ? 50 : 100;
618 nTmp *= rRegDiff;
619 nTmp /= 100;
620 if( !nTmp )
621 ++nTmp;
622 rRegDiff = o3tl::narrowing<sal_uInt16>(nTmp);
623 nNetHeight = rRegDiff;
624 break;
626 case SvxInterLineSpaceRule::Fix:
628 rRegDiff = rRegDiff + rSpace.GetInterLineSpace();
629 nNetHeight = rRegDiff;
630 break;
632 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
634 pDesc->SetRegHeight( rRegDiff );
635 pDesc->SetRegAscent( rRegDiff - nNetHeight +
636 aFnt.GetAscent( pSh, *pOut ) );
637 pOut->SetMapMode( aOldMap );
641 const tools::Long nTmpDiff = pDesc->GetRegAscent() - rRegDiff;
642 if ( aRectFnSet.IsVert() )
643 rRegStart -= nTmpDiff;
644 else
645 rRegStart += nTmpDiff;
649 return ( 0 != rRegDiff );
652 void SwHiddenTextPortion::Paint( const SwTextPaintInfo & rInf) const
654 #ifdef DBG_UTIL
655 OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
656 Color aCol( rInf.GetOpt().GetFieldShadingsColor() );
657 Color aOldColor( pOut->GetFillColor() );
658 pOut->SetFillColor( aCol );
659 Point aPos( rInf.GetPos() );
660 aPos.AdjustY( -150 );
661 aPos.AdjustX( -25 );
662 SwRect aRect( aPos, Size( 100, 200 ) );
663 pOut->DrawRect( aRect.SVRect() );
664 pOut->SetFillColor( aOldColor );
665 #else
666 (void)rInf;
667 #endif
670 bool SwHiddenTextPortion::Format( SwTextFormatInfo &rInf )
672 Width( 0 );
673 rInf.GetTextFrame()->HideFootnotes( rInf.GetIdx(), rInf.GetIdx() + GetLen() );
675 return false;
678 bool SwControlCharPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
679 OUString & rOutString, SwFont & rTmpFont, int &) const
681 if (mcChar == CHAR_WJ || !rTextPaintInfo.GetOpt().IsViewMetaChars())
683 return false;
686 switch (mcChar)
688 case CHAR_ZWSP:
689 rOutString = "/"; break;
690 // case CHAR_LRM :
691 // rText = sal_Unicode(0x2514); break;
692 // case CHAR_RLM :
693 // rText = sal_Unicode(0x2518); break;
694 default:
695 assert(false);
696 break;
699 rTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 );
700 rTmpFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
701 const sal_uInt16 nProp = 40;
702 rTmpFont.SetProportion( nProp ); // a smaller font
704 return true;
707 bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
708 OUString & rOutString, SwFont & rFont, int & rDeltaY) const
710 // custom color is visible without field shading, too
711 if (!rTextPaintInfo.GetOpt().IsShowBookmarks())
713 return false;
716 rOutString = OUStringChar(mcChar);
718 // init font: we want OpenSymbol to ensure it doesn't look too crazy;
719 // thin and a bit higher than the surrounding text
720 auto const nOrigAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
721 rFont.SetName(u"OpenSymbol"_ustr, rFont.GetActual());
722 Size aSize(rFont.GetSize(rFont.GetActual()));
723 // use also the external leading (line gap) of the portion, but don't use
724 // 100% of it because i can't figure out how to baseline align that
725 assert(aSize.Height() != 0);
726 auto const nFactor = aSize.Height() > 0 ? (Height() * 95) / aSize.Height() : Height();
727 rFont.SetProportion(nFactor);
728 rFont.SetWeight(WEIGHT_THIN, rFont.GetActual());
729 rFont.SetColor(rTextPaintInfo.GetOpt().GetFieldShadingsColor());
730 // reset these to default...
731 rFont.SetAlign(ALIGN_BASELINE);
732 rFont.SetUnderline(LINESTYLE_NONE);
733 rFont.SetOverline(LINESTYLE_NONE);
734 rFont.SetStrikeout(STRIKEOUT_NONE);
735 rFont.SetOutline(false);
736 rFont.SetShadow(false);
737 rFont.SetTransparent(false);
738 rFont.SetEmphasisMark(FontEmphasisMark::NONE);
739 rFont.SetEscapement(0);
740 rFont.SetPitch(PITCH_DONTKNOW, rFont.GetActual());
741 rFont.SetRelief(FontRelief::NONE);
743 // adjust Y position to account for different baselines of the fonts
744 auto const nOSAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
745 rDeltaY = nOSAscent - nOrigAscent;
747 return true;
750 void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const
752 if ( !Width() ) // is only set during prepaint mode
753 return;
755 rInf.DrawViewOpt(*this, GetWhichPor());
757 int deltaY(0);
758 SwFont aTmpFont( *rInf.GetFont() );
759 OUString aOutString;
761 if (!(rInf.OnWin()
762 && !rInf.GetOpt().IsPagePreview()
763 && !rInf.GetOpt().IsReadonly()
764 && DoPaint(rInf, aOutString, aTmpFont, deltaY)))
765 return;
767 SwFontSave aFontSave( rInf, &aTmpFont );
769 if ( !mnHalfCharWidth )
770 mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
772 Point aOldPos = rInf.GetPos();
773 Point aNewPos( aOldPos );
774 auto const deltaX((Width() / 2) - mnHalfCharWidth);
775 switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
777 case 0:
778 aNewPos.AdjustX(deltaX);
779 aNewPos.AdjustY(deltaY);
780 break;
781 case 900:
782 aNewPos.AdjustY(-deltaX);
783 aNewPos.AdjustX(deltaY);
784 break;
785 case 2700:
786 aNewPos.AdjustY(deltaX);
787 aNewPos.AdjustX(-deltaY);
788 break;
789 default:
790 assert(false);
791 break;
793 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
795 rInf.DrawText( aOutString, *this );
797 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
800 void SwBookmarkPortion::Paint( const SwTextPaintInfo &rInf ) const
802 if ( !Width() ) // is only set during prepaint mode
803 return;
805 rInf.DrawViewOpt(*this, GetWhichPor());
807 int deltaY(0);
808 SwFont aTmpFont( *rInf.GetFont() );
809 OUString aOutString;
811 if (!(rInf.OnWin()
812 && !rInf.GetOpt().IsPagePreview()
813 && !rInf.GetOpt().IsReadonly()
814 && DoPaint(rInf, aOutString, aTmpFont, deltaY)))
815 return;
817 SwFontSave aFontSave( rInf, &aTmpFont );
819 if ( !mnHalfCharWidth )
820 mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
822 Point aOldPos = rInf.GetPos();
823 Point aNewPos( aOldPos );
824 auto const deltaX((Width() / 2) - mnHalfCharWidth);
825 switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
827 case 0:
828 aNewPos.AdjustX(deltaX);
829 aNewPos.AdjustY(deltaY);
830 break;
831 case 900:
832 aNewPos.AdjustY(-deltaX);
833 aNewPos.AdjustX(deltaY);
834 break;
835 case 2700:
836 aNewPos.AdjustY(deltaX);
837 aNewPos.AdjustX(-deltaY);
838 break;
839 default:
840 assert(false);
841 break;
844 // draw end marks before the character position
845 if ( m_nStart == 0 || m_nEnd == 0 )
847 // single type boundary marks are there outside of the bookmark text
848 // some |text| here
849 // [[ ]]
850 if (m_nStart > 1)
851 aNewPos.AdjustX(mnHalfCharWidth * -2 * (m_aColors.size() - 1));
853 else if ( m_nStart != 0 && m_nEnd != 0 )
854 // both end and start boundary marks: adjust them around the bookmark position
855 // |te|xt|
856 // ]] [[
857 aNewPos.AdjustX(mnHalfCharWidth * -(2 * m_nEnd - 1 + m_nPoint) );
859 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
861 for ( const auto& it : m_aColors )
863 // set bold for custom colored bookmark symbol
864 // and draw multiple symbols showing all custom colors
865 aTmpFont.SetWeight( COL_TRANSPARENT == std::get<1>(it) ? WEIGHT_THIN : WEIGHT_BOLD, aTmpFont.GetActual() );
866 aTmpFont.SetColor( COL_TRANSPARENT == std::get<1>(it) ? rInf.GetOpt().GetFieldShadingsColor() : std::get<1>(it) );
867 aOutString = OUString(std::get<0>(it) == SwScriptInfo::MarkKind::Start ? '[' : ']');
869 // MarkKind::Point: drawn I-beam (e.g. U+2336) as overlapping ][
870 if ( std::get<0>(it) == SwScriptInfo::MarkKind::Point )
872 aNewPos.AdjustX(-mnHalfCharWidth * 5/16);
873 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
874 rInf.DrawText( aOutString, *this );
876 // when the overlapping vertical lines are 50 pixel width on the screen,
877 // this distance (half width * 5/8) still results precise overlapping
878 aNewPos.AdjustX(mnHalfCharWidth * 5/8);
879 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
880 aOutString = OUString('[');
882 rInf.DrawText( aOutString, *this );
883 // place the next symbol after the previous one
884 // TODO: fix orientation and start/end
885 aNewPos.AdjustX(mnHalfCharWidth * 2);
886 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
889 const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
892 void SwBookmarkPortion::HandlePortion( SwPortionHandler& rPH ) const
894 OUStringBuffer aStr;
895 for ( const auto& it : m_aColors )
897 aStr.append("#" + std::get<2>(it) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
898 switch (std::get<0>(it))
900 case SwScriptInfo::MarkKind::Point:
901 break;
902 case SwScriptInfo::MarkKind::Start:
903 aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
904 break;
905 case SwScriptInfo::MarkKind::End:
906 aStr.append(" " + SwResId(STR_CAPTION_END));
907 break;
911 rPH.Special( GetLen(), aStr.makeStringAndClear(), GetWhichPor() );
914 void SwBookmarkPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
916 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBookmarkPortion"));
917 dumpAsXmlAttributes(pWriter, rText, nOffset);
918 nOffset += GetLen();
920 if (!m_aColors.empty())
922 OUStringBuffer aStr;
923 for (const auto& rColor : m_aColors)
925 aStr.append("#" + std::get<2>(rColor) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
926 switch (std::get<0>(rColor))
928 case SwScriptInfo::MarkKind::Point:
929 break;
930 case SwScriptInfo::MarkKind::Start:
931 aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
932 break;
933 case SwScriptInfo::MarkKind::End:
934 aStr.append(" " + SwResId(STR_CAPTION_END));
935 break;
938 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("colors"),
939 BAD_CAST(aStr.makeStringAndClear().toUtf8().getStr()));
942 (void)xmlTextWriterEndElement(pWriter);
945 bool SwControlCharPortion::Format( SwTextFormatInfo &rInf )
947 const SwLinePortion* pRoot = rInf.GetRoot();
948 Width( 0 );
949 Height( pRoot->Height() );
950 SetAscent( pRoot->GetAscent() );
952 return false;
955 SwTwips SwControlCharPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
957 if( !mnViewWidth )
958 mnViewWidth = rInf.GetTextSize(OUString(' ')).Width();
960 return mnViewWidth;
963 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */