/* -*- 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 .
*/
// Get the effective number format, including formula result types. // This assumes that a formula cell has already been calculated.
sal_uLong GetResultValueFormat() const { return nValueFormat;}
// ScOutputData::LayoutStrings() usually triggers a number of calls that require // to lay out the text, which is relatively slow, so cache that operation. const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const
{ return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString);
}
private:
tools::Long GetMaxDigitWidth(); // in logic units
tools::Long GetSignWidth();
tools::Long GetDotWidth();
tools::Long GetExpWidth(); void TextChanged();
};
// #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled) if ( bLineBreak )
eAttrHorJust = SvxCellHorJustify::Standard;
}
if (pOutput->mbSyntaxMode)
pOutput->SetSyntaxColor(&aFont, rCell);
// There is no cell attribute for kerning, default is kerning OFF, all // kerning is stored at an EditText object that is drawn using EditEngine. // See also matching kerning cases in ScColumn::GetNeededSize and // ScColumn::GetOptimalColWidth.
aFont.SetKerning(FontKerning::NONE);
// if there is the leading 0 on a printer device, we have problems // -> take metric from the screen (as for EditEngine!) if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
{
OutputDevice* pDefaultDev = Application::GetDefaultDevice();
MapMode aOld = pDefaultDev->GetMapMode();
pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
aMetric = pDefaultDev->GetFontMetric( aFont );
pDefaultDev->SetMapMode( aOld );
}
// Measuring a string containing a single copy of the repeat char is inaccurate. // To increase accuracy, start with a representative sample of a padding sequence.
constexpr sal_Int32 nSampleSize = 20;
OUStringBuffer aFill(nSampleSize);
comphelper::string::padToLength(aFill, nSampleSize, nRepeatChar);
// Intentionally truncate to round toward zero auto nCharWidth = static_cast<tools::Long>(nAvgCharWidth); if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) ) return;
// Are there restrictions on the cell type we should filter out here ?
tools::Long nTextWidth = aTextSize.Width(); if ( bPixelToLogic )
{
nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
}
// Intentionally truncate to round toward zero auto nCharsToInsert = static_cast<sal_Int32>(static_cast<double>(nSpaceToFill) / nAvgCharWidth);
aFill.ensureCapacity(nCharsToInsert);
comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
aString = aString.replaceAt( nRepeatPos, 0, aFill );
TextChanged();
}
bool ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::LongnWidth )
{ // #i113045# do the single-character width calculations in logic units if (bPixelToLogic)
nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
CellType eType = rCell.getType(); if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA) // must be a value or formula cell. returnfalse;
if (eType == CELLTYPE_FORMULA)
{
ScFormulaCell* pFCell = rCell.getFormula(); if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
{
SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#) returntrue;
} // If it's formula, the result must be a value. if (!pFCell->IsValue()) returnfalse;
}
sal_uLong nFormat = GetResultValueFormat(); if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
{ // Not 'General' number format. Set hash text and bail out.
SetHashText(); returntrue;
}
double fVal = rCell.getValue();
const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat); if (!pNumFormat) returnfalse;
tools::Long nMaxDigit = GetMaxDigitWidth(); if (!nMaxDigit) returnfalse;
// #i112250# A small value might be formatted as "0" when only counting the digits, // but fit into the column when considering the smaller width of the decimal separator. if (aString == "0" && fVal != 0.0)
nDecimalCount = 1;
if (nDecimalCount)
nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount; if (nSignCount)
nWidth += (nMaxDigit - GetSignWidth()) * nSignCount; if (nExpCount)
nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
if (nDecimalCount || nSignCount || nExpCount)
{ // Re-calculate.
nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
OUString sTempOut(aString); if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut, pOutput->mpDoc->GetFormatTable()->GetNatNum()))
{
aString = sTempOut; // Failed to get output string. Bail out. returnfalse;
}
aString = sTempOut;
}
tools::Long nActualTextWidth = GetFmtTextWidth(aString); if (nActualTextWidth > nWidth)
{ // Even after the decimal adjustment the text doesn't fit. Give up.
SetHashText(); returntrue;
}
TextChanged();
maLastCell.clear(); // #i113022# equal cell and format in another column may give different string returnfalse;
}
maLastCell.clear(); // the same text may fit in the next cell
}
tools::Long ScDrawStringsVars::GetMaxDigitWidth()
{ if (nMaxDigitWidth > 0) return nMaxDigitWidth;
for (char i = 0; i < 10; ++i)
{ char cDigit = '0' + i; // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
} return nMaxDigitWidth;
}
tools::Long ScDrawStringsVars::GetSignWidth()
{ if (nSignWidth > 0) return nSignWidth;
bool ScDrawStringsVars::HasEditCharacters() const
{ for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
{ switch(aString[nIdx])
{ case CHAR_NBSP: // tdf#122676: Ignore CHAR_NBSP (this is thousand separator in any number) // if repeat character is set if (nRepeatPos < 0) returntrue; break; case CHAR_SHY: case CHAR_ZWSP: case CHAR_LRM: case CHAR_RLM: case CHAR_NBHY: case CHAR_WJ: returntrue; default: break;
}
}
returnfalse;
}
double ScOutputData::GetStretch() const
{ if ( mpRefDevice->IsMapModeEnabled() )
{ // If a non-trivial MapMode is set, its scale is now already // taken into account in the OutputDevice's font handling // (OutputDevice::ImplNewFont, see #95414#). // The old handling below is only needed for pixel output. return 1.0;
}
// calculation in double is faster than Fraction multiplication // and doesn't overflow
if ( mpRefDevice == pFmtDevice )
{
MapMode aOld = mpRefDevice->GetMapMode(); returnstatic_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
} else
{ // when formatting for printer, device map mode has already been taken care of returnstatic_cast<double>(aZoomY) / static_cast<double>(aZoomX);
}
}
if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
{ // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
{ // apply the same logic here as in DrawStrings/DrawEdit: // Stop at non-empty or merged or overlapped cell, // where a note is empty as well as a cell that's hidden by protection settings
// nX, nArrY: loop variables from DrawStrings / DrawEdit // nPosX, nPosY: corresponding positions for nX, nArrY // nCellX, nCellY: position of the cell that contains the text // nNeeded: Text width, including margin // rPattern: cell format at nCellX, nCellY // nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings // bCellIsValue: if set, don't extend into empty cells // bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set) // bOverwrite: if set, also extend into non-empty cells (for rotated text) // rParam output: various area parameters.
void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::LongnPosY,
SCCOL nCellX, SCROW nCellY, tools::Long nNeeded, const ScPatternAttr& rPattern,
sal_uInt16 nHorJustify, bool bCellIsValue, bool bBreak, bool bOverwrite,
OutputAreaParam& rParam )
{ // rThisRowInfo may be for a different row than nCellY, is still used for clip marks
RowInfo& rThisRowInfo = pRowInfo[nArrY];
// nLeftMissing, nRightMissing are logical, eHorJust values are visual if ( bLayoutRTL )
::std::swap( nLeftMissing, nRightMissing );
SCCOL nRightX = nCellX;
SCCOL nLeftX = nCellX; if ( !bMerged && !bCellIsValue && !bBreak )
{ // look for empty cells into which the text can be extended
bool beginsWithRTLCharacter(const OUString& rStr)
{ if (rStr.isEmpty()) returnfalse;
switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
{ case i18n::DirectionProperty_RIGHT_TO_LEFT: case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC: case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING: case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE: returntrue; default:
;
}
returnfalse;
}
}
/** Get left, right or centered alignment from RTL context.
Does not return standard, block or repeat, for these the contextual left or right alignment is returned.
*/ static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust, bool bCellIsValue, const OUString& rText, const ScPatternAttr& rPattern, const SfxItemSet* pCondSet, const ScDocument* pDoc, SCTAB nTab, constbool bNumberFormatIsText )
{
SvxCellHorJustify eHorJustContext = eInHorJust; bool bUseWritingDirection = false; if (eInHorJust == SvxCellHorJustify::Standard)
{ // fdo#32530: Default alignment depends on value vs // string, and the direction of the 1st letter. if (beginsWithRTLCharacter( rText)) //If language is RTL
{ if (bCellIsValue)
eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left; else
eHorJustContext = SvxCellHorJustify::Right;
} elseif (bCellIsValue) //If language is not RTL
eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right; else
bUseWritingDirection = true;
}
if (bUseWritingDirection ||
eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
{
SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet); if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
eHorJustContext = SvxCellHorJustify::Left; elseif (nDirection == SvxFrameDirection::Environment)
{
SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL"); // fdo#73588: The content of the cell must also // begin with a RTL character to be right // aligned; otherwise, it should be left aligned.
eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
} else
eHorJustContext = SvxCellHorJustify::Right;
} return eHorJustContext;
}
// Try to limit interpreting to only visible cells. Calling e.g. IsValue() // on a formula cell that needs interpreting would call Interpret() // for the entire formula group, which could be large.
mpDoc->InterpretCellsIfNeeded( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ));
// alternative pattern instances in case we need to modify the pattern // before processing the cell value.
std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
// skip text in cell if data bar/icon set is set and only value selected if ( bDoCell )
{ if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
bDoCell = false; if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
bDoCell = false;
}
// output the cell text
ScRefCellValue aCell; if (bDoCell)
{ if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
aCell = pThisRowInfo->cellInfo(nCellX).maCell; else
GetVisibleCell( nCellX, nCellY, nTab, aCell ); // get from document if (aCell.isEmpty())
bDoCell = false; elseif (aCell.getType() == CELLTYPE_EDIT)
bUseEditEngine = true;
}
// Check if this cell is mis-spelled. if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
{ if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
bUseEditEngine = true;
}
if (bDoCell && !bUseEditEngine)
{ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
{
ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
pPattern = rCellInfo.pPatternAttr;
pCondSet = rCellInfo.pConditionSet;
if ( !pPattern )
{ // #i68085# pattern from cell info for hidden columns is null, // test for null is quicker than using column flags
pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
}
} else// get from document
{
pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
} if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() )
{
aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
ScPatternAttr* pAltPattern = aAltPatterns.back().get(); if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
{
pAltPattern->SetStyleSheet(pPreviewStyle);
} elseif ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
{ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
pAltPattern->GetItemSet().Put( *pItem ); if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
pAltPattern->GetItemSet().Put( *pItem ); if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
pAltPattern->GetItemSet().Put( *pItem );
}
pPattern = pAltPattern;
}
if (aCell.hasNumeric() &&
pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
{ // Disable line break when the cell content is numeric.
aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
ScPatternAttr* pAltPattern = aAltPatterns.back().get();
ScLineBreakCell aLineBreak(false);
pAltPattern->GetItemSet().Put(aLineBreak);
pPattern = pAltPattern;
}
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.