sc/source/ui/inc/output.hxx | 15 sc/source/ui/view/output2.cxx | 1168 +++++++++++++++++++++--------------------- 2 files changed, 609 insertions(+), 574 deletions(-)
New commits: commit 4bf3cda4dabd2fb37e386e21e578e2fdab0f1c61 Author: Noel Grandin <[email protected]> AuthorDate: Sat Jul 19 15:34:44 2025 +0200 Commit: Noel Grandin <[email protected]> CommitDate: Sat Jul 26 13:45:58 2025 +0200 extract inner loop of ScOutputData::LayoutStrings into another func because it is getting rather hard to read Change-Id: I29535acf66b0a540727a8215e3ea8f2929b1dfb4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188068 Tested-by: Jenkins Reviewed-by: Noel Grandin <[email protected]> (cherry picked from commit 92b7ef1baa4c8769a6788b3df561a88a89fff0ff) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188358 Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/sc/source/ui/inc/output.hxx b/sc/source/ui/inc/output.hxx index 3c6ebf6cf765..b7933cef53f6 100644 --- a/sc/source/ui/inc/output.hxx +++ b/sc/source/ui/inc/output.hxx @@ -54,6 +54,7 @@ class ScPageBreakData; class FmFormView; class ScFieldEditEngine; class SdrPaintWindow; +class ScDrawStringsVars; #define SC_SCENARIO_HSPACE 60 #define SC_SCENARIO_VSPACE 50 @@ -313,6 +314,20 @@ private: // and the single call to end of constructor to be sure this always happens void SetCellRotations(); + /// inner loop of LayoutStrings + void LayoutStringsImpl(bool bPixelToLogic, RowInfo* pThisRowInfo, SCCOL nX, SCROW nY, SCSIZE nArrY, + std::optional<SCCOL>& oLastEmptyCellX, + SCCOL nLastContentCol, + std::vector<std::unique_ptr<ScPatternAttr> >& aAltPatterns, + const ScPatternAttr*& pOldPattern, + const SfxItemSet*& pOldCondSet, + SvtScriptType& nOldScript, + ScDrawStringsVars& aVars, + bool& bProgress, tools::Long nPosX, tools::Long nPosY, bool bTaggedPDF, + vcl::PDFExtOutDevData* pPDF, + tools::Long nLayoutSign, + KernArray& aDX); + public: /** diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx index c241f65ab211..e581e00e9975 100644 --- a/sc/source/ui/view/output2.cxx +++ b/sc/source/ui/view/output2.cxx @@ -1544,11 +1544,6 @@ void ScOutputData::LayoutStrings(bool bPixelToLogic) --nLoopStartX; // start before nX1 for rest of long text to the left // variables for GetOutputArea - OutputAreaParam aAreaParam; - bool bCellIsValue = false; - tools::Long nNeededWidth = 0; - const ScPatternAttr* pPattern = nullptr; - const SfxItemSet* pCondSet = nullptr; const ScPatternAttr* pOldPattern = nullptr; const SfxItemSet* pOldCondSet = nullptr; SvtScriptType nOldScript = SvtScriptType::NONE; @@ -1590,658 +1585,683 @@ void ScOutputData::LayoutStrings(bool bPixelToLogic) { if (bTaggedPDF) pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::TableData, u"TD"_ustr); + LayoutStringsImpl(bPixelToLogic, pThisRowInfo, nX, nY, nArrY, oLastEmptyCellX, nLastContentCol, + aAltPatterns, pOldPattern, pOldCondSet, nOldScript, aVars, + bProgress, nPosX, nPosY, bTaggedPDF, pPDF, nLayoutSign, aDX); + nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign; + if (bTaggedPDF) + pPDF->EndStructureElement(); + } + if (bTaggedPDF) + pPDF->EndStructureElement(); + } + nPosY += pRowInfo[nArrY].nHeight; + } + if (bTaggedPDF) + pPDF->EndStructureElement(); - bool bMergeEmpty = false; - const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX); - bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText; - - SCCOL nCellX = nX; // position where the cell really starts - SCROW nCellY = nY; - bool bDoCell = false; - bool bUseEditEngine = false; - - // Part of a merged cell? + if ( bProgress ) + ScProgress::DeleteInterpretProgress(); +} - bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped); - if ( bOverlapped ) - { - bEmpty = true; +/// inner loop of LayoutStrings +void ScOutputData::LayoutStringsImpl(bool const bPixelToLogic, RowInfo* const pThisRowInfo, + SCCOL const nX, SCROW const nY, SCSIZE const nArrY, + std::optional<SCCOL>& oLastEmptyCellX, + SCCOL const nLastContentCol, + std::vector<std::unique_ptr<ScPatternAttr> >& aAltPatterns, + const ScPatternAttr*& pOldPattern, + const SfxItemSet*& pOldCondSet, + SvtScriptType& nOldScript, + ScDrawStringsVars& aVars, + bool& bProgress, tools::Long const nPosX, tools::Long const nPosY, + bool const bTaggedPDF, vcl::PDFExtOutDevData* const pPDF, + tools::Long const nLayoutSign, KernArray& aDX) +{ + const ScPatternAttr* pPattern = nullptr; + const SfxItemSet* pCondSet = nullptr; + bool bCellIsValue = false; + tools::Long nNeededWidth = 0; + OutputAreaParam aAreaParam; + bool bMergeEmpty = false; + const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX); + bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText; - SCCOL nOverX; // start of the merged cells - SCROW nOverY; - bool bVisChanged = !pRowInfo[nArrY-1].bChanged; - if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged )) - { - nCellX = nOverX; - nCellY = nOverY; - bDoCell = true; - } - else - bMergeEmpty = true; - } + SCCOL nCellX = nX; // position where the cell really starts + SCROW nCellY = nY; + bool bDoCell = false; + bool bUseEditEngine = false; - // Rest of a long text further to the left? + // Part of a merged cell? - if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped ) - { - if (!oLastEmptyCellX) - { - SCCOL nTempX=nX1; - while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY )) - --nTempX; - oLastEmptyCellX = nTempX; - } + bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped); + if ( bOverlapped ) + { + bEmpty = true; - if ( *oLastEmptyCellX < nX1 && - !IsEmptyCellText( pThisRowInfo, *oLastEmptyCellX, nY ) && - !mpDoc->HasAttrib( *oLastEmptyCellX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) - { - nCellX = *oLastEmptyCellX; - bDoCell = true; - } - } + SCCOL nOverX; // start of the merged cells + SCROW nOverY; + bool bVisChanged = !pRowInfo[nArrY-1].bChanged; + if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged )) + { + nCellX = nOverX; + nCellY = nOverY; + bDoCell = true; + } + else + bMergeEmpty = true; + } - // Rest of a long text further to the right? + // Rest of a long text further to the left? - if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped ) - { - // don't have to look further than nLastContentCol + if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped ) + { + if (!oLastEmptyCellX) + { + SCCOL nTempX=nX1; + while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY )) + --nTempX; + oLastEmptyCellX = nTempX; + } - SCCOL nTempX=nX; - while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) - ++nTempX; + if ( *oLastEmptyCellX < nX1 && + !IsEmptyCellText( pThisRowInfo, *oLastEmptyCellX, nY ) && + !mpDoc->HasAttrib( *oLastEmptyCellX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) + { + nCellX = *oLastEmptyCellX; + bDoCell = true; + } + } - if ( nTempX > nX && - !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && - !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) - { - nCellX = nTempX; - bDoCell = true; - } - } + // Rest of a long text further to the right? - // normal visible cell + if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped ) + { + // don't have to look further than nLastContentCol - if (!bEmpty) - bDoCell = true; + SCCOL nTempX=nX; + while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) + ++nTempX; - // don't output the cell that's being edited + if ( nTempX > nX && + !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && + !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) + { + nCellX = nTempX; + bDoCell = true; + } + } - if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow ) - bDoCell = false; + // normal visible cell - // 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; - } + if (!bEmpty) + bDoCell = true; - // output the cell text + // don't output the cell that's being edited - 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; - else if (aCell.getType() == CELLTYPE_EDIT) - bUseEditEngine = true; - } + if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow ) + bDoCell = false; - // Check if this cell is mis-spelled. - if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING) - { - if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY)) - bUseEditEngine = true; - } + // 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; + } - if (bDoCell && !bUseEditEngine) - { - if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) - { - ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX); - pPattern = rCellInfo.pPatternAttr; - pCondSet = rCellInfo.pConditionSet; + // output the cell text - 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); - } - else if ( 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; - } + 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; + else if (aCell.getType() == CELLTYPE_EDIT) + bUseEditEngine = true; + } - 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; - } + // Check if this cell is mis-spelled. + if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING) + { + if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY)) + bUseEditEngine = true; + } - SvtScriptType nScript = mpDoc->GetCellScriptType( - ScAddress(nCellX, nCellY, nTab), - pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet)); + if (bDoCell && !bUseEditEngine) + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) + { + ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX); + pPattern = rCellInfo.pPatternAttr; + pCondSet = rCellInfo.pConditionSet; - if (nScript == SvtScriptType::NONE) - nScript = ScGlobal::GetDefaultScriptType(); + 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); + } + else if ( 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 ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet || - nScript != nOldScript || mbSyntaxMode ) - { - if ( StringDiffer(pOldPattern,pPattern) || - pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode ) - { - aVars.SetPattern(pPattern, pCondSet, aCell, nScript); - } - else - aVars.SetPatternSimple( pPattern, pCondSet ); - pOldPattern = pPattern; - pOldCondSet = pCondSet; - nOldScript = nScript; - } + 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; + } - // use edit engine for rotated, stacked or mixed-script text - if ( aVars.GetOrient() == SvxCellOrientation::Stacked || - aVars.IsRotated() || IsAmbiguousScript(nScript) ) - bUseEditEngine = true; - } - if (bDoCell && !bUseEditEngine) - { - bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA); - if ( bFormulaCell ) - lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula()); - if ( aVars.SetText(aCell) ) - pOldPattern = nullptr; - bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult()); - } - tools::Long nTotalMargin = 0; - SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard; - if (bDoCell && !bUseEditEngine) - { - CellType eCellType = aCell.getType(); - bCellIsValue = ( eCellType == CELLTYPE_VALUE ); - if ( eCellType == CELLTYPE_FORMULA ) - { - ScFormulaCell* pFCell = aCell.getFormula(); - bCellIsValue = pFCell->IsRunning() || pFCell->IsValue(); - } + SvtScriptType nScript = mpDoc->GetCellScriptType( + ScAddress(nCellX, nCellY, nTab), + pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet)); - const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab ); - eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(), - *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText ); + if (nScript == SvtScriptType::NONE) + nScript = ScGlobal::GetDefaultScriptType(); - bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block ); - // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats - // Must be synchronized with ScColumn::GetNeededSize() - SvNumberFormatter* pFormatter = mpDoc->GetFormatTable(); - if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER)) - bBreak = false; + if ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet || + nScript != nOldScript || mbSyntaxMode ) + { + if ( StringDiffer(pOldPattern,pPattern) || + pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode ) + { + aVars.SetPattern(pPattern, pCondSet, aCell, nScript); + } + else + aVars.SetPatternSimple( pPattern, pCondSet ); + pOldPattern = pPattern; + pOldCondSet = pCondSet; + nOldScript = nScript; + } - bool bRepeat = aVars.IsRepeat() && !bBreak; - bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat; + // use edit engine for rotated, stacked or mixed-script text + if ( aVars.GetOrient() == SvxCellOrientation::Stacked || + aVars.IsRotated() || IsAmbiguousScript(nScript) ) + bUseEditEngine = true; + } + if (bDoCell && !bUseEditEngine) + { + bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA); + if ( bFormulaCell ) + lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula()); + if ( aVars.SetText(aCell) ) + pOldPattern = nullptr; + bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult()); + } + tools::Long nTotalMargin = 0; + SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard; + if (bDoCell && !bUseEditEngine) + { + CellType eCellType = aCell.getType(); + bCellIsValue = ( eCellType == CELLTYPE_VALUE ); + if ( eCellType == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = aCell.getFormula(); + bCellIsValue = pFCell->IsRunning() || pFCell->IsValue(); + } - nTotalMargin = - static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) + - static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX); + const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab ); + eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(), + *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText ); - nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin; + bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block ); + // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats + // Must be synchronized with ScColumn::GetNeededSize() + SvNumberFormatter* pFormatter = mpDoc->GetFormatTable(); + if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER)) + bBreak = false; - // GetOutputArea gives justified rectangles - GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth, - *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), - bCellIsValue || bRepeat || bShrink, bBreak, false, - aAreaParam ); + bool bRepeat = aVars.IsRepeat() && !bBreak; + bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat; - aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin ); - if ( bShrink ) - { - if ( aVars.GetOrient() != SvxCellOrientation::Standard ) - { - // Only horizontal scaling is handled here. - // DrawEdit is used to vertically scale 90 deg rotated text. - bUseEditEngine = true; - } - else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal - { - tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; - tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin + nTotalMargin = + static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) + + static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX); - if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats) - { - tools::Long nScale = ( nAvailable * 100 ) / nScaleSize; + nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin; - aVars.SetShrinkScale( nScale, nOldScript ); - tools::Long nNewSize = aVars.GetTextSize().Width(); + // GetOutputArea gives justified rectangles + GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth, + *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), + bCellIsValue || bRepeat || bShrink, bBreak, false, + aAreaParam ); - sal_uInt16 nShrinkAgain = 0; - while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) - { - // If the text is still too large, reduce the scale again by 10%, until it fits, - // at most 7 times (it's less than 50% of the calculated scale then). + aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin ); + if ( bShrink ) + { + if ( aVars.GetOrient() != SvxCellOrientation::Standard ) + { + // Only horizontal scaling is handled here. + // DrawEdit is used to vertically scale 90 deg rotated text. + bUseEditEngine = true; + } + else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal + { + tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; + tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin - nScale = ( nScale * 9 ) / 10; - aVars.SetShrinkScale( nScale, nOldScript ); - nNewSize = aVars.GetTextSize().Width(); - ++nShrinkAgain; - } - // If even at half the size the font still isn't rendered smaller, - // fall back to normal clipping (showing ### for numbers). - if ( nNewSize <= nAvailable ) - { - // Reset relevant parameters. - aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; - aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; - } + if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats) + { + tools::Long nScale = ( nAvailable * 100 ) / nScaleSize; - pOldPattern = nullptr; - } - } - } + aVars.SetShrinkScale( nScale, nOldScript ); + tools::Long nNewSize = aVars.GetTextSize().Width(); - if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip ) + sal_uInt16 nShrinkAgain = 0; + while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) { - tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; - tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin - // When formatting for the printer, the text sizes don't always add up. - // Round down (too few repetitions) rather than exceeding the cell size then: - if ( pFmtDevice != mpRefDevice ) - ++nRepeatSize; - if ( nRepeatSize > 0 ) - { - tools::Long nRepeatCount = nAvailable / nRepeatSize; - if ( nRepeatCount > 1 ) - { - OUString aCellStr = aVars.GetString(); - OUStringBuffer aRepeated(aCellStr); - for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) - aRepeated.append(aCellStr); - aVars.SetAutoText( aRepeated.makeStringAndClear() ); - } - } - } + // If the text is still too large, reduce the scale again by 10%, until it fits, + // at most 7 times (it's less than 50% of the calculated scale then). - // use edit engine if automatic line breaks are needed - if ( bBreak ) - { - if ( aVars.GetOrient() == SvxCellOrientation::Standard ) - bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ); - else - { - tools::Long nHeight = aVars.GetTextSize().Height() + - static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) + - static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY); - bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() ); - } + nScale = ( nScale * 9 ) / 10; + aVars.SetShrinkScale( nScale, nOldScript ); + nNewSize = aVars.GetTextSize().Width(); + ++nShrinkAgain; } - if (!bUseEditEngine) + // If even at half the size the font still isn't rendered smaller, + // fall back to normal clipping (showing ### for numbers). + if ( nNewSize <= nAvailable ) { - bUseEditEngine = - aVars.GetHorJust() == SvxCellHorJustify::Block && - aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute; + // Reset relevant parameters. + aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; + aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; } + + pOldPattern = nullptr; } - if (bUseEditEngine) + } + } + + if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip ) + { + tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; + tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin + // When formatting for the printer, the text sizes don't always add up. + // Round down (too few repetitions) rather than exceeding the cell size then: + if ( pFmtDevice != mpRefDevice ) + ++nRepeatSize; + if ( nRepeatSize > 0 ) + { + tools::Long nRepeatCount = nAvailable / nRepeatSize; + if ( nRepeatCount > 1 ) { - // mark the cell in ScCellInfo to be drawn in DrawEdit: - // Cells to the left are marked directly, cells to the - // right are handled by the flag for nX2 - SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2; - pThisRowInfo->basicCellInfo(nMarkX).bEditEngine = true; - bDoCell = false; // don't draw here - - // Mark the tagged "TD" structure element to be drawn in DrawEdit - if (bTaggedPDF) - { - sal_Int32 nId = pPDF->GetCurrentStructureElement(); - pPDF->GetScPDFState()->m_TableDataMap[{nY, nX}] = nId; - } + OUString aCellStr = aVars.GetString(); + OUStringBuffer aRepeated(aCellStr); + for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) + aRepeated.append(aCellStr); + aVars.SetAutoText( aRepeated.makeStringAndClear() ); } - if ( bDoCell ) - { - if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) - { - bool bHasHashText = false; - if (mbShowFormulas) - { - aVars.SetHashText(); - bHasHashText = true; - } - else - // Adjust the decimals to fit the available column width. - bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin ); - - if ( bHasHashText ) - { - tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX; - - if ( eOutHorJust == SvxCellHorJustify::Left ) - { - if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) - pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; - bAnyClipped = true; - aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); - } - else if ( eOutHorJust == SvxCellHorJustify::Right ) - { - if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) - pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; - bAnyClipped = true; - aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); - } - else - { - if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) - { - pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; - pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; - } - bAnyClipped = true; - aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); - aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); - } - } - - nNeededWidth = aVars.GetTextSize().Width() + - static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) + - static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ); - if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() ) - { - // Cell value is no longer clipped. Reset relevant parameters. - aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; - aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; - } - } + } + } - tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added - tools::Long nJustPosY = aAreaParam.maAlignRect.Top(); - tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth(); - tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight(); + // use edit engine if automatic line breaks are needed + if ( bBreak ) + { + if ( aVars.GetOrient() == SvxCellOrientation::Standard ) + bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ); + else + { + tools::Long nHeight = aVars.GetTextSize().Height() + + static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) + + static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY); + bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() ); + } + } + if (!bUseEditEngine) + { + bUseEditEngine = + aVars.GetHorJust() == SvxCellHorJustify::Block && + aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute; + } + } + if (bUseEditEngine) + { + // mark the cell in ScCellInfo to be drawn in DrawEdit: + // Cells to the left are marked directly, cells to the + // right are handled by the flag for nX2 + SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2; + pThisRowInfo->basicCellInfo(nMarkX).bEditEngine = true; + bDoCell = false; // don't draw here - bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW ); - // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip - bool bVClip = AdjustAreaParamClipRect(aAreaParam); - bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip; + // Mark the tagged "TD" structure element to be drawn in DrawEdit + if (bTaggedPDF) + { + sal_Int32 nId = pPDF->GetCurrentStructureElement(); + pPDF->GetScPDFState()->m_TableDataMap[{nY, nX}] = nId; + } + } + if ( bDoCell ) + { + if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) + { + bool bHasHashText = false; + if (mbShowFormulas) + { + aVars.SetHashText(); + bHasHashText = true; + } + else + // Adjust the decimals to fit the available column width. + bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin ); - // check horizontal space + if ( bHasHashText ) + { + tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX; - if ( !bOutside ) + if ( eOutHorJust == SvxCellHorJustify::Left ) + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) + pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; + bAnyClipped = true; + aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); + } + else if ( eOutHorJust == SvxCellHorJustify::Right ) + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) + pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; + bAnyClipped = true; + aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); + } + else + { + if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 ) { - bool bRightAdjusted = false; // to correct text width calculation later - switch (eOutHorJust) - { - case SvxCellHorJustify::Left: - nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ); - break; - case SvxCellHorJustify::Right: - nJustPosX += nAvailWidth - aVars.GetTextSize().Width() - - static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX ); - bRightAdjusted = true; - break; - case SvxCellHorJustify::Center: - nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() + - static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) - - static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2; - break; - default: - { - // added to avoid warnings - } - } + pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; + pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; + } + bAnyClipped = true; + aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); + aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); + } + } - tools::Long nTestClipHeight = aVars.GetTextSize().Height(); - switch (aVars.GetVerJust()) - { - case SvxCellVerJustify::Top: - case SvxCellVerJustify::Block: - { - tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); - nJustPosY += nTop; - nTestClipHeight += nTop; - } - break; - case SvxCellVerJustify::Bottom: - { - tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); - nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot; - nTestClipHeight += nBot; - } - break; - case SvxCellVerJustify::Center: - { - tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); - tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); - nJustPosY += ( nOutHeight + nTop - - aVars.GetTextSize().Height() - nBot ) / 2; - nTestClipHeight += std::abs( nTop - nBot ); - } - break; - default: - { - // added to avoid warnings - } - } + nNeededWidth = aVars.GetTextSize().Width() + + static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) + + static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ); + if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() ) + { + // Cell value is no longer clipped. Reset relevant parameters. + aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; + aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; + } + } - if ( nTestClipHeight > nOutHeight ) - { - // no vertical clipping when printing cells with optimal height, - // except when font size is from conditional formatting. - if ( eType != OUTTYPE_PRINTER || - ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) || - ( aVars.HasCondHeight() ) ) - bVClip = true; - } + tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added + tools::Long nJustPosY = aAreaParam.maAlignRect.Top(); + tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth(); + tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight(); - if ( bHClip || bVClip ) - { - // only clip the affected dimension so that not all right-aligned - // columns are cut off when performing a non-proportional resize - if (!bHClip) - { - aAreaParam.maClipRect.SetLeft( nScrX ); - aAreaParam.maClipRect.SetRight( nScrX+nScrW ); - } - if (!bVClip) - { - aAreaParam.maClipRect.SetTop( nScrY ); - aAreaParam.maClipRect.SetBottom( nScrY+nScrH ); - } + bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW ); + // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip + bool bVClip = AdjustAreaParamClipRect(aAreaParam); + bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip; - // aClipRect is not used after SetClipRegion/IntersectClipRegion, - // so it can be modified here - if (bPixelToLogic) - aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect ); + // check horizontal space - if (bMetaFile) - { - mpDev->Push(); - mpDev->IntersectClipRegion( aAreaParam.maClipRect ); - } - else - mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) ); - } + if ( !bOutside ) + { + bool bRightAdjusted = false; // to correct text width calculation later + switch (eOutHorJust) + { + case SvxCellHorJustify::Left: + nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ); + break; + case SvxCellHorJustify::Right: + nJustPosX += nAvailWidth - aVars.GetTextSize().Width() - + static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX ); + bRightAdjusted = true; + break; + case SvxCellHorJustify::Center: + nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() + + static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) - + static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2; + break; + default: + { + // added to avoid warnings + } + } - Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation + tools::Long nTestClipHeight = aVars.GetTextSize().Height(); + switch (aVars.GetVerJust()) + { + case SvxCellVerJustify::Top: + case SvxCellVerJustify::Block: + { + tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); + nJustPosY += nTop; + nTestClipHeight += nTop; + } + break; + case SvxCellVerJustify::Bottom: + { + tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); + nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot; + nTestClipHeight += nBot; + } + break; + case SvxCellVerJustify::Center: + { + tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); + tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); + nJustPosY += ( nOutHeight + nTop - + aVars.GetTextSize().Height() - nBot ) / 2; + nTestClipHeight += std::abs( nTop - nBot ); + } + break; + default: + { + // added to avoid warnings + } + } - switch (aVars.GetOrient()) - { - case SvxCellOrientation::Standard: - nJustPosY += aVars.GetAscent(); - break; - case SvxCellOrientation::TopBottom: - nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent(); - break; - case SvxCellOrientation::BottomUp: - nJustPosY += aVars.GetTextSize().Height(); - nJustPosX += aVars.GetAscent(); - break; - default: - { - // added to avoid warnings - } - } + if ( nTestClipHeight > nOutHeight ) + { + // no vertical clipping when printing cells with optimal height, + // except when font size is from conditional formatting. + if ( eType != OUTTYPE_PRINTER || + ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) || + ( aVars.HasCondHeight() ) ) + bVClip = true; + } - // When clipping, the visible part is now completely defined by the alignment, - // there's no more special handling to show the right part of RTL text. + if ( bHClip || bVClip ) + { + // only clip the affected dimension so that not all right-aligned + // columns are cut off when performing a non-proportional resize + if (!bHClip) + { + aAreaParam.maClipRect.SetLeft( nScrX ); + aAreaParam.maClipRect.SetRight( nScrX+nScrW ); + } + if (!bVClip) + { + aAreaParam.maClipRect.SetTop( nScrY ); + aAreaParam.maClipRect.SetBottom( nScrY+nScrH ); + } - Point aDrawTextPos( nJustPosX, nJustPosY ); - if ( bPixelToLogic ) - { - // undo text width adjustment in pixels - if (bRightAdjusted) - aDrawTextPos.AdjustX(aVars.GetTextSize().Width() ); + // aClipRect is not used after SetClipRegion/IntersectClipRegion, + // so it can be modified here + if (bPixelToLogic) + aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect ); - aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos ); + if (bMetaFile) + { + mpDev->Push(); + mpDev->IntersectClipRegion( aAreaParam.maClipRect ); + } + else + mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) ); + } - // redo text width adjustment in logic units - if (bRightAdjusted) - aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) ); - } + Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation - // in Metafiles always use DrawTextArray to ensure that positions are - // recorded (for non-proportional resize): + switch (aVars.GetOrient()) + { + case SvxCellOrientation::Standard: + nJustPosY += aVars.GetAscent(); + break; + case SvxCellOrientation::TopBottom: + nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent(); + break; + case SvxCellOrientation::BottomUp: + nJustPosY += aVars.GetTextSize().Height(); + nJustPosX += aVars.GetAscent(); + break; + default: + { + // added to avoid warnings + } + } - const OUString& aString = aVars.GetString(); - if (!aString.isEmpty()) - { - if (bTaggedPDF) - pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::Paragraph, u"P"_ustr); + // When clipping, the visible part is now completely defined by the alignment, + // there's no more special handling to show the right part of RTL text. - // If the string is clipped, make it shorter for - // better performance since drawing by HarfBuzz is - // quite expensive especially for long string. + Point aDrawTextPos( nJustPosX, nJustPosY ); + if ( bPixelToLogic ) + { + // undo text width adjustment in pixels + if (bRightAdjusted) + aDrawTextPos.AdjustX(aVars.GetTextSize().Width() ); - OUString aShort = aString; + aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos ); - // But never fiddle with numeric values. - // (Which was the cause of tdf#86024). - // The General automatic format output takes - // care of this, or fixed width numbers either fit - // or display as ###. - if (!bCellIsValue) - { - double fVisibleRatio = 1.0; - double fTextWidth = aVars.GetTextSize().Width(); - sal_Int32 nTextLen = aString.getLength(); - if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0) - { - fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth; - if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) - { - // Only show the left-end segment. - sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; - aShort = aShort.copy(0, nShortLen); - } - } - else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0) - { - fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth; - if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) - { - // Only show the right-end segment. - sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; - aShort = aShort.copy(nTextLen-nShortLen); - - // Adjust the text position after shortening of the string. - double fShortWidth = aVars.GetFmtTextWidth(aShort); - double fOffset = fTextWidth - fShortWidth; - aDrawTextPos.Move(fOffset, 0); - } - } - } + // redo text width adjustment in logic units + if (bRightAdjusted) + aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) ); + } - if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY) - { - size_t nLen = aShort.getLength(); - if (aDX.size() < nLen) - aDX.resize(nLen, 0); + // in Metafiles always use DrawTextArray to ensure that positions are + // recorded (for non-proportional resize): - pFmtDevice->GetTextArray(aShort, &aDX); + const OUString& aString = aVars.GetString(); + if (!aString.isEmpty()) + { + if (bTaggedPDF) + pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::Paragraph, u"P"_ustr); - if ( !mpRefDevice->GetConnectMetaFile() || - mpRefDevice->GetOutDevType() == OUTDEV_PRINTER ) - { - double fMul = GetStretch(); - for (size_t i = 0; i < nLen; ++i) - aDX[i] /= fMul; - } + // If the string is clipped, make it shorter for + // better performance since drawing by HarfBuzz is + // quite expensive especially for long string. - mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen); - } - else - { - mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr, - aVars.GetLayoutGlyphs(aShort)); - } - if (bTaggedPDF) - pPDF->EndStructureElement(); - } + OUString aShort = aString; - if ( bHClip || bVClip ) + // But never fiddle with numeric values. + // (Which was the cause of tdf#86024). + // The General automatic format output takes + // care of this, or fixed width numbers either fit + // or display as ###. + if (!bCellIsValue) + { + double fVisibleRatio = 1.0; + double fTextWidth = aVars.GetTextSize().Width(); + sal_Int32 nTextLen = aString.getLength(); + if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0) + { + fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth; + if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) { - if (bMetaFile) - mpDev->Pop(); - else - mpDev->SetClipRegion(); + // Only show the left-end segment. + sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; + aShort = aShort.copy(0, nShortLen); } - - // PDF: whole-cell hyperlink from formula? - bool bHasURL = pPDF && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell(); - if (bHasURL) + } + else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0) + { + fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth; + if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) { - tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() ); - lcl_DoHyperlinkResult(mpDev, aURLRect, aCell); + // Only show the right-end segment. + sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; + aShort = aShort.copy(nTextLen-nShortLen); + + // Adjust the text position after shortening of the string. + double fShortWidth = aVars.GetFmtTextWidth(aShort); + double fOffset = fTextWidth - fShortWidth; + aDrawTextPos.Move(fOffset, 0); } } } - nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign; + + if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY) + { + size_t nLen = aShort.getLength(); + if (aDX.size() < nLen) + aDX.resize(nLen, 0); + + pFmtDevice->GetTextArray(aShort, &aDX); + + if ( !mpRefDevice->GetConnectMetaFile() || + mpRefDevice->GetOutDevType() == OUTDEV_PRINTER ) + { + double fMul = GetStretch(); + for (size_t i = 0; i < nLen; ++i) + aDX[i] /= fMul; + } + + mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen); + } + else + { + mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr, + aVars.GetLayoutGlyphs(aShort)); + } if (bTaggedPDF) + { pPDF->EndStructureElement(); + } + } + + if ( bHClip || bVClip ) + { + if (bMetaFile) + mpDev->Pop(); + else + mpDev->SetClipRegion(); + } + + // PDF: whole-cell hyperlink from formula? + bool bHasURL = pPDF && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell(); + if (bHasURL) + { + tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() ); + lcl_DoHyperlinkResult(mpDev, aURLRect, aCell); } - if (bTaggedPDF) - pPDF->EndStructureElement(); } - nPosY += pRowInfo[nArrY].nHeight; } - if (bTaggedPDF) - pPDF->EndStructureElement(); - - if ( bProgress ) - ScProgress::DeleteInterpretProgress(); } void ScOutputData::SetRefDevice( OutputDevice* pRDev )
