/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// Returns, if we have an underline breaking situation // Adding some more conditions here means you also have to change them // in SwTextPainter::CheckSpecialUnderline bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
{ return LINESTYLE_NONE == rFnt.GetUnderline() ||
rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
rPor.IsHolePortion() ||
( rPor.IsMultiPortion() && ! static_cast<const SwMultiPortion&>(rPor).IsBidi() ) ||
rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
SvxCaseMap::SmallCaps == rFnt.GetCaseMap();
}
// There are two possibilities to output transparent font: // 1) DrawRect on the whole line and DrawText afterwards // (objectively fast, subjectively slow) // 2) For every portion a DrawRect with subsequent DrawText is done // (objectively slow, subjectively fast) // Since the user usually judges subjectively the second method is set as default. void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, constbool bUnderSized,
::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
::std::optional<SwTaggedPDFHelper> & roTaggedParagraph, boolconst isPDFTaggingEnabled)
{ // maybe catch-up adjustment
GetAdjusted();
AddExtraBlankWidth();
GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
GetInfo().ResetSpaceIdx();
GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
GetInfo().ResetKanaIdx(); // The size of the frame
GetInfo().SetIdx( GetStart() );
GetInfo().SetPos( GetTopLeft() );
constbool bDrawInWindow = GetInfo().OnWin();
// 6882: blank lines can't be optimized by removing them if Formatting Marks are shown constbool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
if (bSkippedNumPortions // ugly but hard to check earlier in PaintSwFrame:
&& !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
{ // there is a num portion but it is outside of the frame area and not painted
assert(!roTaggedLabel);
assert(!roTaggedParagraph);
Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
}
// DropCaps! // 7538: of course for the printer, too if( !m_bPaintDrop )
{ // 8084: Optimization, less painting // AMA: By 8084 7538 has been revived // bDrawInWindow removed, so that DropCaps also can be printed
m_bPaintDrop = pPor == m_pCurr->GetFirstPortion()
&& GetDropLines() >= GetLineNr();
}
// bClip decides if there's a need to clip // The whole thing must be done before retouching
bool bClip = ( bDrawInWindow || bUnderSized ) && !rClip.IsChg(); if( bClip && pPor )
{ // If TopLeft or BottomLeft of the line are outside, the we must clip. // The check for Right() is done in the output loop ...
// Alignment
OutputDevice* pOut = GetInfo().GetOut();
Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() ); if ( aPnt1.X() < rPaint.Left() )
aPnt1.setX( rPaint.Left() ); if ( aPnt1.Y() < rPaint.Top() )
aPnt1.setY( rPaint.Top() );
Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
GetInfo().GetPos().Y() + nTmpHeight ); if ( aPnt2.X() > rPaint.Right() )
aPnt2.setX( rPaint.Right() ); if ( aPnt2.Y() > rPaint.Bottom() )
aPnt2.setY( rPaint.Bottom() );
const SwRect aLineRect( aPnt1, aPnt2 );
if( m_pCurr->IsClipping() )
{ const SwTextFrame& rFrame = *GetInfo().GetTextFrame(); // tdf#117448 at small fixed line height, enlarge clipping area in table cells // to show previously clipped text content on the area of paragraph margins if ( rFrame.IsInTab() )
rClip.ChgClip(aLineRect, m_pFrame, rFrame.GetTopMargin(), rFrame.GetBottomMargin()); else
rClip.ChgClip( aLineRect, m_pFrame );
bClip = false;
}
if( !pPor && !bEndPor ) return;
// Baseline output also if non-TextPortion (compare TabPor with Fill) // if no special vertical alignment is used, // we calculate Y value for the whole line
SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame())); constbool bAdjustBaseLine =
GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
( nullptr != pGrid ) || m_pCurr->GetHangingBaseline(); const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent; if ( ! bAdjustBaseLine )
GetInfo().Y( nLineBaseLine );
// we store the last portion, because a possible paragraph // end character has the same font as this portion // (only in special vertical alignment case, otherwise the first // portion of the line is used) if ( pPor->Width() && pPor->InTextGrp() )
pEndTempl = pPor;
}
// set redlining for line break symbol if ( pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() && GetRedln() )
{
SeekAndChg( GetInfo() ); if ( m_pCurr->GetRedlineEndType() != RedlineType::None ) static_cast<SwBreakPortion&>(*pPor).SetRedline( m_pCurr->GetRedlineEndType() );
}
// A special case are GluePortions which output blanks.
// 6168: Avoid that the rest of a FieldPortion gets the attributes of the // next portion with SeekAndChgBefore(): if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
SeekAndChgBefore( GetInfo() ); elseif ( pPor->IsQuoVadisPortion() )
{ // A remark on QuoVadis/ErgoSum: // We use the Font set for the Paragraph for these portions. // Thus, we initialize:
TextFrameIndex nOffset = GetInfo().GetIdx();
SeekStartAndChg( GetInfo(), true ); if( GetRedln() && m_pCurr->HasRedline() )
{
std::pair<SwTextNode const*, sal_Int32> const pos(
GetTextFrame()->MapViewToModel(nOffset));
GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
}
} elseif( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
SeekAndChg( GetInfo() ); elseif ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
{ // Paragraph symbols should have the same font as the paragraph in front of them, // except for the case that there's redlining in the paragraph if( GetRedln() )
SeekAndChg( GetInfo() ); else
SeekAndChgBefore( GetInfo() );
} else
bSeeked = false;
// bRest = false;
// If the end of the portion juts out, it is clipped. // A safety distance of half the height is added, so that // TTF-"f" isn't overlapping into the page margin. if( bClip &&
GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
{
bClip = false;
rClip.ChgClip(rPaint, m_pFrame, m_pCurr->GetExtraAscent(), m_pCurr->GetExtraDescent());
}
// Portions, which lay "below" the text like post-its
SwLinePortion *pNext = pPor->GetNextPortion(); if( GetInfo().OnWin() && pNext && !pNext->Width() )
{ // Fix 11289: Fields were omitted here because of Last!=Owner during // loading Brief.sdw. Now the fields are allowed again, // by bSeeked Last!=Owner is being avoided. if ( !bSeeked )
SeekAndChg( GetInfo() );
pNext->PrePaint( GetInfo(), pPor );
}
// We calculate a separate font for underlining.
CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt(); if ( pUnderLineFnt )
{ const Point aTmpPoint( GetInfo().X(),
bAdjustBaseLine ?
pUnderLineFnt->GetPos().Y() :
nLineBaseLine );
pUnderLineFnt->SetPos( aTmpPoint );
}
// in extended input mode we do not want a common underline font.
SwUnderlineFont* pOldUnderLineFnt = nullptr; if ( GetRedln() && GetRedln()->ExtOn() )
{
pOldUnderLineFnt = GetInfo().GetUnderFnt();
GetInfo().SetUnderFnt( nullptr );
}
// multiple numbering portions are possible :( if ((pPor->InNumberGrp() // also footnote label // weird special case, bullet with soft hyphen
|| (pPor->InHyphGrp() && pNext && pNext->InNumberGrp()))
&& !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline()
&& !roTaggedLabel) // note: CalcPaintOfst may skip some portions
{
assert(isPDFTaggingEnabled);
Por_Info aPorInfo(*pPor, *this, 1); // open Lbl
roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut);
}
{ // #i16816# tagged pdf support
Por_Info aPorInfo(*pPor, *this, 0);
SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
// lazy open LBody and paragraph tag after num portions have been painted to Lbl if (pPor->InNumberGrp() // also footnote label // note: numbering portion may be split if it has multiple scripts
&& !static_cast<SwNumberPortion const*>(pPor)->HasFollow()) // so wait for the last one
{ if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
{
assert(roTaggedLabel);
roTaggedLabel.reset(); // close Lbl
assert(!roTaggedParagraph);
Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *pOut);
} else
{
assert(!roTaggedLabel);
}
}
// reset underline font if ( pOldUnderLineFnt )
GetInfo().SetUnderFnt( pOldUnderLineFnt );
// reset (for special vertical alignment)
GetInfo().Y( nOldY );
if (GetFnt()->IsURL() && pPor->InTextGrp())
GetInfo().NotifyURL(*pPor); elseif (pPor->IsFlyCntPortion())
{ if (auto* pFlyContentPortion = dynamic_cast<sw::FlyContentPortion*>(pPor))
{ if (auto* pFlyFrame = pFlyContentPortion->GetFlyFrame())
{ if (auto* pFormat = pFlyFrame->GetFormat())
{ auto& url = pFormat->GetURL(); if (!url.GetURL().isEmpty()) // TODO: url.GetMap() ?
GetInfo().NotifyURL(*pPor);
}
}
}
}
pPor = bDrawInWindow || GetInfo().X() <= nMaxRight || // #i16816# tagged pdf support
( GetInfo().GetVsh() &&
GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
pNext && pNext->IsHolePortion() ) ?
pNext :
nullptr; if (!pPor && isPDFTaggingEnabled && (roTaggedLabel || !roTaggedParagraph))
{ // check if the end of the list label is off-screen auto FindEndOfNumbering = [&](SwLinePortion const* pP) { while (pP)
{ if (pP->InNumberGrp()
&& !static_cast<SwNumberPortion const*>(pP)->HasFollow())
{ if (roTaggedLabel)
{
roTaggedLabel.reset();
} // else, if the numbering isn't visible at all, no Lbl if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
{
Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
} returntrue;
}
pP = pP->GetNextPortion();
} returnfalse;
}; if (!FindEndOfNumbering(pNext)) // check rest of current line
{ // check lines that will be cut off if (rPaint.Bottom() < Y() + GetLineHeight())
{ for (SwLineLayout const* pLine = GetNext(); pLine; pLine = pLine->GetNext())
{ if (FindEndOfNumbering(pLine->GetFirstPortion()))
{ break;
}
}
}
}
}
}
// delete underline font delete GetInfo().GetUnderFnt();
GetInfo().SetUnderFnt( nullptr );
// paint remaining stuff, e.g. the line ending symbols, pilcrow (¶) and the line break if( bDrawInWindow )
{ // If special vertical alignment is enabled, GetInfo().Y() is the // top of the current line. Therefore is has to be adjusted for // the painting of the remaining stuff. We first store the old value. const SwTwips nOldY = GetInfo().Y();
if ( bAdjustBaseLine )
GetInfo().Y( GetInfo().GetPos().Y()
+ AdjustBaseLine( *m_pCurr, &aEnd ) );
GetInfo().X( GetInfo().X() + // tdf#163042 In the case of shrunk lines with a single portion, adjust // the line width (if needed, i.e. if the shrunk line doesn't end in a space) // to show the terminating pilcrow at the correct position, and not before that
( ( !( pEndTempl->GetNextPortion() && pEndTempl->GetNextPortion()->IsHolePortion() ) &&
std::abs( m_pCurr->Width() - m_pCurr->GetFirstPortion()->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
? m_pCurr->ExtraShrunkWidth() - m_pCurr->Width() : 0 ) +
( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
aEnd.Paint( GetInfo() );
GetInfo().Y( nOldY );
} if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() )
{ constbool bNextUndersized =
( GetTextFrame()->GetNext() &&
0 == GetTextFrame()->GetNext()->getFramePrintArea().Height() &&
GetTextFrame()->GetNext()->IsTextFrame() && static_cast<SwTextFrame*>(GetTextFrame()->GetNext())->IsUndersized() ) ;
void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
tools::Long nAdjustBaseLine )
{ // Check if common underline should not be continued if ( IsUnderlineBreak( *pPor, *m_pFont ) )
{ // delete underline font delete GetInfo().GetUnderFnt();
GetInfo().SetUnderFnt( nullptr ); return;
} // Reuse calculated underline font as much as possible. if (GetInfo().GetUnderFnt() &&
GetInfo().GetIdx() + pPor->GetLen() <= GetInfo().GetUnderFnt()->GetEnd() + TextFrameIndex(1))
{
SwFont &rFont = GetInfo().GetUnderFnt()->GetFont(); const Color aColor = GetUnderColor( GetInfo().GetFont() ); if ( GetUnderColor( &rFont ) != aColor )
rFont.SetColor( aColor ); return;
}
// If current underline matches the common underline font, we continue // to use the common underline font. // Bug 120769:Color of underline display wrongly if ( GetInfo().GetUnderFnt() &&
GetInfo().GetUnderFnt()->GetFont().GetUnderline() == GetFnt()->GetUnderline() &&
GetInfo().GetFont() && GetInfo().GetFont()->GetUnderColor() != COL_AUTO ) return; //Bug 120769(End)
if (sw::MergedPara const*const pMerged = GetTextFrame()->GetMergedPara())
{ // first, add the paragraph properties to MultiSelection - if there are // Hints too, they will override the positions if they're added later
sal_Int32 nTmp(0); for (autoconst& e : pMerged->extents)
{ if (const SvxUnderlineItem* pItem = e.pNode->GetSwAttrSet().GetItemIfSet(
RES_CHRATR_UNDERLINE))
{ constbool bUnderSelect(m_pFont->GetUnderline() ==
pItem->GetLineStyle());
aUnderMulti.Select(Range(nTmp, nTmp + e.nEnd - e.nStart - 1),
bUnderSelect);
}
nTmp += e.nEnd - e.nStart;
}
}
// find the underline range the current portion is contained in for( size_t i = 0; i < nCnt; ++i )
{ const Range& rRange = aUnderMulti.GetRange( i ); if (nUnderEnd == TextFrameIndex(rRange.Min()))
nUnderEnd = TextFrameIndex(rRange.Max()); elseif (nIndx >= TextFrameIndex(rRange.Min()))
{
nUnderEnd = TextFrameIndex(rRange.Max());
} else break;
}
// calculate the new common underline font
SwFont* pUnderlineFnt = nullptr;
Point aCommonBaseLine;
// check, if underlining is not isolated if (nIndx + GetInfo().GetLen() < nUnderEnd + TextFrameIndex(1))
{ // here starts the algorithm for calculating the underline font
SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
SwAttrIter aIter(*GetInfo().GetTextFrame()->GetTextNodeFirst(),
rScriptInfo, GetTextFrame());
// If we do not have a common baseline we take the baseline // and the font of the lowest portion. if ( nAdjustBaseLine )
{ const sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *m_pCurr, pPor ); if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
{
nMaxBaseLineOfst = nTmpBaseLineOfst;
nSumHeight = nFontHeight;
}
} // in horizontal layout we build a weighted sum of the heights else
nSumHeight += pPor->Width() * nFontHeight;
if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
nBold += pPor->Width();
}
// font weight if ( 2 * nBold > nSumWidth )
pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual ); else
pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );
// common base line
aCommonBaseLine.setY( nAdjustBaseLine + nMaxBaseLineOfst );
}
}
// an escaped redlined portion should also have a special underlining if( ! pUnderlineFnt && m_pFont->GetEscapement() > 0 && GetRedln() &&
GetRedln()->ChkSpecialUnderline() )
pUnderlineFnt = new SwFont( *m_pFont );
GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, nUnderEnd,
aCommonBaseLine ) );
} else // I'm sorry, we do not have a special underlining font for you.
GetInfo().SetUnderFnt( nullptr );
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.