/* -*- 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 .
*/
// global variables declared in fntcache.hxx // FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
SwFntCache *pFntCache = nullptr; // last Font set by ChgFntCache
SwFntObj *pLastFont = nullptr;
// Computes the start and end position of an underline. This function is called // from the DrawText-method (for underlining misspelled words or smarttag terms). staticvoid lcl_calcLinePos( const CalcLinePosData &rData,
Point &rStart, Point &rEnd, TextFrameIndex const nStart, TextFrameIndex const nWrLen)
{
tools::Long nBlank = 0; const TextFrameIndex nEnd = nStart + nWrLen; const tools::Long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR;
// tdf#151968 // if start < end, OutputDevice::DrawWaveLine() will think it is a rotated // line, so we swap nStart and nEnd to avoid this. if ( rData.bBidiPor )
std::swap(rStart, rEnd);
if ( rData.bSwitchL2R )
{
rData.rInf.GetFrame()->SwitchLTRtoRTL( rStart );
rData.rInf.GetFrame()->SwitchLTRtoRTL( rEnd );
// tdf#151968 // We need to do this here as well for LTR text in a RTL paragraph.
std::swap(rStart, rEnd);
}
if ( rData.bSwitchH2V )
{
rData.rInf.GetFrame()->SwitchHorizontalToVertical( rStart );
rData.rInf.GetFrame()->SwitchHorizontalToVertical( rEnd );
}
}
// Returns the Ascent of the Font on the given output device; // it may be necessary to create the screen font first.
sal_uInt16 SwFntObj::GetFontAscent( const SwViewShell *pSh, const OutputDevice& rOut )
{
sal_uInt16 nRet = 0; const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
#if !defined(MACOSX) // #i89844# extleading is below the line for Mac // TODO: move extleading below the line for all platforms too
nRet += GetFontLeading( pSh, rRefDev ); #endif
// Returns the height of the Font on the given output device; // it may be necessary to create the screen font first.
sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice& rOut )
{
sal_uInt16 nRet = 0; const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
#if OSL_DEBUG_LEVEL > 0 // Check if vcl did not change the meaning of GetTextHeight const FontMetric aOutMet( rRefDev.GetFontMetric() );
tools::Long nTmpPrtHeight = o3tl::narrowing<sal_uInt16>(aOutMet.GetAscent()) + aOutMet.GetDescent(); // #i106098#: do not compare with == here due to rounding error
OSL_ENSURE( std::abs(nTmpPrtHeight - m_nPrtHeight) < 3, "GetTextHeight != Ascent + Descent" ); #endif
if ( pSh )
{ if ( USHRT_MAX == m_nGuessedLeading || USHRT_MAX == m_nExtLeading )
{
SolarMutexGuard aGuard;
const vcl::Font aOldFnt( rOut.GetFont() ); const_cast<OutputDevice&>(rOut).SetFont( *m_pPrtFont ); const FontMetric aMet( rOut.GetFontMetric() ); const_cast<OutputDevice&>(rOut).SetFont( aOldFnt );
m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
GuessLeading( *pSh, aMet );
m_nExtLeading = o3tl::narrowing<sal_uInt16>(aMet.GetExternalLeading()); /* HACK: FIXME There is something wrong with Writer's bullet rendering, causing lines with bullets to be higher than they should be. I think this is because Writer uses font's external leading incorrect, as the vertical distance added to every line instead of only a distance between multiple lines, which means a single bullet has external leading added even though it shouldn't, but frankly this is just an educated guess rather than understanding Writer's layout (heh). Symbol font in some documents is 'StarSymbol; Arial Unicode MS', and Windows machines often do not have StarSymbol, falling back to Arial Unicode MS, which has unusually high external leading. So just reset external leading for fonts which are used to bullets, as those should not be used on multiple lines anyway, so in correct rendering external leading should be irrelevant anyway. Interestingly enough, bSymbol is false for 'StarSymbol; Arial Unicode MS', so also check explicitly.
*/ if( m_bSymbol || IsOpenSymbol( m_pPrtFont->GetFamilyName()))
m_nExtLeading = 0;
}
// tdf#139418: MSO never applies ext leading to vertical text, even if the // NoLeading compatibility flag is unset. constbool bDisableExtLeading
= rIDSA.get(DocumentSettingId::MS_WORD_COMP_GRID_METRICS) && GetFont().IsVertical();
// pOut is the output device, not the reference device void SwFntObj::CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut )
{ if ( m_pScrFont ) return;
// any changes to the output device are reset at the end of the function
OutputDevice* pOut = const_cast<OutputDevice*>(&rOut);
// Save old font
vcl::Font aOldOutFont( pOut->GetFont() );
m_nScrHeight = USHRT_MAX;
// Condition for output font / refdev font adjustment
OutputDevice* pPrt = &rSh.GetRefDev();
if( !rSh.GetWin() ||
!rSh.GetViewOptions()->getBrowseMode() ||
rSh.GetViewOptions()->IsPrtFormat() )
{ // After CreatePrtFont m_pPrtFont is the font which is actually used // by the reference device
CreatePrtFont( *pPrt );
m_pPrinter = pPrt;
// save old reference device font
vcl::Font aOldPrtFnt( pPrt->GetFont() );
// set the font used at the reference device at the reference device // and the output device
pPrt->SetFont( *m_pPrtFont );
pOut->SetFont( *m_pPrtFont );
// This should be the default for pScrFont.
m_pScrFont = m_pPrtFont;
FontMetric aMet = pPrt->GetFontMetric( ); // Don't lose "faked" properties of the logical font that don't truly // exist in the physical font metrics which vcl which fake up for us
aMet.SetWeight(m_pScrFont->GetWeightMaybeAskConfig());
aMet.SetItalic(m_pScrFont->GetItalicMaybeAskConfig());
// reset original output device font
pOut->SetFont( aOldOutFont );
}
void SwFntObj::GuessLeading( const SwViewShell& #ifdefined(_WIN32)
rSh #endif
, const FontMetric& rMet )
{ // If leading >= 5, this seems to be enough leading. // Nothing has to be done. if ( rMet.GetInternalLeading() >= 5 )
{
m_nGuessedLeading = 0; return;
}
#ifdefined(_WIN32)
OutputDevice *pWin = rSh.GetWin() ?
rSh.GetWin()->GetOutDev() :
Application::GetDefaultDevice(); if ( pWin )
{
MapMode aTmpMap( MapUnit::MapTwip );
MapMode aOldMap = pWin->GetMapMode( );
pWin->SetMapMode( aTmpMap ); const vcl::Font aOldFnt( pWin->GetFont() );
pWin->SetFont( *m_pPrtFont ); const FontMetric aWinMet( pWin->GetFontMetric() ); const sal_uInt16 nWinHeight = sal_uInt16( aWinMet.GetFontSize().Height() ); if( m_pPrtFont->GetFamilyName().indexOf( aWinMet.GetFamilyName() ) != -1 )
{ // If the Leading on the Window is also 0, then it has to stay // that way (see also StarMath).
sal_Int32 nTmpLeading = aWinMet.GetInternalLeading(); if( nTmpLeading <= 0 )
{
pWin->SetFont( rMet );
nTmpLeading = pWin->GetFontMetric().GetInternalLeading(); if( nTmpLeading < 0 )
m_nGuessedLeading = 0; else
m_nGuessedLeading = sal_uInt16(nTmpLeading);
} else
{
m_nGuessedLeading = sal_uInt16(nTmpLeading); // Manta-Hack #50153#: // Wer beim Leading luegt, luegt moeglicherweise auch beim // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig // tiefergelegt, ohne dabei seine Hoehe zu aendern. // (above original comment preserved for cultural reasons) // Those who lie about their Leading, may lie about their // Ascent/Descent as well, hence the Font will be lowered a // little without changing its height.
sal_Int32 nDiff = std::min( rMet.GetDescent() - aWinMet.GetDescent(),
aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading ); if( nDiff > 0 )
{
OSL_ENSURE( m_nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" ); if ( m_nPrtAscent < USHRT_MAX )
m_nPrtAscent = m_nPrtAscent + o3tl::narrowing<sal_uInt16>(( 2 * nDiff ) / 5);
}
}
} else
{ // If all else fails, take 15% of the height, as empirically // determined by CL
m_nGuessedLeading = (nWinHeight * 15) / 100;
}
pWin->SetFont( aOldFnt );
pWin->SetMapMode( aOldMap );
} else
m_nGuessedLeading = 0; #else
m_nGuessedLeading = 0; #endif
}
// Set the font at the given output device; for screens it may be // necessary to do some adjustment first. void SwFntObj::SetDevFont( const SwViewShell *pSh, OutputDevice& rOut )
{ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
// Here, we actually do not need the leading values, but by calling // GetFontLeading() we assure that the values are calculated for later use.
GetFontLeading( pSh, rRefDev );
}
// Draw wavy lines for spell and grammar errors only if font is large enough. // Lines for smart tags will always be drawn. if (pWList != rInf.GetSmartTags() && WRONG_SHOW_MIN >= nHght)
{ return;
}
SwForbidden::iterator pIter = rForbidden.begin(); if (rInf.GetOut().GetConnectMetaFile())
rInf.GetOut().Push();
const Color aCol( rInf.GetOut().GetLineColor() );
// iterate over all ranges stored in the respective SwWrongList do
{
nStart -= rInf.GetIdx();
if (stMetrics.aBounds.has_value())
{ if (nMaxAscent)
{
*nMaxAscent = static_cast<SwTwips>(std::ceil(-stMetrics.aBounds->Top()));
}
if (nMaxDescent)
{
*nMaxDescent = static_cast<SwTwips>(std::ceil(stMetrics.aBounds->Bottom()));
}
}
// tdf#88908: Adjust qualifying spaces to half of an ideographic space. // For compatibility, this must be done before all other kinds of justification. if (auto pSh = rExtraInf.GetShell(); pSh)
{ const IDocumentSettingAccess& rIDSA = pSh->getIDocumentSettingAccess(); if (rIDSA.get(DocumentSettingId::BALANCE_SPACES_AND_IDEOGRAPHIC_SPACES))
{ constauto* pFont = rExtraInf.GetFont(); bool bScriptIsCJK = (SwFontScript::CJK == pFont->GetActual());
// true if pOut is the printer and the printer has been used for formatting constbool bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() &&
OUTDEV_PRINTER == rRefDev.GetOutDevType(); constbool bBrowse = ( pWin &&
rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
!rInf.GetShell()->GetViewOptions()->IsPrtFormat() &&
!rInf.GetBullet() &&
( rInf.GetSpace() || !rInf.GetKern() ) &&
!rInf.GetWrong() &&
!rInf.GetGrammarCheck() &&
!rInf.GetSmartTags() &&
!rInf.GetGreyWave() );
// bDirectPrint indicates that we can enter the branch which calls // the DrawText functions instead of calling the DrawTextArray functions constbool bDirectPrint = bPrt || bBrowse;
// Condition for output font / refdev font adjustment constbool bUseScrFont =
lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev );
// robust: better use the printer font instead of using no font at all
OSL_ENSURE( pTmpFont, "No screen or printer font?" ); if ( ! pTmpFont )
pTmpFont = m_pPrtFont;
// HACK: LINESTYLE_WAVE must not be abused any more, hence the grey wave // line of the ExtendedAttributeSets will appear in the font color first
// be sure to have the correct layout mode at the printer if ( m_pPrinter )
{
m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
}
if ( bSwitchL2R )
rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
if ( bSwitchH2V )
rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
// In the good old days we used to have a simple DrawText if the // output device is the printer. Now we need a DrawTextArray if // 1. KanaCompression is enabled // 2. Justified alignment // Simple kerning is handled by DrawStretchText if( rInf.GetSpace() || rInf.GetKanaComp() )
{
KernArray aKernArray;
GetTextArray(rInf.GetOut(), rInf, aKernArray);
std::vector<sal_Bool> aKashidaArray;
if ( LANGUAGE_THAI == aLang )
{ // Use rInf.GetSpace() because it has more precision than // nSpaceAdd:
SwScriptInfo::ThaiJustify( rInf.GetText(), &aKernArray,
rInf.GetIdx(), rInf.GetLen(),
rInf.GetNumberOfBlanks(),
rInf.GetSpace() );
// adding space to blanks is already done
bSpecialJust = true;
nSpaceAdd = 0;
}
}
}
double nKernSum = rInf.GetKern();
if ( bStretch || m_bPaintBlank || rInf.GetKern() || bSpecialJust )
{ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++,
nKernSum += rInf.GetKern() )
{ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
nKernSum += nSpaceAdd;
aKernArray[i] += nKernSum;
}
// In case of underlined/strike-through justified text // a blank at the end requires special handling: if( m_bPaintBlank && rInf.GetLen() && ( CH_BLANK ==
rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen())-1]))
{ // If it is a single underlined space, output 2 spaces: if (TextFrameIndex(1) == rInf.GetLen())
{
aKernArray[0] = rInf.GetWidth() + nSpaceAdd;
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
aKernArray, aKashidaArray, sal_Int32(rInf.GetIdx()), 1 );
} else
{
sal_Int32 nIndex(sal_Int32(rInf.GetLen()) - 2);
aKernArray[nIndex] += nSpaceAdd;
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
aKashidaArray, sal_Int32{ rInf.GetIdx() },
sal_Int32{ rInf.GetLen() }, rInf.GetLayoutContext());
}
} else {
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
aKashidaArray, sal_Int32{ rInf.GetIdx() },
sal_Int32{ rInf.GetLen() }, rInf.GetLayoutContext());
}
} else
{
Point aTmpPos( aTextOriginPos );
sal_Int32 j = 0;
sal_Int32 i; for( i = 0; i < sal_Int32(rInf.GetLen()); i++ )
{ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
{
nKernSum += nSpaceAdd; if( j < i )
rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
sal_Int32(rInf.GetIdx()) + j, i - j);
j = i + 1;
SwTwips nAdd = aKernArray[ i ] + nKernSum; if ( ( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl ) == nMode )
nAdd *= -1;
aTmpPos.setX( aTextOriginPos.X() + nAdd );
}
} if( j < i )
rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
sal_Int32(rInf.GetIdx()) + j, i - j);
}
} elseif( bStretch )
{
tools::Long nTmpWidth = rInf.GetWidth(); if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() )
nTmpWidth -= rInf.GetKern();
rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
rInf.GetText(),
sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
} elseif( rInf.GetKern() )
{ const tools::Long nTmpWidth = GetTextSize( rInf ).Width();
// OLE: no printer available // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" ) if ( m_pPrinter )
{ // pTmpFont has already been set as current font for rInf.GetOut() if ( m_pPrinter.get() != rInf.GetpOut() || pTmpFont != m_pPrtFont )
{ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
m_pPrinter->SetFont( *m_pPrtFont );
}
xFormattingDevice = m_pPrinter;
} else
{
xFormattingDevice = &rInf.GetOut();
}
//tdf#152094 see if we can retain a subpixel factor int nSubPixels = 1;
MapMode aMapMode(xFormattingDevice->GetMapMode()); if (aMapMode.IsSimple() && aMapMode.GetMapUnit() == MapUnit::MapTwip)
{ if (xFormattingDevice->GetDPIX() == xFormattingDevice->GetDPIY())
{ int nRatio = xFormattingDevice->GetDPIX() / 1440; if (nRatio * 1440 == xFormattingDevice->GetDPIX())
nSubPixels = nRatio;
}
}
KernArray aKernArray(nSubPixels);
GetTextArray(*xFormattingDevice, rInf, aKernArray);
std::vector<sal_Bool> aKashidaArray;
// Modify Printer and ScreenArrays for special justifications
// adding space to blanks is already done
nSpaceAdd = 0;
}
}
}
if( bBullet )
{ // Copy the substring that will be painted, and replace spaces with // bullets, and everything else with space.
sal_Int32 nCopyStart = sal_Int32(rInf.GetIdx());
sal_Int32 nCopyLen = sal_Int32(rInf.GetLen());
// In case of a single underlined space in justified text, // have to output 2 spaces: if ((nCnt == TextFrameIndex(1)) && rInf.GetSpace() && (cChPrev == CH_BLANK))
{
aKernArray[0] = rInf.GetWidth() +
rInf.GetKern() +
(rInf.GetSpace() / SPACING_PRECISION_FACTOR);
if ( bSwitchL2R )
rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
if ( bSwitchH2V )
rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
SwForbidden aForbidden; // draw line for smart tag data
lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() ); // draw wave line for spell check errors // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict. // reason: some grammar errors can only be found if spelling errors are fixed, // therefore we don't want the user to miss a spelling error.
lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, m_pPrtFont->GetFontSize() ); // draw wave line for grammar check errors
lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
}
}
sal_Int32 nLen = sal_Int32(rInf.GetLen());
if( nLen > 0 )
{
if ( bSwitchL2R )
rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
if ( bSwitchH2V )
rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
sal_Int32 nIdx = sal_Int32(rInf.GetIdx());
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
aKashidaArray, nIdx, nLen, rInf.GetLayoutContext()); if (bBullet)
{
rInf.GetOut().Push();
Color aPreviousColor = pTmpFont->GetColor();
// If the measure length is different from the length, then we are // measuring substring width for caret positioning, see SetMeasureLength() // use in TextCursor::GetCharRect_(). bool bCaret(nMsrLn != nLn);
// be sure to have the correct layout mode at the printer if ( m_pPrinter )
{
m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
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.