tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / output2.cxx
blob421edaafcc5f313b67c5f87844d2a7bd1bd07b7c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <scitems.hxx>
21 #include <editeng/eeitem.hxx>
23 #include <editeng/adjustitem.hxx>
24 #include <svx/algitem.hxx>
25 #include <editeng/brushitem.hxx>
26 #include <svtools/colorcfg.hxx>
27 #include <editeng/colritem.hxx>
28 #include <editeng/charreliefitem.hxx>
29 #include <editeng/crossedoutitem.hxx>
30 #include <editeng/contouritem.hxx>
31 #include <editeng/editobj.hxx>
32 #include <editeng/editstat.hxx>
33 #include <editeng/emphasismarkitem.hxx>
34 #include <editeng/fhgtitem.hxx>
35 #include <editeng/forbiddenruleitem.hxx>
36 #include <editeng/frmdiritem.hxx>
37 #include <editeng/justifyitem.hxx>
38 #include <svx/rotmodit.hxx>
39 #include <editeng/udlnitem.hxx>
40 #include <editeng/unolingu.hxx>
41 #include <editeng/fontitem.hxx>
42 #include <editeng/postitem.hxx>
43 #include <editeng/shdditem.hxx>
44 #include <editeng/wghtitem.hxx>
45 #include <editeng/wrlmitem.hxx>
46 #include <formula/errorcodes.hxx>
47 #include <svl/numformat.hxx>
48 #include <svl/zforlist.hxx>
49 #include <svl/zformat.hxx>
50 #include <vcl/kernarray.hxx>
51 #include <vcl/svapp.hxx>
52 #include <vcl/metric.hxx>
53 #include <vcl/outdev.hxx>
54 #include <vcl/pdfextoutdevdata.hxx>
55 #include <vcl/settings.hxx>
56 #include <vcl/glyphitem.hxx>
57 #include <vcl/glyphitemcache.hxx>
58 #include <sal/log.hxx>
59 #include <unotools/charclass.hxx>
60 #include <osl/diagnose.h>
62 #include <output.hxx>
63 #include <document.hxx>
64 #include <formulacell.hxx>
65 #include <attrib.hxx>
66 #include <patattr.hxx>
67 #include <cellform.hxx>
68 #include <editutil.hxx>
69 #include <progress.hxx>
70 #include <scmod.hxx>
71 #include <fillinfo.hxx>
72 #include <stlsheet.hxx>
73 #include <spellcheckcontext.hxx>
74 #include <scopetools.hxx>
75 #include <tabvwsh.hxx>
77 #include <com/sun/star/i18n/DirectionProperty.hpp>
78 #include <comphelper/scopeguard.hxx>
79 #include <comphelper/string.hxx>
81 #include <memory>
82 #include <vector>
84 #include <math.h>
86 using namespace com::sun::star;
88 //! Merge Autofilter width with column.cxx
89 #define DROPDOWN_BITMAP_SIZE 18
91 #define DRAWTEXT_MAX 32767
93 const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;
94 constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
96 class ScDrawStringsVars
98 ScOutputData* pOutput; // connection
100 const ScPatternAttr* pPattern; // attribute
101 const SfxItemSet* pCondSet; // from conditional formatting
103 vcl::Font aFont; // created from attributes
104 FontMetric aMetric;
105 tools::Long nAscentPixel; // always pixels
106 SvxCellOrientation eAttrOrient;
107 SvxCellHorJustify eAttrHorJust;
108 SvxCellVerJustify eAttrVerJust;
109 SvxCellJustifyMethod eAttrHorJustMethod;
110 const SvxMarginItem* pMargin;
111 sal_uInt16 nIndent;
112 bool bRotated;
114 OUString aString; // contents
115 Size aTextSize;
116 tools::Long nOriginalWidth;
117 tools::Long nMaxDigitWidth;
118 tools::Long nSignWidth;
119 tools::Long nDotWidth;
120 tools::Long nExpWidth;
122 ScRefCellValue maLastCell;
123 sal_uLong nValueFormat;
124 bool bLineBreak;
125 bool bRepeat;
126 bool bShrink;
128 bool bPixelToLogic;
129 bool bCellContrast;
131 Color aBackConfigColor; // used for ScPatternAttr::GetFont calls
132 Color aTextConfigColor;
133 sal_Int32 nRepeatPos;
134 sal_Unicode nRepeatChar;
136 public:
137 ScDrawStringsVars(ScOutputData* pData, bool bPTL);
139 // SetPattern = ex-SetVars
140 // SetPatternSimple: without Font
142 void SetPattern(
143 const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
144 SvtScriptType nScript );
146 void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );
148 bool SetText( const ScRefCellValue& rCell ); // TRUE -> drop pOldPattern
149 void SetHashText();
150 bool SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth );
151 void SetAutoText( const OUString& rAutoText );
153 SvxCellOrientation GetOrient() const { return eAttrOrient; }
154 SvxCellHorJustify GetHorJust() const { return eAttrHorJust; }
155 SvxCellVerJustify GetVerJust() const { return eAttrVerJust; }
156 SvxCellJustifyMethod GetHorJustMethod() const { return eAttrHorJustMethod; }
157 const SvxMarginItem* GetMargin() const { return pMargin; }
159 sal_uInt16 GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; }
160 sal_uInt16 GetRightTotal() const { return pMargin->GetRightMargin() + nIndent; }
162 const OUString& GetString() const { return aString; }
163 const Size& GetTextSize() const { return aTextSize; }
164 tools::Long GetOriginalWidth() const { return nOriginalWidth; }
165 tools::Long GetFmtTextWidth(const OUString& rString);
167 // Get the effective number format, including formula result types.
168 // This assumes that a formula cell has already been calculated.
169 sal_uLong GetResultValueFormat() const { return nValueFormat;}
171 bool GetLineBreak() const { return bLineBreak; }
172 bool IsRepeat() const { return bRepeat; }
173 bool IsShrink() const { return bShrink; }
174 void RepeatToFill( tools::Long nColWidth );
176 tools::Long GetAscent() const { return nAscentPixel; }
177 bool IsRotated() const { return bRotated; }
179 void SetShrinkScale( tools::Long nScale, SvtScriptType nScript );
181 bool HasCondHeight() const { return pCondSet && SfxItemState::SET ==
182 pCondSet->GetItemState( ATTR_FONT_HEIGHT ); }
184 bool HasEditCharacters() const;
186 // ScOutputData::LayoutStrings() usually triggers a number of calls that require
187 // to lay out the text, which is relatively slow, so cache that operation.
188 const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const
190 return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString);
193 private:
194 tools::Long GetMaxDigitWidth(); // in logic units
195 tools::Long GetSignWidth();
196 tools::Long GetDotWidth();
197 tools::Long GetExpWidth();
198 void TextChanged();
201 ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, bool bPTL) :
202 pOutput ( pData ),
203 pPattern ( nullptr ),
204 pCondSet ( nullptr ),
205 nAscentPixel(0),
206 eAttrOrient ( SvxCellOrientation::Standard ),
207 eAttrHorJust( SvxCellHorJustify::Standard ),
208 eAttrVerJust( SvxCellVerJustify::Bottom ),
209 eAttrHorJustMethod( SvxCellJustifyMethod::Auto ),
210 pMargin ( nullptr ),
211 nIndent ( 0 ),
212 bRotated ( false ),
213 nOriginalWidth( 0 ),
214 nMaxDigitWidth( 0 ),
215 nSignWidth( 0 ),
216 nDotWidth( 0 ),
217 nExpWidth( 0 ),
218 nValueFormat( 0 ),
219 bLineBreak ( false ),
220 bRepeat ( false ),
221 bShrink ( false ),
222 bPixelToLogic( bPTL ),
223 nRepeatPos( -1 ),
224 nRepeatChar( 0x0 )
226 bCellContrast = pOutput->mbUseStyleColor &&
227 Application::GetSettings().GetStyleSettings().GetHighContrastMode();
229 const svtools::ColorConfig& rColorConfig = ScModule::get()->GetColorConfig();
230 aBackConfigColor = rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
231 aTextConfigColor = rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor;
234 void ScDrawStringsVars::SetShrinkScale( tools::Long nScale, SvtScriptType nScript )
236 // text remains valid, size is updated
238 OutputDevice* pDev = pOutput->mpDev;
239 OutputDevice* pRefDevice = pOutput->mpRefDevice;
240 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
242 // call GetFont with a modified fraction, use only the height
244 Fraction aFraction( nScale, 100 );
245 if ( !bPixelToLogic )
246 aFraction *= pOutput->aZoomY;
247 vcl::Font aTmpFont;
248 pPattern->fillFontOnly(aTmpFont, pFmtDevice, &aFraction, pCondSet, nScript);
249 // only need font height
250 tools::Long nNewHeight = aTmpFont.GetFontHeight();
251 if ( nNewHeight > 0 )
252 aFont.SetFontHeight( nNewHeight );
254 // set font and dependent variables as in SetPattern
256 pDev->SetFont( aFont );
257 if ( pFmtDevice != pDev )
258 pFmtDevice->SetFont( aFont );
260 aMetric = pFmtDevice->GetFontMetric();
261 if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
263 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
264 MapMode aOld = pDefaultDev->GetMapMode();
265 pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
266 aMetric = pDefaultDev->GetFontMetric( aFont );
267 pDefaultDev->SetMapMode( aOld );
270 nAscentPixel = aMetric.GetAscent();
271 if ( bPixelToLogic )
272 nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
274 SetAutoText( aString ); // same text again, to get text size
277 namespace {
279 template<typename ItemType, typename EnumType>
280 EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
282 const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet));
283 return static_cast<EnumType>(rItem.GetValue());
286 bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
288 return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet);
293 static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB nTab )
295 sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, nTab );
296 SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
297 return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT;
300 void ScDrawStringsVars::SetPattern(
301 const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
302 SvtScriptType nScript )
304 nMaxDigitWidth = 0;
305 nSignWidth = 0;
306 nDotWidth = 0;
307 nExpWidth = 0;
309 pPattern = pNew;
310 pCondSet = pSet;
312 // evaluate pPattern
314 OutputDevice* pDev = pOutput->mpDev;
315 OutputDevice* pRefDevice = pOutput->mpRefDevice;
316 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
318 // font
320 ScAutoFontColorMode eColorMode;
321 if ( pOutput->mbUseStyleColor )
323 if ( pOutput->mbForceAutoColor )
324 eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreAll : ScAutoFontColorMode::IgnoreFont;
325 else
326 eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreBack : ScAutoFontColorMode::Display;
328 else
329 eColorMode = ScAutoFontColorMode::Print;
331 if (bPixelToLogic)
332 pPattern->fillFont(aFont, eColorMode, pFmtDevice, nullptr, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor);
333 else
334 pPattern->fillFont(aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor );
336 aFont.SetAlignment(ALIGN_BASELINE);
338 // orientation
340 eAttrOrient = pPattern->GetCellOrientation( pCondSet );
342 // alignment
344 eAttrHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
346 eAttrVerJust = pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet ).GetValue();
347 if ( eAttrVerJust == SvxCellVerJustify::Standard )
348 eAttrVerJust = SvxCellVerJustify::Bottom;
350 // justification method
352 eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet);
354 // line break
356 bLineBreak = pPattern->GetItem( ATTR_LINEBREAK, pCondSet ).GetValue();
358 // handle "repeat" alignment
360 bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat );
361 if ( bRepeat )
363 // "repeat" disables rotation (before constructing the font)
364 eAttrOrient = SvxCellOrientation::Standard;
366 // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
367 if ( bLineBreak )
368 eAttrHorJust = SvxCellHorJustify::Standard;
371 sal_Int16 nRot;
372 switch (eAttrOrient)
374 case SvxCellOrientation::Standard:
375 nRot = 0;
376 bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 &&
377 !bRepeat;
378 break;
379 case SvxCellOrientation::Stacked:
380 nRot = 0;
381 bRotated = false;
382 break;
383 case SvxCellOrientation::TopBottom:
384 nRot = 2700;
385 bRotated = false;
386 break;
387 case SvxCellOrientation::BottomUp:
388 nRot = 900;
389 bRotated = false;
390 break;
391 default:
392 OSL_FAIL("Invalid SvxCellOrientation value");
393 nRot = 0;
394 bRotated = false;
395 break;
397 aFont.SetOrientation( Degree10(nRot) );
399 // syntax mode
401 if (pOutput->mbSyntaxMode)
402 pOutput->SetSyntaxColor(&aFont, rCell);
404 // There is no cell attribute for kerning, default is kerning OFF, all
405 // kerning is stored at an EditText object that is drawn using EditEngine.
406 // See also matching kerning cases in ScColumn::GetNeededSize and
407 // ScColumn::GetOptimalColWidth.
408 aFont.SetKerning(FontKerning::NONE);
410 pDev->SetFont( aFont );
411 if ( pFmtDevice != pDev )
412 pFmtDevice->SetFont( aFont );
414 aMetric = pFmtDevice->GetFontMetric();
416 // if there is the leading 0 on a printer device, we have problems
417 // -> take metric from the screen (as for EditEngine!)
418 if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
420 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
421 MapMode aOld = pDefaultDev->GetMapMode();
422 pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
423 aMetric = pDefaultDev->GetFontMetric( aFont );
424 pDefaultDev->SetMapMode( aOld );
427 nAscentPixel = aMetric.GetAscent();
428 if ( bPixelToLogic )
429 nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
431 Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() );
432 pDev->SetTextLineColor( aULineColor );
434 Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() );
435 pDev->SetOverlineColor( aOLineColor );
437 // number format
439 nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
441 // margins
442 pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
443 if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right )
444 nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
445 else
446 nIndent = 0;
448 // "Shrink to fit"
450 bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
452 // at least the text size needs to be retrieved again
453 //! differentiate and do not get the text again from the number format?
454 maLastCell.clear();
457 void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet )
459 nMaxDigitWidth = 0;
460 nSignWidth = 0;
461 nDotWidth = 0;
462 nExpWidth = 0;
464 // Is called, when the font variables do not change (!StringDiffer)
466 pPattern = pNew;
467 pCondSet = pSet; //! is this needed ???
469 // number format
471 sal_uLong nOld = nValueFormat;
472 nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
474 if (nValueFormat != nOld)
475 maLastCell.clear(); // always reformat
477 // margins
479 pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
481 if ( eAttrHorJust == SvxCellHorJustify::Left )
482 nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
483 else
484 nIndent = 0;
486 // "Shrink to fit"
488 bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
491 static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell )
493 return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE &&
494 rCell.getDouble() == rOldCell.getDouble();
497 bool ScDrawStringsVars::SetText( const ScRefCellValue& rCell )
499 bool bChanged = false;
501 if (!rCell.isEmpty())
503 if (!SameValue(rCell, maLastCell))
505 maLastCell = rCell; // store cell
507 const Color* pColor;
508 sal_uLong nFormat = nValueFormat;
509 aString = ScCellFormat::GetString( rCell,
510 nFormat, &pColor,
511 nullptr,
512 *pOutput->mpDoc,
513 pOutput->mbShowNullValues,
514 pOutput->mbShowFormulas,
515 true );
516 if ( nFormat )
518 nRepeatPos = aString.indexOf( 0x1B );
519 if ( nRepeatPos != -1 )
521 if (nRepeatPos + 1 == aString.getLength())
522 nRepeatPos = -1;
523 else
525 nRepeatChar = aString[ nRepeatPos + 1 ];
526 // delete placeholder and char to repeat
527 aString = aString.replaceAt( nRepeatPos, 2, u"" );
528 // Do not cache/reuse a repeat-filled string, column
529 // widths or fonts or sizes may differ.
530 maLastCell.clear();
534 else
536 nRepeatPos = -1;
537 nRepeatChar = 0x0;
539 if (aString.getLength() > DRAWTEXT_MAX)
540 aString = aString.copy(0, DRAWTEXT_MAX);
542 if ( pColor && !pOutput->mbSyntaxMode && !( pOutput->mbUseStyleColor && pOutput->mbForceAutoColor ) )
544 OutputDevice* pDev = pOutput->mpDev;
545 aFont.SetColor(*pColor);
546 pDev->SetFont( aFont ); // only for output
547 bChanged = true;
548 maLastCell.clear(); // next time return here again
551 TextChanged();
553 // otherwise keep string/size
555 else
557 aString.clear();
558 maLastCell.clear();
559 aTextSize = Size(0,0);
560 nOriginalWidth = 0;
563 return bChanged;
566 void ScDrawStringsVars::SetHashText()
568 SetAutoText(u"###"_ustr);
571 void ScDrawStringsVars::RepeatToFill( tools::Long nColWidth )
573 if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() )
574 return;
576 // Measuring a string containing a single copy of the repeat char is inaccurate.
577 // To increase accuracy, start with a representative sample of a padding sequence.
578 constexpr sal_Int32 nSampleSize = 20;
579 OUStringBuffer aFill(nSampleSize);
580 comphelper::string::padToLength(aFill, nSampleSize, nRepeatChar);
582 tools::Long nSampleWidth = GetFmtTextWidth(aFill.makeStringAndClear());
583 double nAvgCharWidth = static_cast<double>(nSampleWidth) / static_cast<double>(nSampleSize);
585 // Intentionally truncate to round toward zero
586 auto nCharWidth = static_cast<tools::Long>(nAvgCharWidth);
587 if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) )
588 return;
590 // Are there restrictions on the cell type we should filter out here ?
591 tools::Long nTextWidth = aTextSize.Width();
592 if ( bPixelToLogic )
594 nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
595 nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
598 tools::Long nSpaceToFill = nColWidth - nTextWidth;
599 if ( nSpaceToFill <= nCharWidth )
600 return;
602 // Intentionally truncate to round toward zero
603 auto nCharsToInsert = static_cast<sal_Int32>(static_cast<double>(nSpaceToFill) / nAvgCharWidth);
604 aFill.ensureCapacity(nCharsToInsert);
605 comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
606 aString = aString.replaceAt( nRepeatPos, 0, aFill );
607 TextChanged();
610 bool ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth )
612 // #i113045# do the single-character width calculations in logic units
613 if (bPixelToLogic)
614 nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
616 CellType eType = rCell.getType();
617 if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA)
618 // must be a value or formula cell.
619 return false;
621 if (eType == CELLTYPE_FORMULA)
623 ScFormulaCell* pFCell = rCell.getFormula();
624 if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
626 SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
627 return true;
629 // If it's formula, the result must be a value.
630 if (!pFCell->IsValue())
631 return false;
634 sal_uLong nFormat = GetResultValueFormat();
635 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
637 // Not 'General' number format. Set hash text and bail out.
638 SetHashText();
639 return true;
642 double fVal = rCell.getValue();
644 const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat);
645 if (!pNumFormat)
646 return false;
648 tools::Long nMaxDigit = GetMaxDigitWidth();
649 if (!nMaxDigit)
650 return false;
652 sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
654 OUString sTempOut(aString);
655 if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut, pOutput->mpDoc->GetFormatTable()->GetNatNum()))
657 aString = sTempOut;
658 // Failed to get output string. Bail out.
659 return false;
661 aString = sTempOut;
663 sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
664 sal_Int32 nLen = aString.getLength();
665 sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0];
666 for( sal_Int32 i = 0; i < nLen; ++i )
668 sal_Unicode c = aString[i];
669 if (c == '-')
670 ++nSignCount;
671 else if (c == cDecSep)
672 ++nDecimalCount;
673 else if (c == 'E')
674 ++nExpCount;
677 // #i112250# A small value might be formatted as "0" when only counting the digits,
678 // but fit into the column when considering the smaller width of the decimal separator.
679 if (aString == "0" && fVal != 0.0)
680 nDecimalCount = 1;
682 if (nDecimalCount)
683 nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
684 if (nSignCount)
685 nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
686 if (nExpCount)
687 nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
689 if (nDecimalCount || nSignCount || nExpCount)
691 // Re-calculate.
692 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
693 OUString sTempOut(aString);
694 if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut, pOutput->mpDoc->GetFormatTable()->GetNatNum()))
696 aString = sTempOut;
697 // Failed to get output string. Bail out.
698 return false;
700 aString = sTempOut;
703 tools::Long nActualTextWidth = GetFmtTextWidth(aString);
704 if (nActualTextWidth > nWidth)
706 // Even after the decimal adjustment the text doesn't fit. Give up.
707 SetHashText();
708 return true;
711 TextChanged();
712 maLastCell.clear(); // #i113022# equal cell and format in another column may give different string
713 return false;
716 void ScDrawStringsVars::SetAutoText( const OUString& rAutoText )
718 aString = rAutoText;
720 OutputDevice* pRefDevice = pOutput->mpRefDevice;
721 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
722 aTextSize.setWidth( GetFmtTextWidth( aString ) );
723 aTextSize.setHeight( pFmtDevice->GetTextHeight() );
725 if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
727 double fMul = pOutput->GetStretch();
728 aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
731 aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
732 if ( GetOrient() != SvxCellOrientation::Standard )
734 tools::Long nTemp = aTextSize.Height();
735 aTextSize.setHeight( aTextSize.Width() );
736 aTextSize.setWidth( nTemp );
739 nOriginalWidth = aTextSize.Width();
740 if ( bPixelToLogic )
741 aTextSize = pRefDevice->LogicToPixel( aTextSize );
743 maLastCell.clear(); // the same text may fit in the next cell
746 tools::Long ScDrawStringsVars::GetMaxDigitWidth()
748 if (nMaxDigitWidth > 0)
749 return nMaxDigitWidth;
751 for (char i = 0; i < 10; ++i)
753 char cDigit = '0' + i;
754 // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
755 tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
756 nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
758 return nMaxDigitWidth;
761 tools::Long ScDrawStringsVars::GetSignWidth()
763 if (nSignWidth > 0)
764 return nSignWidth;
766 nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-'));
767 return nSignWidth;
770 tools::Long ScDrawStringsVars::GetDotWidth()
772 if (nDotWidth > 0)
773 return nDotWidth;
775 const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator;
776 nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
777 return nDotWidth;
780 tools::Long ScDrawStringsVars::GetExpWidth()
782 if (nExpWidth > 0)
783 return nExpWidth;
785 nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E'));
786 return nExpWidth;
789 tools::Long ScDrawStringsVars::GetFmtTextWidth( const OUString& rString )
791 return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString ));
794 void ScDrawStringsVars::TextChanged()
796 OutputDevice* pRefDevice = pOutput->mpRefDevice;
797 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
798 aTextSize.setWidth( GetFmtTextWidth( aString ) );
799 aTextSize.setHeight( pFmtDevice->GetTextHeight() );
801 if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
803 double fMul = pOutput->GetStretch();
804 aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
807 aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
808 if ( GetOrient() != SvxCellOrientation::Standard )
810 tools::Long nTemp = aTextSize.Height();
811 aTextSize.setHeight( aTextSize.Width() );
812 aTextSize.setWidth( nTemp );
815 nOriginalWidth = aTextSize.Width();
816 if ( bPixelToLogic )
817 aTextSize = pRefDevice->LogicToPixel( aTextSize );
820 bool ScDrawStringsVars::HasEditCharacters() const
822 for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
824 switch(aString[nIdx])
826 case CHAR_NBSP:
827 // tdf#122676: Ignore CHAR_NBSP (this is thousand separator in any number)
828 // if repeat character is set
829 if (nRepeatPos < 0)
830 return true;
831 break;
832 case CHAR_SHY:
833 case CHAR_ZWSP:
834 case CHAR_LRM:
835 case CHAR_RLM:
836 case CHAR_NBHY:
837 case CHAR_WJ:
838 return true;
839 default:
840 break;
844 return false;
847 double ScOutputData::GetStretch() const
849 if ( mpRefDevice->IsMapModeEnabled() )
851 // If a non-trivial MapMode is set, its scale is now already
852 // taken into account in the OutputDevice's font handling
853 // (OutputDevice::ImplNewFont, see #95414#).
854 // The old handling below is only needed for pixel output.
855 return 1.0;
858 // calculation in double is faster than Fraction multiplication
859 // and doesn't overflow
861 if ( mpRefDevice == pFmtDevice )
863 MapMode aOld = mpRefDevice->GetMapMode();
864 return static_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
866 else
868 // when formatting for printer, device map mode has already been taken care of
869 return static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
873 // output strings
875 static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, ScRefCellValue& rCell )
877 vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
879 OUString aURL;
880 OUString aCellText;
881 if (rCell.getType() == CELLTYPE_FORMULA)
883 ScFormulaCell* pFCell = rCell.getFormula();
884 if ( pFCell->IsHyperLinkCell() )
885 pFCell->GetURLResult( aURL, aCellText );
888 if ( !aURL.isEmpty() && pPDFData )
890 vcl::PDFExtOutDevBookmarkEntry aBookmark;
891 aBookmark.nLinkId = pPDFData->CreateLink(rRect, aCellText);
892 aBookmark.aBookmark = aURL;
893 std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
894 rBookmarks.push_back( aBookmark );
898 void ScOutputData::SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell )
900 switch (rCell.getType())
902 case CELLTYPE_VALUE:
903 pFont->SetColor(*mxValueColor);
904 break;
905 case CELLTYPE_STRING:
906 pFont->SetColor(*mxTextColor);
907 break;
908 case CELLTYPE_FORMULA:
909 pFont->SetColor(*mxFormulaColor);
910 break;
911 default:
913 // added to avoid warnings
918 static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
920 ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
921 SfxItemSet aSet( rEngine.GetEmptyItemSet() );
922 aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
923 rEngine.QuickSetAttribs( aSet, aSel );
924 // function is called with update mode set to FALSE
927 void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell )
929 Color aColor;
930 switch (rCell.getType())
932 case CELLTYPE_VALUE:
933 aColor = *mxValueColor;
934 break;
935 case CELLTYPE_STRING:
936 case CELLTYPE_EDIT:
937 aColor = *mxTextColor;
938 break;
939 case CELLTYPE_FORMULA:
940 aColor = *mxFormulaColor;
941 break;
942 default:
944 // added to avoid warnings
947 lcl_SetEditColor( rEngine, aColor );
950 bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
951 SCCOL& rOverX, SCROW& rOverY,
952 bool bVisRowChanged )
954 bool bDoMerge = false;
955 bool bIsLeft = ( nX == nVisX1 );
956 bool bIsTop = ( nY == nVisY1 ) || bVisRowChanged;
958 bool bHOver;
959 bool bVOver;
960 bool bHidden;
962 if (!mpDoc->ColHidden(nX, nTab) && nX >= nX1 && nX <= nX2
963 && !mpDoc->RowHidden(nY, nTab) && nY >= nY1 && nY <= nY2)
965 ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
966 bHOver = pInfo->bHOverlapped;
967 bVOver = pInfo->bVOverlapped;
969 else
971 ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG)->GetValue();
972 bHOver = bool(nOverlap2 & ScMF::Hor);
973 bVOver = bool(nOverlap2 & ScMF::Ver);
976 if ( bHOver && bVOver )
977 bDoMerge = bIsLeft && bIsTop;
978 else if ( bHOver )
979 bDoMerge = bIsLeft;
980 else if ( bVOver )
981 bDoMerge = bIsTop;
983 rOverX = nX;
984 rOverY = nY;
986 while (bHOver) // nY constant
988 --rOverX;
989 bHidden = mpDoc->ColHidden(rOverX, nTab);
990 if ( !bDoMerge && !bHidden )
991 return false;
993 if (rOverX >= nX1 && !bHidden)
995 bHOver = pRowInfo[nArrY].cellInfo(rOverX).bHOverlapped;
996 bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
998 else
1000 ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, nTab, ATTR_MERGE_FLAG)->GetValue();
1001 bHOver = bool(nOverlap & ScMF::Hor);
1002 bVOver = bool(nOverlap & ScMF::Ver);
1006 while (bVOver)
1008 --rOverY;
1009 bHidden = mpDoc->RowHidden(rOverY, nTab);
1010 if ( !bDoMerge && !bHidden )
1011 return false;
1013 if (nArrY>0)
1014 --nArrY; // local copy !
1016 if (rOverX >= nX1 && rOverY >= nY1 &&
1017 !mpDoc->ColHidden(rOverX, nTab) &&
1018 !mpDoc->RowHidden(rOverY, nTab) &&
1019 pRowInfo[nArrY].nRowNo == rOverY)
1021 bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
1023 else
1025 ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, nTab, ATTR_MERGE_FLAG )->GetValue();
1026 bVOver = bool(nOverlap & ScMF::Ver);
1030 return true;
1033 static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern )
1035 assert(pNewPattern && "pNewPattern");
1037 if ( ScPatternAttr::areSame( pNewPattern, rpOldPattern ) )
1038 return false;
1039 else if ( !rpOldPattern )
1040 return true;
1041 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT ), rpOldPattern->GetItem( ATTR_FONT ) ) )
1042 return true;
1043 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT ), rpOldPattern->GetItem( ATTR_CJK_FONT ) ) )
1044 return true;
1045 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT ), rpOldPattern->GetItem( ATTR_CTL_FONT ) ) )
1046 return true;
1047 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) ) )
1048 return true;
1049 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) ) )
1050 return true;
1051 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) ) )
1052 return true;
1053 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) ) )
1054 return true;
1055 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) ) )
1056 return true;
1057 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) ) )
1058 return true;
1059 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_FONT_POSTURE ) ) )
1060 return true;
1061 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) ) )
1062 return true;
1063 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) ) )
1064 return true;
1065 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_UNDERLINE ), rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) ) )
1066 return true;
1067 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_OVERLINE ), rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) ) )
1068 return true;
1069 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WORDLINE ), rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) ) )
1070 return true;
1071 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ), rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) ) )
1072 return true;
1073 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CONTOUR ), rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) ) )
1074 return true;
1075 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_SHADOWED ), rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) ) )
1076 return true;
1077 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_COLOR ), rpOldPattern->GetItem( ATTR_FONT_COLOR ) ) )
1078 return true;
1079 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) ) )
1080 return true;
1081 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) ) )
1082 return true;
1083 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) ) )
1084 return true;
1085 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) ) )
1086 return true;
1087 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_STACKED ), rpOldPattern->GetItem( ATTR_STACKED ) ) )
1088 return true;
1089 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_LINEBREAK ), rpOldPattern->GetItem( ATTR_LINEBREAK ) ) )
1090 return true;
1091 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_MARGIN ), rpOldPattern->GetItem( ATTR_MARGIN ) ) )
1092 return true;
1093 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_ROTATE_VALUE ), rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) ) )
1094 return true;
1095 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ), rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) ) )
1096 return true;
1097 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ), rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) ) )
1098 return true;
1099 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_RELIEF ), rpOldPattern->GetItem( ATTR_FONT_RELIEF ) ) )
1100 return true;
1101 else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_BACKGROUND ), rpOldPattern->GetItem( ATTR_BACKGROUND ) ) )
1102 return true; // needed with automatic text color
1103 else
1105 rpOldPattern = pNewPattern;
1106 return false;
1110 static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc,
1111 const ScFormulaCell* pFCell )
1113 if ( !bProgress && pFCell->GetDirty() )
1115 ScProgress::CreateInterpretProgress( pDoc );
1116 bProgress = true;
1120 static bool IsAmbiguousScript( SvtScriptType nScript )
1122 return ( nScript != SvtScriptType::LATIN &&
1123 nScript != SvtScriptType::ASIAN &&
1124 nScript != SvtScriptType::COMPLEX );
1127 bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
1129 // pThisRowInfo may be NULL
1131 bool bEmpty;
1132 if ( pThisRowInfo && nX <= nX2 )
1133 bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1134 else
1136 ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1137 bEmpty = aCell.isEmpty();
1140 if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
1142 // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
1143 // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
1145 bool bIsPrint = ( eType == OUTTYPE_PRINTER );
1147 if ( bIsPrint || bTabProtected )
1149 const ScProtectionAttr* pAttr =
1150 mpDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
1151 if ( bIsPrint && pAttr->GetHidePrint() )
1152 bEmpty = true;
1153 else if ( bTabProtected )
1155 if ( pAttr->GetHideCell() )
1156 bEmpty = true;
1157 else if ( mbShowFormulas && pAttr->GetHideFormula() )
1159 if (mpDoc->GetCellType(ScAddress(nX, nY, nTab)) == CELLTYPE_FORMULA)
1160 bEmpty = true;
1165 return bEmpty;
1168 void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScRefCellValue& rCell )
1170 rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP));
1171 if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow))
1172 rCell.clear();
1175 bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
1177 // apply the same logic here as in DrawStrings/DrawEdit:
1178 // Stop at non-empty or merged or overlapped cell,
1179 // where a note is empty as well as a cell that's hidden by protection settings
1181 ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1182 if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY))
1183 return false;
1185 const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, nTab );
1186 return !(pPattern->GetItem(ATTR_MERGE).IsMerged() ||
1187 pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped());
1190 // nX, nArrY: loop variables from DrawStrings / DrawEdit
1191 // nPosX, nPosY: corresponding positions for nX, nArrY
1192 // nCellX, nCellY: position of the cell that contains the text
1193 // nNeeded: Text width, including margin
1194 // rPattern: cell format at nCellX, nCellY
1195 // nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings
1196 // bCellIsValue: if set, don't extend into empty cells
1197 // bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
1198 // bOverwrite: if set, also extend into non-empty cells (for rotated text)
1199 // rParam output: various area parameters.
1201 void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY,
1202 SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
1203 const ScPatternAttr& rPattern,
1204 sal_uInt16 nHorJustify, bool bCellIsValue,
1205 bool bBreak, bool bOverwrite,
1206 OutputAreaParam& rParam )
1208 // rThisRowInfo may be for a different row than nCellY, is still used for clip marks
1209 RowInfo& rThisRowInfo = pRowInfo[nArrY];
1211 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1213 tools::Long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX
1214 SCCOL nCompCol = nX;
1215 while ( nCellX > nCompCol )
1217 //! extra member function for width?
1218 tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1219 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1220 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1221 nCellPosX += nColWidth * nLayoutSign;
1222 ++nCompCol;
1224 while ( nCellX < nCompCol )
1226 --nCompCol;
1227 tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1228 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1229 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1230 nCellPosX -= nColWidth * nLayoutSign;
1233 tools::Long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY
1234 SCSIZE nCompArr = nArrY;
1235 SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
1236 while ( nCellY > nCompRow )
1238 if ( nCompArr + 1 < nArrCount )
1240 nCellPosY += pRowInfo[nCompArr].nHeight;
1241 ++nCompArr;
1242 nCompRow = pRowInfo[nCompArr].nRowNo;
1244 else
1246 sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, nTab );
1247 if ( nDocHeight )
1248 nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY );
1249 ++nCompRow;
1252 nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, mnPPTY );
1254 const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE );
1255 bool bMerged = pMerge->IsMerged();
1256 tools::Long nMergeCols = pMerge->GetColMerge();
1257 if ( nMergeCols == 0 )
1258 nMergeCols = 1;
1259 tools::Long nMergeRows = pMerge->GetRowMerge();
1260 if ( nMergeRows == 0 )
1261 nMergeRows = 1;
1263 tools::Long nMergeSizeX = 0;
1264 for ( tools::Long i=0; i<nMergeCols; i++ )
1266 tools::Long nColWidth = ( nCellX+i <= nX2 ) ?
1267 pRowInfo[0].basicCellInfo(nCellX+i).nWidth :
1268 static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * mnPPTX );
1269 nMergeSizeX += nColWidth;
1271 tools::Long nMergeSizeY = 0;
1272 short nDirect = 0;
1273 if ( rThisRowInfo.nRowNo == nCellY )
1275 // take first row's height from row info
1276 nMergeSizeY += rThisRowInfo.nHeight;
1277 nDirect = 1; // skip in loop
1279 // following rows always from document
1280 nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, mnPPTY);
1282 --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines)
1284 rParam.mnColWidth = nMergeSizeX; // store the actual column width.
1285 rParam.mnLeftClipLength = rParam.mnRightClipLength = 0;
1287 // construct the rectangles using logical left/right values (justify is called at the end)
1289 // rAlignRect is the single cell or merged area, used for alignment.
1291 rParam.maAlignRect.SetLeft( nCellPosX );
1292 rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign );
1293 rParam.maAlignRect.SetTop( nCellPosY );
1294 rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 );
1296 // rClipRect is all cells that are used for output.
1297 // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.
1299 rParam.maClipRect = rParam.maAlignRect;
1300 if ( nNeeded > nMergeSizeX )
1302 SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify);
1304 tools::Long nMissing = nNeeded - nMergeSizeX;
1305 tools::Long nLeftMissing = 0;
1306 tools::Long nRightMissing = 0;
1307 switch ( eHorJust )
1309 case SvxCellHorJustify::Left:
1310 nRightMissing = nMissing;
1311 break;
1312 case SvxCellHorJustify::Right:
1313 nLeftMissing = nMissing;
1314 break;
1315 case SvxCellHorJustify::Center:
1316 nLeftMissing = nMissing / 2;
1317 nRightMissing = nMissing - nLeftMissing;
1318 break;
1319 default:
1321 // added to avoid warnings
1325 // nLeftMissing, nRightMissing are logical, eHorJust values are visual
1326 if ( bLayoutRTL )
1327 ::std::swap( nLeftMissing, nRightMissing );
1329 SCCOL nRightX = nCellX;
1330 SCCOL nLeftX = nCellX;
1331 if ( !bMerged && !bCellIsValue && !bBreak )
1333 // look for empty cells into which the text can be extended
1335 while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
1337 ++nRightX;
1338 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, nTab ) * mnPPTX );
1339 nRightMissing -= nAdd;
1340 rParam.maClipRect.AdjustRight(nAdd * nLayoutSign );
1342 if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
1343 rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true;
1346 while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
1348 if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
1349 rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true;
1351 --nLeftX;
1352 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, nTab ) * mnPPTX );
1353 nLeftMissing -= nAdd;
1354 rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) );
1358 // Set flag and reserve space for clipping mark triangle,
1359 // even if rThisRowInfo isn't for nCellY (merged cells).
1360 if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
1362 rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right;
1363 bAnyClipped = true;
1364 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1365 rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
1367 if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
1369 rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left;
1370 bAnyClipped = true;
1371 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1372 rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign );
1375 rParam.mbLeftClip = ( nLeftMissing > 0 );
1376 rParam.mbRightClip = ( nRightMissing > 0 );
1377 rParam.mnLeftClipLength = nLeftMissing;
1378 rParam.mnRightClipLength = nRightMissing;
1380 else
1382 rParam.mbLeftClip = rParam.mbRightClip = false;
1384 // leave space for AutoFilter on screen
1385 // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)
1387 if ( eType==OUTTYPE_WINDOW &&
1388 ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) &&
1389 ( !bBreak || mpRefDevice == pFmtDevice ) )
1391 // filter drop-down width depends on row height
1392 double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
1393 fZoom = fZoom > 1.0 ? fZoom : 1.0;
1394 const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE;
1395 bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
1396 if ( bFit )
1398 // content fits even in the remaining area without the filter button
1399 // -> align within that remaining area
1401 rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) );
1402 rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) );
1407 // justify both rectangles for alignment calculation, use with DrawText etc.
1409 rParam.maAlignRect.Normalize();
1410 rParam.maClipRect.Normalize();
1413 namespace {
1415 bool beginsWithRTLCharacter(const OUString& rStr)
1417 if (rStr.isEmpty())
1418 return false;
1420 switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
1422 case i18n::DirectionProperty_RIGHT_TO_LEFT:
1423 case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC:
1424 case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING:
1425 case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE:
1426 return true;
1427 default:
1431 return false;
1436 /** Get left, right or centered alignment from RTL context.
1438 Does not return standard, block or repeat, for these the contextual left or
1439 right alignment is returned.
1441 static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust,
1442 bool bCellIsValue, const OUString& rText,
1443 const ScPatternAttr& rPattern, const SfxItemSet* pCondSet,
1444 const ScDocument* pDoc, SCTAB nTab, const bool bNumberFormatIsText )
1446 SvxCellHorJustify eHorJustContext = eInHorJust;
1447 bool bUseWritingDirection = false;
1448 if (eInHorJust == SvxCellHorJustify::Standard)
1450 // fdo#32530: Default alignment depends on value vs
1451 // string, and the direction of the 1st letter.
1452 if (beginsWithRTLCharacter( rText)) //If language is RTL
1454 if (bCellIsValue)
1455 eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1456 else
1457 eHorJustContext = SvxCellHorJustify::Right;
1459 else if (bCellIsValue) //If language is not RTL
1460 eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right;
1461 else
1462 bUseWritingDirection = true;
1465 if (bUseWritingDirection ||
1466 eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
1468 SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet);
1469 if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
1470 eHorJustContext = SvxCellHorJustify::Left;
1471 else if (nDirection == SvxFrameDirection::Environment)
1473 SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL");
1474 // fdo#73588: The content of the cell must also
1475 // begin with a RTL character to be right
1476 // aligned; otherwise, it should be left aligned.
1477 eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1479 else
1480 eHorJustContext = SvxCellHorJustify::Right;
1482 return eHorJustContext;
1485 void ScOutputData::DrawStrings( bool bPixelToLogic )
1487 LayoutStrings(bPixelToLogic);
1490 void ScOutputData::LayoutStrings(bool bPixelToLogic)
1492 vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1493 bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1494 if (bTaggedPDF)
1496 bool bReopenTag = ReopenPDFStructureElement(vcl::PDFWriter::Table);
1497 if (!bReopenTag)
1499 sal_Int32 nId = pPDF->EnsureStructureElement(nullptr);
1500 pPDF->InitStructureElement(nId, vcl::PDFWriter::Table, u"Table"_ustr);
1501 pPDF->BeginStructureElement(nId);
1502 pPDF->GetScPDFState()->m_TableId = nId;
1506 bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings();
1507 mpDoc->SetLayoutStrings(true);
1508 OSL_ENSURE( mpDev == mpRefDevice ||
1509 mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(),
1510 "LayoutStrings: different MapUnits ?!?!" );
1511 vcl::text::ComplexTextLayoutFlags eTextLayout = mpDev->GetLayoutMode();
1512 mpDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
1514 comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings, eTextLayout] {
1515 mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings);
1516 mpDev->SetLayoutMode(eTextLayout);
1519 sc::IdleSwitch aIdleSwitch(*mpDoc, false);
1521 // Try to limit interpreting to only visible cells. Calling e.g. IsValue()
1522 // on a formula cell that needs interpreting would call Interpret()
1523 // for the entire formula group, which could be large.
1524 mpDoc->InterpretCellsIfNeeded( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ));
1526 ScDrawStringsVars aVars( this, bPixelToLogic );
1528 bool bProgress = false;
1530 tools::Long nInitPosX = nScrX;
1531 if ( bLayoutRTL )
1532 nInitPosX += nMirrorW - 1; // pixels
1533 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1535 SCCOL nLastContentCol = mpDoc->MaxCol();
1536 if ( nX2 < mpDoc->MaxCol() )
1538 SCROW nEndRow;
1539 mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
1542 SCCOL nLoopStartX = nX1;
1543 if ( nX1 > 0 && !bTaggedPDF )
1544 --nLoopStartX; // start before nX1 for rest of long text to the left
1546 // variables for GetOutputArea
1547 OutputAreaParam aAreaParam;
1548 bool bCellIsValue = false;
1549 tools::Long nNeededWidth = 0;
1550 const ScPatternAttr* pPattern = nullptr;
1551 const SfxItemSet* pCondSet = nullptr;
1552 const ScPatternAttr* pOldPattern = nullptr;
1553 const SfxItemSet* pOldCondSet = nullptr;
1554 SvtScriptType nOldScript = SvtScriptType::NONE;
1556 // alternative pattern instances in case we need to modify the pattern
1557 // before processing the cell value.
1558 std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
1560 KernArray aDX;
1561 tools::Long nPosY = nScrY;
1562 for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
1564 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1565 SCROW nY = pThisRowInfo->nRowNo;
1566 if (pThisRowInfo->bChanged)
1568 if (bTaggedPDF)
1570 bool bReopenTag = false;
1571 if (nLoopStartX != 0)
1573 bReopenTag
1574 = ReopenPDFStructureElement(vcl::PDFWriter::TableRow, nY);
1576 if (!bReopenTag)
1578 sal_Int32 nId = pPDF->EnsureStructureElement(nullptr);
1579 pPDF->InitStructureElement(nId, vcl::PDFWriter::TableRow, u"TR"_ustr);
1580 pPDF->BeginStructureElement(nId);
1581 pPDF->GetScPDFState()->m_TableRowMap.emplace(nY, nId);
1585 tools::Long nPosX = nInitPosX;
1586 if ( nLoopStartX < nX1 )
1587 nPosX -= pRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign;
1588 for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
1590 if (bTaggedPDF)
1591 pPDF->WrapBeginStructureElement(vcl::PDFWriter::TableData, u"TD"_ustr);
1593 bool bMergeEmpty = false;
1594 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
1595 bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1597 SCCOL nCellX = nX; // position where the cell really starts
1598 SCROW nCellY = nY;
1599 bool bDoCell = false;
1600 bool bUseEditEngine = false;
1602 // Part of a merged cell?
1604 bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped);
1605 if ( bOverlapped )
1607 bEmpty = true;
1609 SCCOL nOverX; // start of the merged cells
1610 SCROW nOverY;
1611 bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
1612 if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
1614 nCellX = nOverX;
1615 nCellY = nOverY;
1616 bDoCell = true;
1618 else
1619 bMergeEmpty = true;
1622 // Rest of a long text further to the left?
1624 if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
1626 SCCOL nTempX=nX1;
1627 while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1628 --nTempX;
1630 if ( nTempX < nX1 &&
1631 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1632 !mpDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1634 nCellX = nTempX;
1635 bDoCell = true;
1639 // Rest of a long text further to the right?
1641 if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
1643 // don't have to look further than nLastContentCol
1645 SCCOL nTempX=nX;
1646 while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1647 ++nTempX;
1649 if ( nTempX > nX &&
1650 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1651 !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1653 nCellX = nTempX;
1654 bDoCell = true;
1658 // normal visible cell
1660 if (!bEmpty)
1661 bDoCell = true;
1663 // don't output the cell that's being edited
1665 if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
1666 bDoCell = false;
1668 // skip text in cell if data bar/icon set is set and only value selected
1669 if ( bDoCell )
1671 if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
1672 bDoCell = false;
1673 if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
1674 bDoCell = false;
1677 // output the cell text
1679 ScRefCellValue aCell;
1680 if (bDoCell)
1682 if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
1683 aCell = pThisRowInfo->cellInfo(nCellX).maCell;
1684 else
1685 GetVisibleCell( nCellX, nCellY, nTab, aCell ); // get from document
1686 if (aCell.isEmpty())
1687 bDoCell = false;
1688 else if (aCell.getType() == CELLTYPE_EDIT)
1689 bUseEditEngine = true;
1692 // Check if this cell is mis-spelled.
1693 if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
1695 if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
1696 bUseEditEngine = true;
1699 if (bDoCell && !bUseEditEngine)
1701 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1703 ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
1704 pPattern = rCellInfo.pPatternAttr;
1705 pCondSet = rCellInfo.pConditionSet;
1707 if ( !pPattern )
1709 // #i68085# pattern from cell info for hidden columns is null,
1710 // test for null is quicker than using column flags
1711 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1712 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1715 else // get from document
1717 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1718 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1720 if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() )
1722 aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1723 ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1724 if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
1726 pAltPattern->SetStyleSheet(pPreviewStyle);
1728 else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
1730 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
1731 pAltPattern->GetItemSet().Put( *pItem );
1732 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
1733 pAltPattern->GetItemSet().Put( *pItem );
1734 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
1735 pAltPattern->GetItemSet().Put( *pItem );
1737 pPattern = pAltPattern;
1740 if (aCell.hasNumeric() &&
1741 pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
1743 // Disable line break when the cell content is numeric.
1744 aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1745 ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1746 ScLineBreakCell aLineBreak(false);
1747 pAltPattern->GetItemSet().Put(aLineBreak);
1748 pPattern = pAltPattern;
1751 SvtScriptType nScript = mpDoc->GetCellScriptType(
1752 ScAddress(nCellX, nCellY, nTab),
1753 pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet));
1755 if (nScript == SvtScriptType::NONE)
1756 nScript = ScGlobal::GetDefaultScriptType();
1758 if ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet ||
1759 nScript != nOldScript || mbSyntaxMode )
1761 if ( StringDiffer(pOldPattern,pPattern) ||
1762 pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode )
1764 aVars.SetPattern(pPattern, pCondSet, aCell, nScript);
1766 else
1767 aVars.SetPatternSimple( pPattern, pCondSet );
1768 pOldPattern = pPattern;
1769 pOldCondSet = pCondSet;
1770 nOldScript = nScript;
1773 // use edit engine for rotated, stacked or mixed-script text
1774 if ( aVars.GetOrient() == SvxCellOrientation::Stacked ||
1775 aVars.IsRotated() || IsAmbiguousScript(nScript) )
1776 bUseEditEngine = true;
1778 if (bDoCell && !bUseEditEngine)
1780 bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA);
1781 if ( bFormulaCell )
1782 lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula());
1783 if ( aVars.SetText(aCell) )
1784 pOldPattern = nullptr;
1785 bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult());
1787 tools::Long nTotalMargin = 0;
1788 SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard;
1789 if (bDoCell && !bUseEditEngine)
1791 CellType eCellType = aCell.getType();
1792 bCellIsValue = ( eCellType == CELLTYPE_VALUE );
1793 if ( eCellType == CELLTYPE_FORMULA )
1795 ScFormulaCell* pFCell = aCell.getFormula();
1796 bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
1799 const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
1800 eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(),
1801 *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText );
1803 bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block );
1804 // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats
1805 // Must be synchronized with ScColumn::GetNeededSize()
1806 SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1807 if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER))
1808 bBreak = false;
1810 bool bRepeat = aVars.IsRepeat() && !bBreak;
1811 bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;
1813 nTotalMargin =
1814 static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) +
1815 static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX);
1817 nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;
1819 // GetOutputArea gives justified rectangles
1820 GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
1821 *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
1822 bCellIsValue || bRepeat || bShrink, bBreak, false,
1823 aAreaParam );
1825 aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin );
1826 if ( bShrink )
1828 if ( aVars.GetOrient() != SvxCellOrientation::Standard )
1830 // Only horizontal scaling is handled here.
1831 // DrawEdit is used to vertically scale 90 deg rotated text.
1832 bUseEditEngine = true;
1834 else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal
1836 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1837 tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin
1839 if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats)
1841 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
1843 aVars.SetShrinkScale( nScale, nOldScript );
1844 tools::Long nNewSize = aVars.GetTextSize().Width();
1846 sal_uInt16 nShrinkAgain = 0;
1847 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
1849 // If the text is still too large, reduce the scale again by 10%, until it fits,
1850 // at most 7 times (it's less than 50% of the calculated scale then).
1852 nScale = ( nScale * 9 ) / 10;
1853 aVars.SetShrinkScale( nScale, nOldScript );
1854 nNewSize = aVars.GetTextSize().Width();
1855 ++nShrinkAgain;
1857 // If even at half the size the font still isn't rendered smaller,
1858 // fall back to normal clipping (showing ### for numbers).
1859 if ( nNewSize <= nAvailable )
1861 // Reset relevant parameters.
1862 aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1863 aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1866 pOldPattern = nullptr;
1871 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
1873 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1874 tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin
1875 // When formatting for the printer, the text sizes don't always add up.
1876 // Round down (too few repetitions) rather than exceeding the cell size then:
1877 if ( pFmtDevice != mpRefDevice )
1878 ++nRepeatSize;
1879 if ( nRepeatSize > 0 )
1881 tools::Long nRepeatCount = nAvailable / nRepeatSize;
1882 if ( nRepeatCount > 1 )
1884 OUString aCellStr = aVars.GetString();
1885 OUStringBuffer aRepeated(aCellStr);
1886 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
1887 aRepeated.append(aCellStr);
1888 aVars.SetAutoText( aRepeated.makeStringAndClear() );
1893 // use edit engine if automatic line breaks are needed
1894 if ( bBreak )
1896 if ( aVars.GetOrient() == SvxCellOrientation::Standard )
1897 bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
1898 else
1900 tools::Long nHeight = aVars.GetTextSize().Height() +
1901 static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) +
1902 static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY);
1903 bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() );
1906 if (!bUseEditEngine)
1908 bUseEditEngine =
1909 aVars.GetHorJust() == SvxCellHorJustify::Block &&
1910 aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute;
1913 if (bUseEditEngine)
1915 // mark the cell in ScCellInfo to be drawn in DrawEdit:
1916 // Cells to the left are marked directly, cells to the
1917 // right are handled by the flag for nX2
1918 SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
1919 pThisRowInfo->basicCellInfo(nMarkX).bEditEngine = true;
1920 bDoCell = false; // don't draw here
1922 // Mark the tagged "TD" structure element to be drawn in DrawEdit
1923 if (bTaggedPDF)
1925 sal_Int32 nId = pPDF->GetCurrentStructureElement();
1926 pPDF->GetScPDFState()->m_TableDataMap[{nY, nX}] = nId;
1929 if ( bDoCell )
1931 if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
1933 bool bHasHashText = false;
1934 if (mbShowFormulas)
1936 aVars.SetHashText();
1937 bHasHashText = true;
1939 else
1940 // Adjust the decimals to fit the available column width.
1941 bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin );
1943 if ( bHasHashText )
1945 tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
1947 if ( eOutHorJust == SvxCellHorJustify::Left )
1949 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1950 pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
1951 bAnyClipped = true;
1952 aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
1954 else if ( eOutHorJust == SvxCellHorJustify::Right )
1956 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1957 pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
1958 bAnyClipped = true;
1959 aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
1961 else
1963 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1965 pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
1966 pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
1968 bAnyClipped = true;
1969 aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
1970 aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
1974 nNeededWidth = aVars.GetTextSize().Width() +
1975 static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) +
1976 static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX );
1977 if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
1979 // Cell value is no longer clipped. Reset relevant parameters.
1980 aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1981 aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1985 tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added
1986 tools::Long nJustPosY = aAreaParam.maAlignRect.Top();
1987 tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
1988 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight();
1990 bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
1991 // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip
1992 bool bVClip = AdjustAreaParamClipRect(aAreaParam);
1993 bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
1995 // check horizontal space
1997 if ( !bOutside )
1999 bool bRightAdjusted = false; // to correct text width calculation later
2000 switch (eOutHorJust)
2002 case SvxCellHorJustify::Left:
2003 nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX );
2004 break;
2005 case SvxCellHorJustify::Right:
2006 nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
2007 static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX );
2008 bRightAdjusted = true;
2009 break;
2010 case SvxCellHorJustify::Center:
2011 nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
2012 static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) -
2013 static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2;
2014 break;
2015 default:
2017 // added to avoid warnings
2021 tools::Long nTestClipHeight = aVars.GetTextSize().Height();
2022 switch (aVars.GetVerJust())
2024 case SvxCellVerJustify::Top:
2025 case SvxCellVerJustify::Block:
2027 tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
2028 nJustPosY += nTop;
2029 nTestClipHeight += nTop;
2031 break;
2032 case SvxCellVerJustify::Bottom:
2034 tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
2035 nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
2036 nTestClipHeight += nBot;
2038 break;
2039 case SvxCellVerJustify::Center:
2041 tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
2042 tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
2043 nJustPosY += ( nOutHeight + nTop -
2044 aVars.GetTextSize().Height() - nBot ) / 2;
2045 nTestClipHeight += std::abs( nTop - nBot );
2047 break;
2048 default:
2050 // added to avoid warnings
2054 if ( nTestClipHeight > nOutHeight )
2056 // no vertical clipping when printing cells with optimal height,
2057 // except when font size is from conditional formatting.
2058 if ( eType != OUTTYPE_PRINTER ||
2059 ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) ||
2060 ( aVars.HasCondHeight() ) )
2061 bVClip = true;
2064 if ( bHClip || bVClip )
2066 // only clip the affected dimension so that not all right-aligned
2067 // columns are cut off when performing a non-proportional resize
2068 if (!bHClip)
2070 aAreaParam.maClipRect.SetLeft( nScrX );
2071 aAreaParam.maClipRect.SetRight( nScrX+nScrW );
2073 if (!bVClip)
2075 aAreaParam.maClipRect.SetTop( nScrY );
2076 aAreaParam.maClipRect.SetBottom( nScrY+nScrH );
2079 // aClipRect is not used after SetClipRegion/IntersectClipRegion,
2080 // so it can be modified here
2081 if (bPixelToLogic)
2082 aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect );
2084 if (bMetaFile)
2086 mpDev->Push();
2087 mpDev->IntersectClipRegion( aAreaParam.maClipRect );
2089 else
2090 mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
2093 Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation
2095 switch (aVars.GetOrient())
2097 case SvxCellOrientation::Standard:
2098 nJustPosY += aVars.GetAscent();
2099 break;
2100 case SvxCellOrientation::TopBottom:
2101 nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
2102 break;
2103 case SvxCellOrientation::BottomUp:
2104 nJustPosY += aVars.GetTextSize().Height();
2105 nJustPosX += aVars.GetAscent();
2106 break;
2107 default:
2109 // added to avoid warnings
2113 // When clipping, the visible part is now completely defined by the alignment,
2114 // there's no more special handling to show the right part of RTL text.
2116 Point aDrawTextPos( nJustPosX, nJustPosY );
2117 if ( bPixelToLogic )
2119 // undo text width adjustment in pixels
2120 if (bRightAdjusted)
2121 aDrawTextPos.AdjustX(aVars.GetTextSize().Width() );
2123 aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos );
2125 // redo text width adjustment in logic units
2126 if (bRightAdjusted)
2127 aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) );
2130 // in Metafiles always use DrawTextArray to ensure that positions are
2131 // recorded (for non-proportional resize):
2133 const OUString& aString = aVars.GetString();
2134 if (!aString.isEmpty())
2136 if (bTaggedPDF)
2137 pPDF->WrapBeginStructureElement(vcl::PDFWriter::Paragraph, u"P"_ustr);
2139 // If the string is clipped, make it shorter for
2140 // better performance since drawing by HarfBuzz is
2141 // quite expensive especially for long string.
2143 OUString aShort = aString;
2145 // But never fiddle with numeric values.
2146 // (Which was the cause of tdf#86024).
2147 // The General automatic format output takes
2148 // care of this, or fixed width numbers either fit
2149 // or display as ###.
2150 if (!bCellIsValue)
2152 double fVisibleRatio = 1.0;
2153 double fTextWidth = aVars.GetTextSize().Width();
2154 sal_Int32 nTextLen = aString.getLength();
2155 if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0)
2157 fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth;
2158 if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2160 // Only show the left-end segment.
2161 sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2162 aShort = aShort.copy(0, nShortLen);
2165 else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0)
2167 fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth;
2168 if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2170 // Only show the right-end segment.
2171 sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2172 aShort = aShort.copy(nTextLen-nShortLen);
2174 // Adjust the text position after shortening of the string.
2175 double fShortWidth = aVars.GetFmtTextWidth(aShort);
2176 double fOffset = fTextWidth - fShortWidth;
2177 aDrawTextPos.Move(fOffset, 0);
2182 if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY)
2184 size_t nLen = aShort.getLength();
2185 if (aDX.size() < nLen)
2186 aDX.resize(nLen, 0);
2188 pFmtDevice->GetTextArray(aShort, &aDX);
2190 if ( !mpRefDevice->GetConnectMetaFile() ||
2191 mpRefDevice->GetOutDevType() == OUTDEV_PRINTER )
2193 double fMul = GetStretch();
2194 for (size_t i = 0; i < nLen; ++i)
2195 aDX[i] /= fMul;
2198 mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
2200 else
2202 mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr,
2203 aVars.GetLayoutGlyphs(aShort));
2205 if (bTaggedPDF)
2206 pPDF->EndStructureElement();
2209 if ( bHClip || bVClip )
2211 if (bMetaFile)
2212 mpDev->Pop();
2213 else
2214 mpDev->SetClipRegion();
2217 // PDF: whole-cell hyperlink from formula?
2218 bool bHasURL = pPDF && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell();
2219 if (bHasURL)
2221 tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
2222 lcl_DoHyperlinkResult(mpDev, aURLRect, aCell);
2226 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2227 if (bTaggedPDF)
2228 pPDF->EndStructureElement();
2230 if (bTaggedPDF)
2231 pPDF->EndStructureElement();
2233 nPosY += pRowInfo[nArrY].nHeight;
2235 if (bTaggedPDF)
2236 pPDF->EndStructureElement();
2238 if ( bProgress )
2239 ScProgress::DeleteInterpretProgress();
2242 void ScOutputData::SetRefDevice( OutputDevice* pRDev )
2244 mpRefDevice = pFmtDevice = pRDev;
2245 // reset EditEngine because it depends on pFmtDevice and mpRefDevice
2246 mxOutputEditEngine.reset();
2249 void ScOutputData::SetFmtDevice( OutputDevice* pRDev )
2251 pFmtDevice = pRDev;
2252 // reset EditEngine because it depends on pFmtDevice
2253 mxOutputEditEngine.reset();
2256 void ScOutputData::SetUseStyleColor( bool bSet )
2258 mbUseStyleColor = bSet;
2259 // reset EditEngine because it depends on mbUseStyleColor
2260 mxOutputEditEngine.reset();
2263 void ScOutputData::InitOutputEditEngine()
2265 if (!mxOutputEditEngine)
2267 mxOutputEditEngine = std::make_unique<ScFieldEditEngine>(mpDoc, mpDoc->GetEnginePool());
2268 mxOutputEditEngine->SetUpdateLayout( false );
2269 mxOutputEditEngine->EnableUndo( false ); // don't need undo for painting purposes
2270 // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
2271 mxOutputEditEngine->SetRefDevice( pFmtDevice );
2272 EEControlBits nCtrl = mxOutputEditEngine->GetControlWord();
2273 if ( bShowSpellErrors )
2274 nCtrl |= EEControlBits::ONLINESPELLING;
2275 if ( eType == OUTTYPE_PRINTER )
2276 nCtrl &= ~EEControlBits::MARKFIELDS;
2277 else
2278 nCtrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
2279 mxOutputEditEngine->SetControlWord( nCtrl );
2280 mxOutputEditEngine->EnableAutoColor( mbUseStyleColor );
2282 else
2284 // just in case someone turned it on during the last paint cycle
2285 mxOutputEditEngine->SetUpdateLayout( false );
2287 // we don't track changes to these settings, so we have to apply them every time
2288 mpDoc->ApplyAsianEditSettings( *mxOutputEditEngine );
2289 mxOutputEditEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( nTab ) );
2292 static void lcl_ClearEdit( EditEngine& rEngine ) // text and attributes
2294 rEngine.SetUpdateLayout( false );
2296 rEngine.SetText(OUString());
2297 // do not keep any para-attributes
2298 const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
2299 if (rPara.Count())
2300 rEngine.SetParaAttribs( 0,
2301 SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
2302 rEngine.EnableSkipOutsideFormat(false);
2305 static bool lcl_SafeIsValue( ScRefCellValue& rCell )
2307 switch (rCell.getType())
2309 case CELLTYPE_VALUE:
2310 return true;
2311 case CELLTYPE_FORMULA:
2313 ScFormulaCell* pFCell = rCell.getFormula();
2314 if (pFCell->IsRunning() || pFCell->IsValue())
2315 return true;
2317 break;
2318 default:
2320 // added to avoid warnings
2323 return false;
2326 static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent )
2328 bool bUpdateMode = rEngine.SetUpdateLayout( false );
2330 sal_Int32 nParCount = rEngine.GetParagraphCount();
2331 for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
2333 std::vector<sal_Int32> aPortions;
2334 rEngine.GetPortions( nPar, aPortions );
2336 sal_Int32 nStart = 0;
2337 for ( const sal_Int32 nEnd : aPortions )
2339 ESelection aSel( nPar, nStart, nPar, nEnd );
2340 SfxItemSet aAttribs = rEngine.GetAttribs( aSel );
2342 tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight();
2343 tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight();
2344 tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight();
2346 nWestern = ( nWestern * nPercent ) / 100;
2347 nCJK = ( nCJK * nPercent ) / 100;
2348 nCTL = ( nCTL * nPercent ) / 100;
2350 aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
2351 aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
2352 aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );
2354 rEngine.QuickSetAttribs( aAttribs, aSel ); //! remove paragraph attributes from aAttribs?
2356 nStart = nEnd;
2360 if ( bUpdateMode )
2361 rEngine.SetUpdateLayout( true );
2364 static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate )
2366 if ( bSwap )
2367 bWidth = !bWidth;
2369 if ( nAttrRotate )
2371 tools::Long nRealWidth = static_cast<tools::Long>(rEngine.CalcTextWidth());
2372 tools::Long nRealHeight = rEngine.GetTextHeight();
2374 // assuming standard mode, otherwise width isn't used
2376 double nRealOrient = toRadians(nAttrRotate); // 1/100th degrees
2377 double nAbsCos = fabs( cos( nRealOrient ) );
2378 double nAbsSin = fabs( sin( nRealOrient ) );
2379 if ( bWidth )
2380 return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
2381 else
2382 return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
2384 else if ( bWidth )
2385 return static_cast<tools::Long>(rEngine.CalcTextWidth());
2386 else
2387 return rEngine.GetTextHeight();
2390 void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect,
2391 tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
2392 bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
2393 tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
2395 if ( !bWidth )
2397 // vertical
2399 tools::Long nScaleSize = bPixelToLogic ?
2400 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2402 // Don't scale if it fits already.
2403 // Allowing to extend into the margin, to avoid scaling at optimal height.
2404 if ( nScaleSize <= rAlignRect.GetHeight() )
2405 return;
2407 bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp );
2408 tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
2409 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2411 lcl_ScaleFonts( rEngine, nScale );
2412 rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2413 tools::Long nNewSize = bPixelToLogic ?
2414 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2416 sal_uInt16 nShrinkAgain = 0;
2417 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2419 // further reduce, like in DrawStrings
2420 lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
2421 rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2422 nNewSize = bPixelToLogic ?
2423 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2424 ++nShrinkAgain;
2427 // sizes for further processing (alignment etc):
2428 rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate );
2429 tools::Long nPixelWidth = bPixelToLogic ?
2430 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2431 rNeededPixel = nPixelWidth + nLeftM + nRightM;
2433 else if ( rLeftClip || rRightClip )
2435 // horizontal
2437 tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
2438 tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin
2440 if ( nScaleSize <= nAvailable )
2441 return;
2443 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2445 lcl_ScaleFonts( rEngine, nScale );
2446 rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2447 tools::Long nNewSize = bPixelToLogic ?
2448 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2450 sal_uInt16 nShrinkAgain = 0;
2451 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2453 // further reduce, like in DrawStrings
2454 lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
2455 rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2456 nNewSize = bPixelToLogic ?
2457 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2458 ++nShrinkAgain;
2460 if ( nNewSize <= nAvailable )
2461 rLeftClip = rRightClip = false;
2463 // sizes for further processing (alignment etc):
2464 rNeededPixel = nNewSize + nLeftM + nRightM;
2465 rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate );
2469 ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue) :
2470 meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ),
2471 meHorJustContext( meHorJustAttr ),
2472 meHorJustResult( meHorJustAttr ),
2473 meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ),
2474 meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ),
2475 meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ),
2476 meOrient( pPattern->GetCellOrientation(pCondSet) ),
2477 mnArrY(0),
2478 mnX(0), mnCellX(0), mnCellY(0),
2479 mnPosX(0), mnPosY(0), mnInitPosX(0),
2480 mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ),
2481 mbCellIsValue(bCellIsValue),
2482 mbAsianVertical(false),
2483 mbPixelToLogic(false),
2484 mbHyphenatorSet(false),
2485 mpEngine(nullptr),
2486 mpPattern(pPattern),
2487 mpCondSet(pCondSet),
2488 mpPreviewFontSet(nullptr),
2489 mpOldPattern(nullptr),
2490 mpOldCondSet(nullptr),
2491 mpOldPreviewFontSet(nullptr),
2492 mpThisRowInfo(nullptr),
2493 mpMisspellRanges(nullptr)
2496 bool ScOutputData::DrawEditParam::readCellContent(
2497 const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields)
2499 if (maCell.getType() == CELLTYPE_EDIT)
2501 const EditTextObject* pData = maCell.getEditText();
2502 if (pData)
2504 mpEngine->SetTextCurrentDefaults(*pData);
2506 if ( mbBreak && !mbAsianVertical && pData->HasField() )
2508 // Fields aren't wrapped, so clipping is enabled to prevent
2509 // a field from being drawn beyond the cell size
2511 rWrapFields = true;
2514 else
2516 OSL_FAIL("pData == 0");
2517 return false;
2520 else
2522 sal_uInt32 nFormat = mpPattern->GetNumberFormat(
2523 pDoc->GetFormatTable(), mpCondSet );
2524 const Color* pColor;
2525 OUString aString = ScCellFormat::GetString( maCell,
2526 nFormat, &pColor,
2527 nullptr,
2528 *pDoc,
2529 bShowNullValues,
2530 bShowFormulas);
2532 mpEngine->SetTextCurrentDefaults(aString);
2533 if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
2534 lcl_SetEditColor( *mpEngine, *pColor );
2537 if (mpMisspellRanges)
2538 mpEngine->SetAllMisspellRanges(*mpMisspellRanges);
2540 return true;
2543 static Color GetConfBackgroundColor()
2545 if (const ScTabViewShell* pTabViewShellBg = ScTabViewShell::GetActiveViewShell())
2546 return pTabViewShellBg->GetViewRenderingData().GetDocColor();
2547 return ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2550 void ScOutputData::DrawEditParam::setPatternToEngine(bool bUseStyleColor)
2552 // syntax highlighting mode is ignored here
2553 // StringDiffer doesn't look at hyphenate, language items
2555 if (ScPatternAttr::areSame(mpPattern, mpOldPattern) && mpCondSet == mpOldCondSet && mpPreviewFontSet == mpOldPreviewFontSet )
2556 return;
2558 Color nConfBackColor = GetConfBackgroundColor();
2559 bool bCellContrast = bUseStyleColor &&
2560 Application::GetSettings().GetStyleSettings().GetHighContrastMode();
2562 auto pSet = std::make_unique<SfxItemSet>( mpEngine->GetEmptyItemSet() );
2563 mpPattern->FillEditItemSet( pSet.get(), mpCondSet );
2564 if ( mpPreviewFontSet )
2566 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) )
2568 // tdf#125054 adapt WhichID
2569 pSet->PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO);
2571 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
2573 // tdf#125054 adapt WhichID
2574 pSet->PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO_CJK);
2576 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
2578 // tdf#125054 adapt WhichID
2579 pSet->PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO_CTL);
2582 bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
2583 mpEngine->SetDefaults( std::move(pSet) );
2584 mpOldPattern = mpPattern;
2585 mpOldCondSet = mpCondSet;
2586 mpOldPreviewFontSet = mpPreviewFontSet;
2588 EEControlBits nControl = mpEngine->GetControlWord();
2589 if (meOrient == SvxCellOrientation::Stacked)
2590 nControl |= EEControlBits::ONECHARPERLINE;
2591 else
2592 nControl &= ~EEControlBits::ONECHARPERLINE;
2593 mpEngine->SetControlWord( nControl );
2595 if ( !mbHyphenatorSet && bParaHyphenate )
2597 // set hyphenator the first time it is needed
2598 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
2599 mpEngine->SetHyphenator( xXHyphenator );
2600 mbHyphenatorSet = true;
2603 Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor();
2604 if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
2605 aBackCol = nConfBackColor;
2606 mpEngine->SetBackgroundColor( aBackCol );
2609 void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const
2611 const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet);
2613 sal_uInt16 nIndent = 0;
2614 if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right)
2615 nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet);
2617 rLeftM = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX));
2618 rTopM = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY));
2619 rRightM = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX));
2620 rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY));
2621 if(meHorJustAttr == SvxCellHorJustify::Right)
2623 rLeftM = static_cast<tools::Long>((rMargin.GetLeftMargin() * nPPTX));
2624 rRightM = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX));
2628 void ScOutputData::DrawEditParam::calcPaperSize(
2629 Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const
2631 tools::Long nTopM, nLeftM, nBottomM, nRightM;
2632 calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY);
2634 if (isVerticallyOriented())
2636 rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM );
2637 rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM );
2639 else
2641 rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM );
2642 rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2645 if (mbAsianVertical)
2647 rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2648 // Subtract some extra value from the height or else the text would go
2649 // outside the cell area. The value of 5 is arbitrary, and is based
2650 // entirely on heuristics.
2651 rPaperSize.AdjustHeight( -5 );
2655 void ScOutputData::DrawEditParam::getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const
2657 tools::Long nEngineWidth = 0;
2658 if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical)
2659 nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth());
2661 tools::Long nEngineHeight = pEngine->GetTextHeight();
2663 if (isVerticallyOriented())
2664 std::swap( nEngineWidth, nEngineHeight );
2666 if (meOrient == SvxCellOrientation::Stacked)
2667 nEngineWidth = nEngineWidth * 11 / 10;
2669 rWidth = nEngineWidth;
2670 rHeight = nEngineHeight;
2673 bool ScOutputData::DrawEditParam::hasLineBreak() const
2675 return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical);
2678 bool ScOutputData::DrawEditParam::isHyperlinkCell() const
2680 if (maCell.getType() != CELLTYPE_FORMULA)
2681 return false;
2683 return maCell.getFormula()->IsHyperLinkCell();
2686 bool ScOutputData::DrawEditParam::isVerticallyOriented() const
2688 return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp);
2691 void ScOutputData::DrawEditParam::calcStartPosForVertical(
2692 Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice)
2694 OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!");
2696 if (mbPixelToLogic)
2697 rLogicStart = pRefDevice->PixelToLogic(rLogicStart);
2699 if (!mbBreak)
2700 return;
2702 // vertical adjustment is within the EditEngine
2703 if (mbPixelToLogic)
2704 rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
2705 else
2706 rLogicStart.AdjustY(nTopM );
2708 switch (meHorJustResult)
2710 case SvxCellHorJustify::Center:
2711 rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 );
2712 break;
2713 case SvxCellHorJustify::Right:
2714 rLogicStart.AdjustX(nCellWidth - nEngineWidth );
2715 break;
2716 default:
2717 ; // do nothing
2721 void ScOutputData::DrawEditParam::setAlignmentToEngine()
2723 if (isVerticallyOriented() || mbAsianVertical)
2725 SvxAdjust eSvxAdjust = SvxAdjust::Left;
2726 switch (meVerJust)
2728 case SvxCellVerJustify::Top:
2729 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2730 SvxAdjust::Left : SvxAdjust::Right;
2731 break;
2732 case SvxCellVerJustify::Center:
2733 eSvxAdjust = SvxAdjust::Center;
2734 break;
2735 case SvxCellVerJustify::Bottom:
2736 case SvxCellVerJustify::Standard:
2737 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2738 SvxAdjust::Right : SvxAdjust::Left;
2739 break;
2740 case SvxCellVerJustify::Block:
2741 eSvxAdjust = SvxAdjust::Block;
2742 break;
2745 mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2746 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2748 if (meHorJustResult == SvxCellHorJustify::Block)
2749 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2751 else
2753 // horizontal alignment now may depend on cell content
2754 // (for values with number formats with mixed script types)
2755 // -> always set adjustment
2757 SvxAdjust eSvxAdjust = SvxAdjust::Left;
2758 if (meOrient == SvxCellOrientation::Stacked)
2759 eSvxAdjust = SvxAdjust::Center;
2760 else if (mbBreak)
2762 if (meOrient == SvxCellOrientation::Standard)
2763 switch (meHorJustResult)
2765 case SvxCellHorJustify::Repeat: // repeat is not yet implemented
2766 case SvxCellHorJustify::Standard:
2767 SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()");
2768 [[fallthrough]];
2769 case SvxCellHorJustify::Left:
2770 eSvxAdjust = SvxAdjust::Left;
2771 break;
2772 case SvxCellHorJustify::Center:
2773 eSvxAdjust = SvxAdjust::Center;
2774 break;
2775 case SvxCellHorJustify::Right:
2776 eSvxAdjust = SvxAdjust::Right;
2777 break;
2778 case SvxCellHorJustify::Block:
2779 eSvxAdjust = SvxAdjust::Block;
2780 break;
2782 else
2783 switch (meVerJust)
2785 case SvxCellVerJustify::Top:
2786 eSvxAdjust = SvxAdjust::Right;
2787 break;
2788 case SvxCellVerJustify::Center:
2789 eSvxAdjust = SvxAdjust::Center;
2790 break;
2791 case SvxCellVerJustify::Bottom:
2792 case SvxCellVerJustify::Standard:
2793 eSvxAdjust = SvxAdjust::Left;
2794 break;
2795 case SvxCellVerJustify::Block:
2796 eSvxAdjust = SvxAdjust::Block;
2797 break;
2801 mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2803 if (mbAsianVertical)
2805 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2806 if (meHorJustResult == SvxCellHorJustify::Block)
2807 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2809 else
2811 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) );
2812 if (meVerJust == SvxCellVerJustify::Block)
2813 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2817 mpEngine->SetVertical(mbAsianVertical);
2818 if (maCell.getType() == CELLTYPE_EDIT)
2820 // We need to synchronize the vertical mode in the EditTextObject
2821 // instance too. No idea why we keep this state in two separate
2822 // instances.
2823 const EditTextObject* pData = maCell.getEditText();
2824 if (pData)
2825 const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical);
2829 bool ScOutputData::DrawEditParam::adjustHorAlignment(ScFieldEditEngine* pEngine)
2831 if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center)
2833 SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ?
2834 SvxAdjust::Center : SvxAdjust::Right;
2836 const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false);
2837 pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) );
2838 pEngine->SetUpdateLayout(bPrevUpdateLayout);
2839 return true;
2841 return false;
2844 void ScOutputData::DrawEditParam::adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev)
2846 // PDF: whole-cell hyperlink from formula?
2847 vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
2848 bool bHasURL = pPDFData && isHyperlinkCell();
2849 if (!bHasURL)
2850 return;
2852 tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth());
2853 tools::Long nURLHeight = mpEngine->GetTextHeight();
2854 if (mbBreak)
2856 Size aPaper = mpEngine->GetPaperSize();
2857 if ( mbAsianVertical )
2858 nURLHeight = aPaper.Height();
2859 else
2860 nURLWidth = aPaper.Width();
2862 if (isVerticallyOriented())
2863 std::swap( nURLWidth, nURLHeight );
2864 else if (mbAsianVertical)
2865 aURLStart.AdjustX( -nURLWidth );
2867 tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
2868 lcl_DoHyperlinkResult(pDev, aURLRect, maCell);
2871 // Returns true if the rect is clipped vertically
2872 bool ScOutputData::AdjustAreaParamClipRect(OutputAreaParam& rAreaParam)
2874 if( rAreaParam.maClipRect.Left() < nScrX )
2876 rAreaParam.maClipRect.SetLeft( nScrX );
2877 rAreaParam.mbLeftClip = true;
2879 if( rAreaParam.maClipRect.Right() > nScrX + nScrW )
2881 rAreaParam.maClipRect.SetRight( nScrX + nScrW ); //! minus one?
2882 rAreaParam.mbRightClip = true;
2885 bool bVClip = false;
2887 if( rAreaParam.maClipRect.Top() < nScrY )
2889 rAreaParam.maClipRect.SetTop( nScrY );
2890 bVClip = true;
2892 if( rAreaParam.maClipRect.Bottom() > nScrY + nScrH )
2894 rAreaParam.maClipRect.SetBottom( nScrY + nScrH ); //! minus one?
2895 bVClip = true;
2897 return bVClip;
2900 // Doesn't handle clip marks - should be handled in advance using GetOutputArea
2901 class ClearableClipRegion
2903 public:
2904 ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip,
2905 const VclPtr<OutputDevice>& pDev, bool bMetaFile )
2906 :mbMetaFile(bMetaFile)
2908 if (!(bClip || bSimClip))
2909 return;
2911 maRect = rRect;
2912 if (bClip) // for bSimClip only initialize aClipRect
2914 mpDev.reset(pDev);
2915 if (mbMetaFile)
2917 mpDev->Push();
2918 mpDev->IntersectClipRegion(maRect);
2920 else
2921 mpDev->SetClipRegion(vcl::Region(maRect));
2925 ~ClearableClipRegion() COVERITY_NOEXCEPT_FALSE
2927 // Pop() or SetClipRegion() must only be called in case bClip was true
2928 // in the ctor, and only then mpDev is set.
2929 if (mpDev)
2931 if (mbMetaFile)
2932 mpDev->Pop();
2933 else
2934 mpDev->SetClipRegion();
2938 const tools::Rectangle& getRect() const { return maRect; }
2940 private:
2941 tools::Rectangle maRect;
2942 VclPtr<OutputDevice> mpDev;
2943 bool mbMetaFile;
2946 // Returns needed width in current units; sets rNeededPixel to needed width in pixels
2947 tools::Long ScOutputData::SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
2948 tools::Long& rNeededPixel, tools::Long nAddWidthPixels )
2950 rParam.mpEngine->SetTextCurrentDefaults( rSetString );
2951 tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() );
2952 if ( rParam.mbPixelToLogic )
2953 rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width();
2954 else
2955 rNeededPixel = nEngineWidth;
2957 rNeededPixel += nAddWidthPixels;
2959 return nEngineWidth;
2962 void ScOutputData::DrawEditStandard(DrawEditParam& rParam)
2964 OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
2965 OSL_ASSERT(!rParam.mbAsianVertical);
2967 Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
2969 bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
2970 bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
2971 Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
2973 if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat )
2975 // ignore orientation/rotation if "repeat" is active
2976 rParam.meOrient = SvxCellOrientation::Standard;
2977 nAttrRotate = 0_deg100;
2979 // #i31843# "repeat" with "line breaks" is treated as default alignment
2980 // (but rotation is still disabled).
2981 // Default again leads to context dependent alignment instead of
2982 // SvxCellHorJustify::Standard.
2983 if ( rParam.mbBreak )
2984 rParam.meHorJustResult = rParam.meHorJustContext;
2987 if (nAttrRotate)
2989 //! set flag to find the cell in DrawRotated again ?
2990 //! (or flag already set during DrawBackground, then no query here)
2991 return; // rotated is outputted separately
2994 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
2996 //! mirror margin values for RTL?
2997 //! move margin down to after final GetOutputArea call
2998 tools::Long nTopM, nLeftM, nBottomM, nRightM;
2999 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3001 SCCOL nXForPos = rParam.mnX;
3002 if ( nXForPos < nX1 )
3004 nXForPos = nX1;
3005 rParam.mnPosX = rParam.mnInitPosX;
3007 SCSIZE nArrYForPos = rParam.mnArrY;
3008 if ( nArrYForPos < 1 )
3010 nArrYForPos = 1;
3011 rParam.mnPosY = nScrY;
3014 OutputAreaParam aAreaParam;
3016 // Initial page size - large for normal text, cell size for automatic line breaks
3018 Size aPaperSize( 1000000, 1000000 );
3019 if (rParam.mbBreak)
3021 // call GetOutputArea with nNeeded=0, to get only the cell width
3023 //! handle nArrY == 0
3024 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3025 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3026 rParam.mbCellIsValue, true, false, aAreaParam );
3028 //! special ScEditUtil handling if formatting for printer
3029 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3031 if (rParam.mbPixelToLogic)
3033 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3034 if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
3036 // #i85342# screen display and formatting for printer,
3037 // use same GetEditArea call as in ScViewData::SetEditEngine
3039 Fraction aFract(1,1);
3040 tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
3041 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
3042 aLogicSize.setWidth( aUtilRect.GetWidth() );
3044 rParam.mpEngine->SetPaperSize(aLogicSize);
3046 else
3047 rParam.mpEngine->SetPaperSize(aPaperSize);
3049 // Fill the EditEngine (cell attributes and text)
3051 // default alignment for asian vertical mode is top-right
3052 if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard )
3053 rParam.meVerJust = SvxCellVerJustify::Top;
3055 rParam.setPatternToEngine(mbUseStyleColor);
3056 rParam.setAlignmentToEngine();
3057 // Don't format unnecessary parts if the text will be drawn from top (Standard will
3058 // act that way if text doesn't fit, see below).
3059 rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top
3060 || rParam.meVerJust==SvxCellVerJustify::Standard);
3062 // Read content from cell
3064 bool bWrapFields = false;
3065 if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3066 // Failed to read cell content. Bail out.
3067 return;
3069 if ( mbSyntaxMode )
3070 SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell);
3071 else if ( mbUseStyleColor && mbForceAutoColor )
3072 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
3074 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
3076 // Get final output area using the calculated width
3078 tools::Long nEngineWidth, nEngineHeight;
3079 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3081 tools::Long nNeededPixel = nEngineWidth;
3082 if (rParam.mbPixelToLogic)
3083 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3084 nNeededPixel += nLeftM + nRightM;
3086 if (!rParam.mbBreak || bShrink)
3088 // for break, the first GetOutputArea call is sufficient
3089 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3090 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3091 rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
3093 if ( bShrink )
3095 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3096 nLeftM, nTopM, nRightM, nBottomM, true,
3097 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
3098 nEngineWidth, nEngineHeight, nNeededPixel,
3099 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3101 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
3103 // First check if twice the space for the formatted text is available
3104 // (otherwise just keep it unchanged).
3106 tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
3107 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
3108 if ( nAvailable >= 2 * nFormatted )
3110 // "repeat" is handled with unformatted text (for performance reasons)
3111 OUString aCellStr = rParam.mpEngine->GetText();
3113 tools::Long nRepeatSize = 0;
3114 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
3115 if ( pFmtDevice != mpRefDevice )
3116 ++nRepeatSize;
3117 if ( nRepeatSize > 0 )
3119 tools::Long nRepeatCount = nAvailable / nRepeatSize;
3120 if ( nRepeatCount > 1 )
3122 OUStringBuffer aRepeated(aCellStr);
3123 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3124 aRepeated.append(aCellStr);
3126 SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3127 nNeededPixel, (nLeftM + nRightM ) );
3129 nEngineHeight = rParam.mpEngine->GetTextHeight();
3135 if (rParam.mnX >= nX1 && rParam.mbCellIsValue
3136 && (aAreaParam.mbLeftClip || aAreaParam.mbRightClip))
3138 SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) );
3139 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
3140 ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3141 SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
3144 if (eOutHorJust != SvxCellHorJustify::Left)
3146 aPaperSize.setWidth( nNeededPixel + 1 );
3147 if (rParam.mbPixelToLogic)
3148 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
3149 else
3150 rParam.mpEngine->SetPaperSize(aPaperSize);
3154 tools::Long nStartX = aAreaParam.maAlignRect.Left();
3155 tools::Long nStartY = aAreaParam.maAlignRect.Top();
3156 tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3157 tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3158 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3160 if (rParam.mbBreak)
3162 // text with automatic breaks is aligned only within the
3163 // edit engine's paper size, the output of the whole area
3164 // is always left-aligned
3166 nStartX += nLeftM;
3168 else
3170 if ( eOutHorJust == SvxCellHorJustify::Right )
3171 nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3172 else if ( eOutHorJust == SvxCellHorJustify::Center )
3173 nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3174 else
3175 nStartX += nLeftM;
3178 bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3179 if (bOutside)
3180 return;
3182 // Also take fields in a cell with automatic breaks into account: clip to cell width
3183 bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3184 bool bSimClip = false;
3186 Size aCellSize; // output area, excluding margins, in logical units
3187 if (rParam.mbPixelToLogic)
3188 aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
3189 else
3190 aCellSize = Size( nOutWidth, nOutHeight );
3192 if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
3194 const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3195 bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3197 // Don't clip for text height when printing rows with optimal height,
3198 // except when font size is from conditional formatting.
3199 //! Allow clipping when vertically merged?
3200 if ( eType != OUTTYPE_PRINTER ||
3201 ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3202 ( rParam.mpCondSet && SfxItemState::SET ==
3203 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
3204 bClip = true;
3205 else
3206 bSimClip = true;
3208 // Show clip marks if height is at least 5pt too small and
3209 // there are several lines of text.
3210 // Not for asian vertical text, because that would interfere
3211 // with the default right position of the text.
3212 // Only with automatic line breaks, to avoid having to find
3213 // the cells with the horizontal end of the text again.
3214 if ( nEngineHeight - aCellSize.Height() > 100 &&
3215 rParam.mbBreak && bMarkClipped &&
3216 ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
3218 ScCellInfo* pClipMarkCell = nullptr;
3219 if ( bMerged )
3221 // anywhere in the merged area...
3222 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
3223 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3225 else
3226 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3228 pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
3229 bAnyClipped = true;
3231 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
3232 if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
3233 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
3235 // Standard is normally treated as Bottom, but if text height is clipped, then
3236 // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat().
3237 if (rParam.meVerJust==SvxCellVerJustify::Standard)
3238 rParam.meVerJust=SvxCellVerJustify::Top;
3242 Point aURLStart;
3244 { // Clip marks are already handled in GetOutputArea
3245 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
3246 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
3248 Point aLogicStart;
3249 if (rParam.mbPixelToLogic)
3250 aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
3251 else
3252 aLogicStart = Point(nStartX, nStartY);
3254 if (!rParam.mbBreak)
3256 // horizontal alignment
3257 if (rParam.adjustHorAlignment(rParam.mpEngine))
3258 // reset adjustment for the next cell
3259 rParam.mpOldPattern = nullptr;
3262 if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
3263 rParam.meVerJust==SvxCellVerJustify::Standard)
3265 //! if pRefDevice != pFmtDevice, keep heights in logic units,
3266 //! only converting margin?
3268 if (rParam.mbPixelToLogic)
3269 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
3270 mpRefDevice->LogicToPixel(aCellSize).Height() -
3271 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
3272 )).Height() );
3273 else
3274 aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
3276 else if (rParam.meVerJust==SvxCellVerJustify::Center)
3278 if (rParam.mbPixelToLogic)
3279 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
3280 mpRefDevice->LogicToPixel(aCellSize).Height() -
3281 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
3282 / 2)).Height() );
3283 else
3284 aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
3286 else // top
3288 if (rParam.mbPixelToLogic)
3289 aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
3290 else
3291 aLogicStart.AdjustY(nTopM );
3294 aURLStart = aLogicStart; // copy before modifying for orientation
3296 // bMoveClipped handling has been replaced by complete alignment
3297 // handling (also extending to the left).
3299 if (bSimClip)
3301 // no hard clip, only draw the affected rows
3302 Point aDocStart = aClip.getRect().TopLeft();
3303 aDocStart -= aLogicStart;
3304 rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
3306 else
3308 rParam.mpEngine->Draw(*mpDev, aLogicStart);
3312 rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3315 void ScOutputData::SetClipMarks( OutputAreaParam &aAreaParam, ScCellInfo* pClipMarkCell,
3316 SvxCellHorJustify eOutHorJust,
3317 tools::Long nLayoutSign )
3319 tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
3321 if ( eOutHorJust == SvxCellHorJustify::Left )
3323 pClipMarkCell->nClipMark |= ScClipMark::Right;
3324 bAnyClipped = true;
3325 aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
3327 else if ( eOutHorJust == SvxCellHorJustify::Right )
3329 pClipMarkCell->nClipMark |= ScClipMark::Left;
3330 bAnyClipped = true;
3331 aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
3333 else
3335 pClipMarkCell->nClipMark |= ScClipMark::Right;
3336 pClipMarkCell->nClipMark |= ScClipMark::Left;
3337 bAnyClipped = true;
3338 aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
3339 aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
3344 void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
3345 bool bMerged, OutputAreaParam& aAreaParam, bool bTop)
3347 // Show clip marks if width is at least 5pt too small and
3348 // there are several lines of text.
3349 // Not for asian vertical text, because that would interfere
3350 // with the default right position of the text.
3351 // Only with automatic line breaks, to avoid having to find
3352 // the cells with the horizontal end of the text again.
3353 if (nEngineWidth - aCellSize.Width() <= 100 || !rParam.mbBreak || !bMarkClipped
3354 || (rParam.mpEngine->GetParagraphCount() <= 1 && rParam.mpEngine->GetLineCount(0) <= 1))
3355 return;
3357 ScCellInfo* pClipMarkCell = nullptr;
3358 if (bMerged)
3360 // anywhere in the merged area...
3361 SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
3362 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3364 else
3365 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3367 bAnyClipped = true;
3368 bVertical = true;
3369 const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
3370 if (bTop)
3372 pClipMarkCell->nClipMark |= ScClipMark::Top;
3373 if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3374 aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
3376 else
3378 pClipMarkCell->nClipMark |= ScClipMark::Bottom;
3379 if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3380 aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
3384 ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize,
3385 OutputAreaParam& aAreaParam, tools::Long nEngineWidth,
3386 bool bWrapFields, bool bTop)
3388 // Also take fields in a cell with automatic breaks into account: clip to cell width
3389 bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3390 bool bSimClip = false;
3392 const Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
3393 if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() )
3395 const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3396 const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3398 // Don't clip for text height when printing rows with optimal height,
3399 // except when font size is from conditional formatting.
3400 //! Allow clipping when vertically merged?
3401 if ( eType != OUTTYPE_PRINTER ||
3402 ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3403 ( rParam.mpCondSet && SfxItemState::SET ==
3404 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
3405 bClip = true;
3406 else
3407 bSimClip = true;
3409 ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop);
3412 // Clip marks are already handled in GetOutputArea
3413 return ClearableClipRegionPtr(new ClearableClipRegion(rParam.mbPixelToLogic ?
3414 mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
3415 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile));
3418 void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam)
3420 OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3422 const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3423 const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3425 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3427 //! mirror margin values for RTL?
3428 //! move margin down to after final GetOutputArea call
3429 tools::Long nTopM, nLeftM, nBottomM, nRightM;
3430 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3432 SCCOL nXForPos = rParam.mnX;
3433 if ( nXForPos < nX1 )
3435 nXForPos = nX1;
3436 rParam.mnPosX = rParam.mnInitPosX;
3438 SCSIZE nArrYForPos = rParam.mnArrY;
3439 if ( nArrYForPos < 1 )
3441 nArrYForPos = 1;
3442 rParam.mnPosY = nScrY;
3445 OutputAreaParam aAreaParam;
3447 // Initial page size - large for normal text, cell size for automatic line breaks
3449 Size aPaperSize( 1000000, 1000000 );
3450 if (rParam.mbBreak)
3452 // call GetOutputArea with nNeeded=0, to get only the cell width
3454 //! handle nArrY == 0
3455 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3456 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3457 rParam.mbCellIsValue, true, false, aAreaParam );
3459 //! special ScEditUtil handling if formatting for printer
3460 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3462 if (rParam.mbPixelToLogic)
3464 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3465 rParam.mpEngine->SetPaperSize(aLogicSize);
3467 else
3468 rParam.mpEngine->SetPaperSize(aPaperSize);
3470 // Fill the EditEngine (cell attributes and text)
3472 rParam.setPatternToEngine(mbUseStyleColor);
3473 rParam.setAlignmentToEngine();
3475 // Read content from cell
3477 bool bWrapFields = false;
3478 if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3479 // Failed to read cell content. Bail out.
3480 return;
3482 if ( mbSyntaxMode )
3483 SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3484 else if ( mbUseStyleColor && mbForceAutoColor )
3485 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
3487 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
3489 // Get final output area using the calculated width
3491 tools::Long nEngineWidth, nEngineHeight;
3492 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3494 tools::Long nNeededPixel = nEngineWidth;
3495 if (rParam.mbPixelToLogic)
3496 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3497 nNeededPixel += nLeftM + nRightM;
3499 if (!rParam.mbBreak || bShrink)
3501 // for break, the first GetOutputArea call is sufficient
3502 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3503 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3504 rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
3506 if ( bShrink )
3508 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3509 nLeftM, nTopM, nRightM, nBottomM, false,
3510 (rParam.meOrient), 0_deg100, rParam.mbPixelToLogic,
3511 nEngineWidth, nEngineHeight, nNeededPixel,
3512 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3514 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
3516 // First check if twice the space for the formatted text is available
3517 // (otherwise just keep it unchanged).
3519 const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
3520 const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
3521 if ( nAvailable >= 2 * nFormatted )
3523 // "repeat" is handled with unformatted text (for performance reasons)
3524 OUString aCellStr = rParam.mpEngine->GetText();
3526 tools::Long nRepeatSize = 0;
3527 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
3528 if ( pFmtDevice != mpRefDevice )
3529 ++nRepeatSize;
3530 if ( nRepeatSize > 0 )
3532 const tools::Long nRepeatCount = nAvailable / nRepeatSize;
3533 if ( nRepeatCount > 1 )
3535 OUStringBuffer aRepeated(aCellStr);
3536 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3537 aRepeated.append(aCellStr);
3539 nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3540 nNeededPixel, (nLeftM + nRightM ) );
3542 nEngineHeight = rParam.mpEngine->GetTextHeight();
3547 if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3549 nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) );
3551 // No clip marks if "###" doesn't fit (same as in DrawStrings)
3555 tools::Long nStartX = aAreaParam.maAlignRect.Left();
3556 const tools::Long nStartY = aAreaParam.maAlignRect.Top();
3557 const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3558 const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3559 const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3561 if (rParam.mbBreak)
3563 // text with automatic breaks is aligned only within the
3564 // edit engine's paper size, the output of the whole area
3565 // is always left-aligned
3567 nStartX += nLeftM;
3569 else
3571 if ( eOutHorJust == SvxCellHorJustify::Right )
3572 nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3573 else if ( eOutHorJust == SvxCellHorJustify::Center )
3574 nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3575 else
3576 nStartX += nLeftM;
3579 const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3580 if (bOutside)
3581 return;
3583 // output area, excluding margins, in logical units
3584 const Size aCellSize = rParam.mbPixelToLogic
3585 ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
3586 : Size( nOutWidth, nOutHeight );
3588 Point aURLStart;
3591 const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true );
3593 Point aLogicStart(nStartX, nStartY);
3594 rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
3596 aURLStart = aLogicStart; // copy before modifying for orientation
3598 if (rParam.meHorJustResult == SvxCellHorJustify::Block || rParam.mbBreak)
3600 Size aPSize = rParam.mpEngine->GetPaperSize();
3601 aPSize.setWidth( aCellSize.Height() );
3602 rParam.mpEngine->SetPaperSize(aPSize);
3603 aLogicStart.AdjustY(
3604 rParam.mbBreak ? aPSize.Width() : nEngineHeight );
3606 else
3608 // Note that the "paper" is rotated 90 degrees to the left, so
3609 // paper's width is in vertical direction. Also, the whole text
3610 // is on a single line, as text wrap is not in effect.
3612 // Set the paper width to be the width of the text.
3613 Size aPSize = rParam.mpEngine->GetPaperSize();
3614 aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
3615 rParam.mpEngine->SetPaperSize(aPSize);
3617 tools::Long nGap = 0;
3618 tools::Long nTopOffset = 0;
3619 if (rParam.mbPixelToLogic)
3621 nGap = mpRefDevice->LogicToPixel(aCellSize).Height() - mpRefDevice->LogicToPixel(aPSize).Width();
3622 nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
3623 nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
3625 else
3627 nGap = aCellSize.Height() - aPSize.Width();
3628 nTopOffset = nTopM;
3631 // First, align text to bottom.
3632 aLogicStart.AdjustY(aCellSize.Height() );
3633 aLogicStart.AdjustY(nTopOffset );
3635 switch (rParam.meVerJust)
3637 case SvxCellVerJustify::Standard:
3638 case SvxCellVerJustify::Bottom:
3639 // align to bottom (do nothing).
3640 break;
3641 case SvxCellVerJustify::Center:
3642 // center it.
3643 aLogicStart.AdjustY( -(nGap / 2) );
3644 break;
3645 case SvxCellVerJustify::Block:
3646 case SvxCellVerJustify::Top:
3647 // align to top
3648 aLogicStart.AdjustY( -nGap );
3649 break;
3650 default:
3655 rParam.mpEngine->Draw(*mpDev, aLogicStart, 900_deg10);
3658 rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3661 void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam)
3663 OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3665 const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3666 const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3668 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3670 //! mirror margin values for RTL?
3671 //! move margin down to after final GetOutputArea call
3672 tools::Long nTopM, nLeftM, nBottomM, nRightM;
3673 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3675 SCCOL nXForPos = rParam.mnX;
3676 if ( nXForPos < nX1 )
3678 nXForPos = nX1;
3679 rParam.mnPosX = rParam.mnInitPosX;
3681 SCSIZE nArrYForPos = rParam.mnArrY;
3682 if ( nArrYForPos < 1 )
3684 nArrYForPos = 1;
3685 rParam.mnPosY = nScrY;
3688 OutputAreaParam aAreaParam;
3690 // Initial page size - large for normal text, cell size for automatic line breaks
3692 Size aPaperSize( 1000000, 1000000 );
3693 if (rParam.hasLineBreak())
3695 // call GetOutputArea with nNeeded=0, to get only the cell width
3697 //! handle nArrY == 0
3698 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3699 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3700 rParam.mbCellIsValue, true, false, aAreaParam );
3702 //! special ScEditUtil handling if formatting for printer
3703 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3705 if (rParam.mbPixelToLogic)
3707 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3708 rParam.mpEngine->SetPaperSize(aLogicSize);
3710 else
3711 rParam.mpEngine->SetPaperSize(aPaperSize);
3713 // Fill the EditEngine (cell attributes and text)
3715 rParam.setPatternToEngine(mbUseStyleColor);
3716 rParam.setAlignmentToEngine();
3718 // Read content from cell
3720 bool bWrapFields = false;
3721 if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3722 // Failed to read cell content. Bail out.
3723 return;
3725 if ( mbSyntaxMode )
3726 SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3727 else if ( mbUseStyleColor && mbForceAutoColor )
3728 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
3730 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
3732 // Get final output area using the calculated width
3734 tools::Long nEngineWidth, nEngineHeight;
3735 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3737 tools::Long nNeededPixel = nEngineWidth;
3738 if (rParam.mbPixelToLogic)
3739 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3740 nNeededPixel += nLeftM + nRightM;
3742 if (!rParam.mbBreak || bShrink)
3744 // for break, the first GetOutputArea call is sufficient
3745 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3746 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3747 rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
3749 if ( bShrink )
3751 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3752 nLeftM, nTopM, nRightM, nBottomM, false,
3753 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
3754 nEngineWidth, nEngineHeight, nNeededPixel,
3755 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3757 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
3759 // First check if twice the space for the formatted text is available
3760 // (otherwise just keep it unchanged).
3762 const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
3763 const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
3764 if ( nAvailable >= 2 * nFormatted )
3766 // "repeat" is handled with unformatted text (for performance reasons)
3767 OUString aCellStr = rParam.mpEngine->GetText();
3769 tools::Long nRepeatSize = 0;
3770 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
3772 if ( pFmtDevice != mpRefDevice )
3773 ++nRepeatSize;
3774 if ( nRepeatSize > 0 )
3776 const tools::Long nRepeatCount = nAvailable / nRepeatSize;
3777 if ( nRepeatCount > 1 )
3779 OUStringBuffer aRepeated(aCellStr);
3780 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3781 aRepeated.append(aCellStr);
3783 nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3784 nNeededPixel, (nLeftM + nRightM ) );
3786 nEngineHeight = rParam.mpEngine->GetTextHeight();
3791 if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3793 nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) );
3795 // No clip marks if "###" doesn't fit (same as in DrawStrings)
3799 tools::Long nStartX = aAreaParam.maAlignRect.Left();
3800 const tools::Long nStartY = aAreaParam.maAlignRect.Top();
3801 const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3802 const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3803 const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3805 if (rParam.mbBreak)
3807 // text with automatic breaks is aligned only within the
3808 // edit engine's paper size, the output of the whole area
3809 // is always left-aligned
3811 nStartX += nLeftM;
3812 if (rParam.meHorJustResult == SvxCellHorJustify::Block)
3813 nStartX += aPaperSize.Height();
3815 else
3817 if ( eOutHorJust == SvxCellHorJustify::Right )
3818 nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3819 else if ( eOutHorJust == SvxCellHorJustify::Center )
3820 nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3821 else
3822 nStartX += nLeftM;
3825 const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3826 if (bOutside)
3827 return;
3829 // output area, excluding margins, in logical units
3830 const Size aCellSize = rParam.mbPixelToLogic
3831 ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
3832 : Size( nOutWidth, nOutHeight );
3834 Point aURLStart;
3837 const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false );
3839 Point aLogicStart(nStartX, nStartY);
3840 rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
3842 aURLStart = aLogicStart; // copy before modifying for orientation
3844 if (rParam.meHorJustResult != SvxCellHorJustify::Block)
3846 aLogicStart.AdjustX(nEngineWidth );
3847 if (!rParam.mbBreak)
3849 // Set the paper width to text size.
3850 Size aPSize = rParam.mpEngine->GetPaperSize();
3851 aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
3852 rParam.mpEngine->SetPaperSize(aPSize);
3854 tools::Long nGap = 0;
3855 tools::Long nTopOffset = 0; // offset by top margin
3856 if (rParam.mbPixelToLogic)
3858 nGap = mpRefDevice->LogicToPixel(aPSize).Width() - mpRefDevice->LogicToPixel(aCellSize).Height();
3859 nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
3860 nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
3862 else
3864 nGap = aPSize.Width() - aCellSize.Height();
3865 nTopOffset = nTopM;
3867 aLogicStart.AdjustY(nTopOffset );
3869 switch (rParam.meVerJust)
3871 case SvxCellVerJustify::Standard:
3872 case SvxCellVerJustify::Bottom:
3873 // align to bottom
3874 aLogicStart.AdjustY( -nGap );
3875 break;
3876 case SvxCellVerJustify::Center:
3877 // center it.
3878 aLogicStart.AdjustY( -(nGap / 2) );
3879 break;
3880 case SvxCellVerJustify::Block:
3881 case SvxCellVerJustify::Top:
3882 // align to top (do nothing)
3883 default:
3889 // bMoveClipped handling has been replaced by complete alignment
3890 // handling (also extending to the left).
3892 rParam.mpEngine->Draw(*mpDev, aLogicStart, 2700_deg10);
3895 rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3898 void ScOutputData::DrawEditStacked(DrawEditParam& rParam)
3900 OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3901 Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
3903 bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3904 bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3906 rParam.mbAsianVertical =
3907 lcl_GetBoolValue(*rParam.mpPattern, ATTR_VERTICAL_ASIAN, rParam.mpCondSet);
3909 if ( rParam.mbAsianVertical )
3911 // in asian mode, use EditEngine::SetVertical instead of EEControlBits::ONECHARPERLINE
3912 rParam.meOrient = SvxCellOrientation::Standard;
3913 DrawEditAsianVertical(rParam);
3914 return;
3917 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3919 //! mirror margin values for RTL?
3920 //! move margin down to after final GetOutputArea call
3921 tools::Long nTopM, nLeftM, nBottomM, nRightM;
3922 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3924 SCCOL nXForPos = rParam.mnX;
3925 if ( nXForPos < nX1 )
3927 nXForPos = nX1;
3928 rParam.mnPosX = rParam.mnInitPosX;
3930 SCSIZE nArrYForPos = rParam.mnArrY;
3931 if ( nArrYForPos < 1 )
3933 nArrYForPos = 1;
3934 rParam.mnPosY = nScrY;
3937 OutputAreaParam aAreaParam;
3939 // Initial page size - large for normal text, cell size for automatic line breaks
3941 Size aPaperSize( 1000000, 1000000 );
3942 // call GetOutputArea with nNeeded=0, to get only the cell width
3944 //! handle nArrY == 0
3945 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3946 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3947 rParam.mbCellIsValue, true, false, aAreaParam );
3949 //! special ScEditUtil handling if formatting for printer
3950 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3952 if (rParam.mbPixelToLogic)
3954 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3955 if ( rParam.mbBreak && mpRefDevice != pFmtDevice )
3957 // #i85342# screen display and formatting for printer,
3958 // use same GetEditArea call as in ScViewData::SetEditEngine
3960 Fraction aFract(1,1);
3961 tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
3962 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
3963 aLogicSize.setWidth( aUtilRect.GetWidth() );
3965 rParam.mpEngine->SetPaperSize(aLogicSize);
3967 else
3968 rParam.mpEngine->SetPaperSize(aPaperSize);
3970 // Fill the EditEngine (cell attributes and text)
3972 rParam.setPatternToEngine(mbUseStyleColor);
3973 rParam.setAlignmentToEngine();
3975 // Read content from cell
3977 bool bWrapFields = false;
3978 if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3979 // Failed to read cell content. Bail out.
3980 return;
3982 if ( mbSyntaxMode )
3983 SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3984 else if ( mbUseStyleColor && mbForceAutoColor )
3985 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
3987 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
3989 // Get final output area using the calculated width
3991 tools::Long nEngineWidth, nEngineHeight;
3992 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3994 tools::Long nNeededPixel = nEngineWidth;
3995 if (rParam.mbPixelToLogic)
3996 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3997 nNeededPixel += nLeftM + nRightM;
3999 if (bShrink)
4001 // for break, the first GetOutputArea call is sufficient
4002 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
4003 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4004 true, false, false, aAreaParam );
4006 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
4007 nLeftM, nTopM, nRightM, nBottomM, true,
4008 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
4009 nEngineWidth, nEngineHeight, nNeededPixel,
4010 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
4012 if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
4014 nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) );
4015 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4016 ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
4017 SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
4020 if ( eOutHorJust != SvxCellHorJustify::Left )
4022 aPaperSize.setWidth( nNeededPixel + 1 );
4023 if (rParam.mbPixelToLogic)
4024 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4025 else
4026 rParam.mpEngine->SetPaperSize(aPaperSize);
4030 tools::Long nStartX = aAreaParam.maAlignRect.Left();
4031 tools::Long nStartY = aAreaParam.maAlignRect.Top();
4032 tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
4033 tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
4034 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
4036 if (rParam.mbBreak)
4038 // text with automatic breaks is aligned only within the
4039 // edit engine's paper size, the output of the whole area
4040 // is always left-aligned
4042 nStartX += nLeftM;
4044 else
4046 if ( eOutHorJust == SvxCellHorJustify::Right )
4047 nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
4048 else if ( eOutHorJust == SvxCellHorJustify::Center )
4049 nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
4050 else
4051 nStartX += nLeftM;
4054 bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
4055 if (bOutside)
4056 return;
4058 // Also take fields in a cell with automatic breaks into account: clip to cell width
4059 bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
4060 bool bSimClip = false;
4062 Size aCellSize; // output area, excluding margins, in logical units
4063 if (rParam.mbPixelToLogic)
4064 aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
4065 else
4066 aCellSize = Size( nOutWidth, nOutHeight );
4068 if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
4070 const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
4071 bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
4073 // Don't clip for text height when printing rows with optimal height,
4074 // except when font size is from conditional formatting.
4075 //! Allow clipping when vertically merged?
4076 if ( eType != OUTTYPE_PRINTER ||
4077 ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
4078 ( rParam.mpCondSet && SfxItemState::SET ==
4079 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
4080 bClip = true;
4081 else
4082 bSimClip = true;
4084 // Show clip marks if height is at least 5pt too small and
4085 // there are several lines of text.
4086 // Not for asian vertical text, because that would interfere
4087 // with the default right position of the text.
4088 // Only with automatic line breaks, to avoid having to find
4089 // the cells with the horizontal end of the text again.
4090 if ( nEngineHeight - aCellSize.Height() > 100 &&
4091 rParam.mbBreak && bMarkClipped &&
4092 ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
4094 ScCellInfo* pClipMarkCell = nullptr;
4095 if ( bMerged )
4097 // anywhere in the merged area...
4098 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
4099 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
4101 else
4102 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
4104 pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
4105 bAnyClipped = true;
4107 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
4108 if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
4109 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
4113 Point aURLStart;
4115 { // Clip marks are already handled in GetOutputArea
4116 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
4117 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
4119 Point aLogicStart;
4120 if (rParam.mbPixelToLogic)
4121 aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
4122 else
4123 aLogicStart = Point(nStartX, nStartY);
4125 if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
4126 rParam.meVerJust==SvxCellVerJustify::Standard)
4128 //! if pRefDevice != pFmtDevice, keep heights in logic units,
4129 //! only converting margin?
4131 if (rParam.mbPixelToLogic)
4132 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
4133 mpRefDevice->LogicToPixel(aCellSize).Height() -
4134 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
4135 )).Height() );
4136 else
4137 aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
4139 else if (rParam.meVerJust==SvxCellVerJustify::Center)
4141 if (rParam.mbPixelToLogic)
4142 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
4143 mpRefDevice->LogicToPixel(aCellSize).Height() -
4144 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
4145 / 2)).Height() );
4146 else
4147 aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
4149 else // top
4151 if (rParam.mbPixelToLogic)
4152 aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
4153 else
4154 aLogicStart.AdjustY(nTopM );
4157 aURLStart = aLogicStart; // copy before modifying for orientation
4159 Size aPaperLogic = rParam.mpEngine->GetPaperSize();
4160 aPaperLogic.setWidth( nEngineWidth );
4161 rParam.mpEngine->SetPaperSize(aPaperLogic);
4163 // bMoveClipped handling has been replaced by complete alignment
4164 // handling (also extending to the left).
4166 if (bSimClip)
4168 // no hard clip, only draw the affected rows
4169 Point aDocStart = aClip.getRect().TopLeft();
4170 aDocStart -= aLogicStart;
4171 rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
4173 else
4175 rParam.mpEngine->Draw(*mpDev, aLogicStart);
4179 rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
4182 void ScOutputData::DrawEditAsianVertical(DrawEditParam& rParam)
4184 // When in asian vertical orientation, the orientation value is STANDARD,
4185 // and the asian vertical boolean is true.
4186 OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
4187 OSL_ASSERT(rParam.mbAsianVertical);
4188 OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
4190 Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
4192 bool bHidden = false;
4193 bool bShrink = !rParam.mbBreak && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
4194 Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
4196 if (nAttrRotate)
4198 //! set flag to find the cell in DrawRotated again ?
4199 //! (or flag already set during DrawBackground, then no query here)
4200 bHidden = true; // rotated is outputted separately
4203 // default alignment for asian vertical mode is top-right
4204 /* TODO: is setting meHorJustContext and meHorJustResult unconditionally to
4205 * SvxCellHorJustify::Right really wanted? Seems this was done all the time,
4206 * also before context was introduced and everything was attr only. */
4207 if ( rParam.meHorJustAttr == SvxCellHorJustify::Standard )
4208 rParam.meHorJustResult = rParam.meHorJustContext = SvxCellHorJustify::Right;
4210 if (bHidden)
4211 return;
4213 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
4215 //! mirror margin values for RTL?
4216 //! move margin down to after final GetOutputArea call
4217 tools::Long nTopM, nLeftM, nBottomM, nRightM;
4218 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
4220 SCCOL nXForPos = rParam.mnX;
4221 if ( nXForPos < nX1 )
4223 nXForPos = nX1;
4224 rParam.mnPosX = rParam.mnInitPosX;
4226 SCSIZE nArrYForPos = rParam.mnArrY;
4227 if ( nArrYForPos < 1 )
4229 nArrYForPos = 1;
4230 rParam.mnPosY = nScrY;
4233 OutputAreaParam aAreaParam;
4235 // Initial page size - large for normal text, cell size for automatic line breaks
4237 Size aPaperSize( 1000000, 1000000 );
4238 // call GetOutputArea with nNeeded=0, to get only the cell width
4240 //! handle nArrY == 0
4241 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
4242 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4243 rParam.mbCellIsValue, true, false, aAreaParam );
4245 //! special ScEditUtil handling if formatting for printer
4246 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
4248 if (rParam.mbPixelToLogic)
4250 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
4251 if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
4253 // #i85342# screen display and formatting for printer,
4254 // use same GetEditArea call as in ScViewData::SetEditEngine
4256 Fraction aFract(1,1);
4257 tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
4258 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
4259 aLogicSize.setWidth( aUtilRect.GetWidth() );
4261 rParam.mpEngine->SetPaperSize(aLogicSize);
4263 else
4264 rParam.mpEngine->SetPaperSize(aPaperSize);
4266 // Fill the EditEngine (cell attributes and text)
4268 // default alignment for asian vertical mode is top-right
4269 if ( rParam.meVerJust == SvxCellVerJustify::Standard )
4270 rParam.meVerJust = SvxCellVerJustify::Top;
4272 rParam.setPatternToEngine(mbUseStyleColor);
4273 rParam.setAlignmentToEngine();
4275 // Read content from cell
4277 bool bWrapFields = false;
4278 if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
4279 // Failed to read cell content. Bail out.
4280 return;
4282 if ( mbSyntaxMode )
4283 SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
4284 else if ( mbUseStyleColor && mbForceAutoColor )
4285 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
4287 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
4289 // Get final output area using the calculated width
4291 tools::Long nEngineWidth, nEngineHeight;
4292 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
4294 tools::Long nNeededPixel = nEngineWidth;
4295 if (rParam.mbPixelToLogic)
4296 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
4297 nNeededPixel += nLeftM + nRightM;
4299 // for break, the first GetOutputArea call is sufficient
4300 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
4301 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4302 rParam.mbCellIsValue || bShrink, false, false, aAreaParam );
4304 if ( bShrink )
4306 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
4307 nLeftM, nTopM, nRightM, nBottomM, false,
4308 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
4309 nEngineWidth, nEngineHeight, nNeededPixel,
4310 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
4312 if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
4314 nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) );
4315 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4316 ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
4317 SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
4320 if (eOutHorJust != SvxCellHorJustify::Left)
4322 aPaperSize.setWidth( nNeededPixel + 1 );
4323 if (rParam.mbPixelToLogic)
4324 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4325 else
4326 rParam.mpEngine->SetPaperSize(aPaperSize);
4329 tools::Long nStartX = aAreaParam.maAlignRect.Left();
4330 tools::Long nStartY = aAreaParam.maAlignRect.Top();
4331 tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
4332 tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
4333 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
4335 // text with automatic breaks is aligned only within the
4336 // edit engine's paper size, the output of the whole area
4337 // is always left-aligned
4339 nStartX += nLeftM;
4341 bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
4342 if (bOutside)
4343 return;
4345 // Also take fields in a cell with automatic breaks into account: clip to cell width
4346 bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
4347 bool bSimClip = false;
4349 Size aCellSize; // output area, excluding margins, in logical units
4350 if (rParam.mbPixelToLogic)
4351 aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
4352 else
4353 aCellSize = Size( nOutWidth, nOutHeight );
4355 if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
4357 const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
4358 bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
4360 // Don't clip for text height when printing rows with optimal height,
4361 // except when font size is from conditional formatting.
4362 //! Allow clipping when vertically merged?
4363 if ( eType != OUTTYPE_PRINTER ||
4364 ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
4365 ( rParam.mpCondSet && SfxItemState::SET ==
4366 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
4367 bClip = true;
4368 else
4369 bSimClip = true;
4371 // Show clip marks if height is at least 5pt too small and
4372 // there are several lines of text.
4373 // Not for asian vertical text, because that would interfere
4374 // with the default right position of the text.
4375 // Only with automatic line breaks, to avoid having to find
4376 // the cells with the horizontal end of the text again.
4377 if ( nEngineHeight - aCellSize.Height() > 100 &&
4378 ( rParam.mbBreak || rParam.meOrient == SvxCellOrientation::Stacked ) &&
4379 !rParam.mbAsianVertical && bMarkClipped &&
4380 ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
4382 ScCellInfo* pClipMarkCell = nullptr;
4383 if ( bMerged )
4385 // anywhere in the merged area...
4386 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
4387 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
4389 else
4390 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
4392 pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
4393 bAnyClipped = true;
4395 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
4396 if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
4397 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
4401 Point aURLStart;
4403 { // Clip marks are already handled in GetOutputArea
4404 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
4405 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
4407 Point aLogicStart;
4408 if (rParam.mbPixelToLogic)
4409 aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
4410 else
4411 aLogicStart = Point(nStartX, nStartY);
4413 tools::Long nAvailWidth = aCellSize.Width();
4414 // space for AutoFilter is already handled in GetOutputArea
4416 // horizontal alignment
4418 if (rParam.meHorJustResult==SvxCellHorJustify::Right)
4419 aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
4420 else if (rParam.meHorJustResult==SvxCellHorJustify::Center)
4421 aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
4423 // paper size is subtracted below
4424 aLogicStart.AdjustX(nEngineWidth );
4426 // vertical adjustment is within the EditEngine
4427 if (rParam.mbPixelToLogic)
4428 aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
4429 else
4430 aLogicStart.AdjustY(nTopM );
4432 aURLStart = aLogicStart; // copy before modifying for orientation
4434 // bMoveClipped handling has been replaced by complete alignment
4435 // handling (also extending to the left).
4437 // with SetVertical, the start position is top left of
4438 // the whole output area, not the text itself
4439 aLogicStart.AdjustX( -(rParam.mpEngine->GetPaperSize().Width()) );
4441 rParam.mpEngine->Draw(*mpDev, aLogicStart);
4444 rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
4447 void ScOutputData::DrawEdit(bool bPixelToLogic)
4449 vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
4450 bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
4452 InitOutputEditEngine();
4454 bool bHyphenatorSet = false;
4455 const ScPatternAttr* pOldPattern = nullptr;
4456 const SfxItemSet* pOldCondSet = nullptr;
4457 const SfxItemSet* pOldPreviewFontSet = nullptr;
4458 ScRefCellValue aCell;
4460 tools::Long nInitPosX = nScrX;
4461 if ( bLayoutRTL )
4463 nInitPosX += nMirrorW - 1;
4465 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4467 SCCOL nLastContentCol = mpDoc->MaxCol();
4468 if ( nX2 < mpDoc->MaxCol() )
4470 SCROW nEndRow;
4471 mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
4474 tools::Long nRowPosY = nScrY;
4475 for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 of the rest of the merged
4477 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
4479 if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
4481 if ( pThisRowInfo->bChanged || nArrY==0 )
4483 tools::Long nPosX = 0;
4484 for (SCCOL nX=0; nX<=nX2; nX++) // due to overflow
4486 std::unique_ptr< ScPatternAttr > pPreviewPattr;
4487 if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
4489 if (pThisRowInfo->basicCellInfo(nX).bEditEngine)
4491 SCROW nY = pThisRowInfo->nRowNo;
4493 bool bReopenTag = false;
4494 if (bTaggedPDF)
4495 bReopenTag = ReopenPDFStructureElement(vcl::PDFWriter::TableData, nY, nX);
4497 SCCOL nCellX = nX; // position where the cell really starts
4498 SCROW nCellY = nY;
4499 bool bDoCell = false;
4501 // if merged cell contains hidden row or column or both
4502 const ScMergeFlagAttr* pMergeFlag = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG);
4503 bool bOverlapped = (pMergeFlag->IsHorOverlapped() || pMergeFlag->IsVerOverlapped());
4505 tools::Long nPosY = nRowPosY;
4506 if (bOverlapped)
4508 nY = pRowInfo[nArrY].nRowNo;
4509 SCCOL nOverX; // start of the merged cells
4510 SCROW nOverY;
4511 if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, true ))
4513 nCellX = nOverX;
4514 nCellY = nOverY;
4515 bDoCell = true;
4518 else if ( nX == nX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() )
4520 // Rest of a long text further to the right?
4522 SCCOL nTempX=nX;
4523 while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
4524 ++nTempX;
4526 if ( nTempX > nX &&
4527 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
4528 !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
4530 nCellX = nTempX;
4531 bDoCell = true;
4534 else
4536 bDoCell = true;
4539 if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
4540 bDoCell = false;
4542 const ScPatternAttr* pPattern = nullptr;
4543 const SfxItemSet* pCondSet = nullptr;
4544 if (bDoCell)
4546 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
4547 !mpDoc->ColHidden(nCellX, nTab) )
4549 ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
4550 pPattern = rCellInfo.pPatternAttr;
4551 pCondSet = rCellInfo.pConditionSet;
4552 aCell = rCellInfo.maCell;
4554 else // get from document
4556 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
4557 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
4558 GetVisibleCell( nCellX, nCellY, nTab, aCell );
4560 if (aCell.isEmpty())
4561 bDoCell = false;
4563 if (bDoCell)
4565 if ( mpDoc->GetPreviewCellStyle() )
4567 if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
4569 pPreviewPattr.reset( new ScPatternAttr(*pPattern) );
4570 pPreviewPattr->SetStyleSheet(pPreviewStyle);
4571 pPattern = pPreviewPattr.get();
4574 SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab );
4575 lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
4577 // fdo#32530: Check if the first character is RTL.
4578 OUString aStr = mpDoc->GetString(nCellX, nCellY, nTab);
4580 DrawEditParam aParam(pPattern, pCondSet, lcl_SafeIsValue(aCell));
4581 const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
4582 aParam.meHorJustContext = getAlignmentFromContext( aParam.meHorJustAttr,
4583 aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText);
4584 aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ?
4585 SvxCellHorJustify::Block : aParam.meHorJustContext;
4586 aParam.mbPixelToLogic = bPixelToLogic;
4587 aParam.mbHyphenatorSet = bHyphenatorSet;
4588 aParam.mpEngine = mxOutputEditEngine.get();
4589 aParam.maCell = aCell;
4590 aParam.mnArrY = nArrY;
4591 aParam.mnX = nX;
4592 aParam.mnCellX = nCellX;
4593 aParam.mnCellY = nCellY;
4594 aParam.mnPosX = nPosX;
4595 aParam.mnPosY = nPosY;
4596 aParam.mnInitPosX = nInitPosX;
4597 aParam.mpPreviewFontSet = pPreviewFontSet;
4598 aParam.mpOldPattern = pOldPattern;
4599 aParam.mpOldCondSet = pOldCondSet;
4600 aParam.mpOldPreviewFontSet = pOldPreviewFontSet;
4601 aParam.mpThisRowInfo = pThisRowInfo;
4602 if (mpSpellCheckCxt)
4603 aParam.mpMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY);
4605 if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat)
4607 // ignore orientation/rotation if "repeat" is active
4608 aParam.meOrient = SvxCellOrientation::Standard;
4610 switch (aParam.meOrient)
4612 case SvxCellOrientation::BottomUp:
4613 DrawEditBottomTop(aParam);
4614 break;
4615 case SvxCellOrientation::TopBottom:
4616 DrawEditTopBottom(aParam);
4617 break;
4618 case SvxCellOrientation::Stacked:
4619 // this can be vertically stacked or asian vertical.
4620 DrawEditStacked(aParam);
4621 break;
4622 default:
4623 DrawEditStandard(aParam);
4626 // Retrieve parameters for next iteration.
4627 pOldPattern = aParam.mpOldPattern;
4628 pOldCondSet = aParam.mpOldCondSet;
4629 pOldPreviewFontSet = aParam.mpOldPreviewFontSet;
4630 bHyphenatorSet = aParam.mbHyphenatorSet;
4632 if (bReopenTag)
4633 pPDF->EndStructureElement();
4635 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
4638 nRowPosY += pRowInfo[nArrY].nHeight;
4641 if (mrTabInfo.maArray.HasCellRotation())
4643 DrawRotated(bPixelToLogic); //! call from outside ?
4647 void ScOutputData::DrawRotated(bool bPixelToLogic)
4649 InitOutputEditEngine();
4650 //! store nRotMax
4651 SCCOL nRotMax = nX2;
4652 for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
4653 if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
4654 nRotMax = pRowInfo[nRotY].nRotMaxCol;
4656 Color nConfBackColor = GetConfBackgroundColor();
4657 bool bCellContrast = mbUseStyleColor &&
4658 Application::GetSettings().GetStyleSettings().GetHighContrastMode();
4660 bool bHyphenatorSet = false;
4661 const ScPatternAttr* pPattern;
4662 const SfxItemSet* pCondSet;
4663 const ScPatternAttr* pOldPattern = nullptr;
4664 const SfxItemSet* pOldCondSet = nullptr;
4665 ScRefCellValue aCell;
4667 tools::Long nInitPosX = nScrX;
4668 if ( bLayoutRTL )
4670 nInitPosX += nMirrorW - 1;
4672 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4674 tools::Long nRowPosY = nScrY;
4675 for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 for the rest of the merged
4677 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
4678 tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight);
4679 if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
4681 if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
4683 tools::Long nPosX = 0;
4684 for (SCCOL nX=0; nX<=nRotMax; nX++)
4686 if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
4688 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
4689 if ( pInfo->nRotateDir != ScRotateDir::NONE )
4691 SCROW nY = pThisRowInfo->nRowNo;
4693 bool bHidden = false;
4694 if (bEditMode)
4695 if ( nX == nEditCol && nY == nEditRow )
4696 bHidden = true;
4698 if (!bHidden)
4700 lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
4702 tools::Long nPosY = nRowPosY;
4704 //! rest from merged cells further up do not work!
4706 bool bFromDoc = false;
4707 pPattern = pInfo->pPatternAttr;
4708 pCondSet = pInfo->pConditionSet;
4709 if (!pPattern)
4711 pPattern = mpDoc->GetPattern( nX, nY, nTab );
4712 bFromDoc = true;
4714 aCell = pInfo->maCell;
4715 if (bFromDoc)
4716 pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
4718 if (aCell.isEmpty() && nX>nX2)
4719 GetVisibleCell( nX, nY, nTab, aCell );
4721 if (aCell.isEmpty() || IsEmptyCellText(pThisRowInfo, nX, nY))
4722 bHidden = true; // nRotateDir is also set without a cell
4724 tools::Long nCellWidth = static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nX).nWidth);
4726 SvxCellHorJustify eHorJust =
4727 pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet).GetValue();
4728 bool bBreak = ( eHorJust == SvxCellHorJustify::Block ) ||
4729 pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue();
4730 bool bRepeat = ( eHorJust == SvxCellHorJustify::Repeat && !bBreak );
4731 bool bShrink = !bBreak && !bRepeat &&
4732 pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
4733 SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
4735 const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
4736 bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
4738 tools::Long nStartX = nPosX;
4739 tools::Long nStartY = nPosY;
4740 if (nX<nX1)
4742 if ((bBreak || eOrient!=SvxCellOrientation::Standard) && !bMerged)
4743 bHidden = true;
4744 else
4746 nStartX = nInitPosX;
4747 SCCOL nCol = nX1;
4748 while (nCol > nX)
4750 --nCol;
4751 nStartX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
4755 tools::Long nCellStartX = nStartX;
4757 // omit substitute representation of small text
4759 if (!bHidden)
4761 tools::Long nOutWidth = nCellWidth - 1;
4762 tools::Long nOutHeight = nCellHeight;
4764 if ( bMerged )
4766 SCCOL nCountX = pMerge->GetColMerge();
4767 for (SCCOL i=1; i<nCountX; i++)
4768 nOutWidth += mpDoc->GetColWidth(nX+i,nTab) * mnPPTX;
4769 SCROW nCountY = pMerge->GetRowMerge();
4770 nOutHeight += mpDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, mnPPTY);
4773 SvxCellVerJustify eVerJust =
4774 pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet).GetValue();
4776 // syntax mode is ignored here...
4778 // StringDiffer doesn't look at hyphenate, language items
4779 if ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet )
4781 auto pSet = std::make_unique<SfxItemSet>( mxOutputEditEngine->GetEmptyItemSet() );
4782 pPattern->FillEditItemSet( pSet.get(), pCondSet );
4784 // adjustment for EditEngine
4785 SvxAdjust eSvxAdjust = SvxAdjust::Left;
4786 if (eOrient==SvxCellOrientation::Stacked)
4787 eSvxAdjust = SvxAdjust::Center;
4788 // adjustment for bBreak is omitted here
4789 pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
4791 bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
4792 mxOutputEditEngine->SetDefaults( std::move(pSet) );
4793 pOldPattern = pPattern;
4794 pOldCondSet = pCondSet;
4796 EEControlBits nControl = mxOutputEditEngine->GetControlWord();
4797 if (eOrient==SvxCellOrientation::Stacked)
4798 nControl |= EEControlBits::ONECHARPERLINE;
4799 else
4800 nControl &= ~EEControlBits::ONECHARPERLINE;
4801 mxOutputEditEngine->SetControlWord( nControl );
4803 if ( !bHyphenatorSet && bParaHyphenate )
4805 // set hyphenator the first time it is needed
4806 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
4807 mxOutputEditEngine->SetHyphenator( xXHyphenator );
4808 bHyphenatorSet = true;
4811 Color aBackCol =
4812 pPattern->GetItem( ATTR_BACKGROUND, pCondSet ).GetColor();
4813 if ( mbUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
4814 aBackCol = nConfBackColor;
4815 mxOutputEditEngine->SetBackgroundColor( aBackCol );
4818 // margins
4820 //! change position and paper size to EditUtil !!!
4822 const SvxMarginItem* pMargin =
4823 &pPattern->GetItem(ATTR_MARGIN, pCondSet);
4824 sal_uInt16 nIndent = 0;
4825 if ( eHorJust == SvxCellHorJustify::Left )
4826 nIndent = pPattern->GetItem(ATTR_INDENT, pCondSet).GetValue();
4828 tools::Long nTotalHeight = nOutHeight; // without subtracting the margin
4829 if ( bPixelToLogic )
4830 nTotalHeight = mpRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height();
4832 tools::Long nLeftM = static_cast<tools::Long>( (pMargin->GetLeftMargin() + nIndent) * mnPPTX );
4833 tools::Long nTopM = static_cast<tools::Long>( pMargin->GetTopMargin() * mnPPTY );
4834 tools::Long nRightM = static_cast<tools::Long>( pMargin->GetRightMargin() * mnPPTX );
4835 tools::Long nBottomM = static_cast<tools::Long>( pMargin->GetBottomMargin() * mnPPTY );
4836 nStartX += nLeftM;
4837 nStartY += nTopM;
4838 nOutWidth -= nLeftM + nRightM;
4839 nOutHeight -= nTopM + nBottomM;
4841 // rotate here already, to adjust paper size for page breaks
4842 Degree100 nAttrRotate;
4843 double nSin = 0.0;
4844 double nCos = 1.0;
4845 SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
4846 if ( eOrient == SvxCellOrientation::Standard )
4848 nAttrRotate = pPattern->
4849 GetItem(ATTR_ROTATE_VALUE, pCondSet).GetValue();
4850 if ( nAttrRotate )
4852 eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
4854 // tdf#143377 To use the same limits to avoid too big Skew
4855 // with TextOrientation in Calc, use 1/2 degree here, too.
4856 // This equals '50' in the notation here (100th degree)
4857 static const sal_Int32 nMinRad(50);
4859 // bring nAttrRotate to the range [0..36000[
4860 nAttrRotate = Degree100(((nAttrRotate.get() % 36000) + 36000) % 36000);
4862 // check for to be avoided extreme values and correct
4863 if (nAttrRotate < Degree100(nMinRad))
4865 // range [0..50]
4866 nAttrRotate = Degree100(nMinRad);
4867 eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
4869 else if (nAttrRotate > Degree100(36000 - nMinRad))
4871 // range [35950..36000[
4872 nAttrRotate = Degree100(36000 - nMinRad);
4873 eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
4875 else if (nAttrRotate > Degree100(18000 - nMinRad) && (nAttrRotate < Degree100(18000 + nMinRad)))
4877 // range 50 around 18000, [17950..18050]
4878 nAttrRotate = (nAttrRotate > Degree100(18000))
4879 ? Degree100(18000 + nMinRad)
4880 : Degree100(18000 - nMinRad);
4881 eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
4884 if ( bLayoutRTL )
4886 // keep in range [0..36000[
4887 nAttrRotate = Degree100(36000 - nAttrRotate.get());
4890 double nRealOrient = toRadians(nAttrRotate); // 1/100 degree
4891 nCos = cos( nRealOrient );
4893 // tdf#143377 new strategy: instead of using zero for nSin, which
4894 // would be the *correct* value, continue with the corrected maximum
4895 // allowed value which is then *not* zero. This is similar to
4896 // the behaviour before where (just due to numerical unprecisions)
4897 // nSin was also not zero (pure coincidence), but very close to it.
4898 // I checked and tried to make safe all places below that use
4899 // nSin and divide by it, but there is too much going on and that
4900 // would not be safe, so rely on the same values as before, but
4901 // now numerically limited to not get the Skew go havoc
4902 nSin = sin( nRealOrient );
4906 Size aPaperSize( 1000000, 1000000 );
4907 if (eOrient==SvxCellOrientation::Stacked)
4908 aPaperSize.setWidth( nOutWidth ); // to center
4909 else if (bBreak)
4911 if (nAttrRotate)
4913 //! the correct paper size for break depends on the number
4914 //! of rows, as long as the rows can not be outputted individually
4915 //! offsetted -> therefore unlimited, so no wrapping.
4916 //! With offset rows the following would be correct:
4917 aPaperSize.setWidth( static_cast<tools::Long>(nOutHeight / fabs(nSin)) );
4919 else if (eOrient == SvxCellOrientation::Standard)
4920 aPaperSize.setWidth( nOutWidth );
4921 else
4922 aPaperSize.setWidth( nOutHeight - 1 );
4924 if (bPixelToLogic)
4925 mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4926 else
4927 mxOutputEditEngine->SetPaperSize(aPaperSize); // scale is always 1
4929 // read data from cell
4931 if (aCell.getType() == CELLTYPE_EDIT)
4933 if (aCell.getEditText())
4934 mxOutputEditEngine->SetTextCurrentDefaults(*aCell.getEditText());
4935 else
4937 OSL_FAIL("pData == 0");
4940 else
4942 sal_uInt32 nFormat = pPattern->GetNumberFormat(
4943 mpDoc->GetFormatTable(), pCondSet );
4944 const Color* pColor;
4945 OUString aString = ScCellFormat::GetString( aCell,
4946 nFormat, &pColor,
4947 nullptr,
4948 *mpDoc,
4949 mbShowNullValues,
4950 mbShowFormulas);
4952 mxOutputEditEngine->SetTextCurrentDefaults(aString);
4953 if ( pColor && !mbSyntaxMode && !( mbUseStyleColor && mbForceAutoColor ) )
4954 lcl_SetEditColor( *mxOutputEditEngine, *pColor );
4957 if ( mbSyntaxMode )
4959 SetEditSyntaxColor(*mxOutputEditEngine, aCell);
4961 else if ( mbUseStyleColor && mbForceAutoColor )
4962 lcl_SetEditColor( *mxOutputEditEngine, COL_AUTO ); //! or have a flag at EditEngine
4964 mxOutputEditEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
4966 tools::Long nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
4967 tools::Long nEngineHeight = mxOutputEditEngine->GetTextHeight();
4969 if (nAttrRotate && bBreak)
4971 double nAbsCos = fabs( nCos );
4972 double nAbsSin = fabs( nSin );
4974 // adjust width of papersize for height of text
4975 int nSteps = 5;
4976 while (nSteps > 0)
4978 // everything is in pixels
4979 tools::Long nEnginePixel = mpRefDevice->LogicToPixel(
4980 Size(0,nEngineHeight)).Height();
4981 tools::Long nEffHeight = nOutHeight - static_cast<tools::Long>(nEnginePixel * nAbsCos) + 2;
4982 tools::Long nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
4983 bool bFits = ( nNewWidth >= aPaperSize.Width() );
4984 if ( bFits )
4985 nSteps = 0;
4986 else
4988 if ( nNewWidth < 4 )
4990 // can't fit -> fall back to using half height
4991 nEffHeight = nOutHeight / 2;
4992 nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
4993 nSteps = 0;
4995 else
4996 --nSteps;
4998 // set paper width and get new text height
4999 aPaperSize.setWidth( nNewWidth );
5000 if (bPixelToLogic)
5001 mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
5002 else
5003 mxOutputEditEngine->SetPaperSize(aPaperSize); // Scale is always 1
5004 //mxOutputEditEngine->QuickFormatDoc( sal_True );
5006 nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
5007 nEngineHeight = mxOutputEditEngine->GetTextHeight();
5012 tools::Long nRealWidth = nEngineWidth;
5013 tools::Long nRealHeight = nEngineHeight;
5015 // when rotated, adjust size
5016 if (nAttrRotate)
5018 double nAbsCos = fabs( nCos );
5019 double nAbsSin = fabs( nSin );
5021 if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
5022 nEngineWidth = static_cast<tools::Long>( nRealWidth * nAbsCos +
5023 nRealHeight * nAbsSin );
5024 else
5025 nEngineWidth = static_cast<tools::Long>( nRealHeight / nAbsSin );
5026 //! limit !!!
5028 nEngineHeight = static_cast<tools::Long>( nRealHeight * nAbsCos +
5029 nRealWidth * nAbsSin );
5032 if (!nAttrRotate) // only rotated text here
5033 bHidden = true; //! check first !!!
5035 //! omit which doesn't stick out
5037 if (!bHidden)
5039 Size aClipSize( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY );
5041 // go on writing
5043 Size aCellSize;
5044 if (bPixelToLogic)
5045 aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
5046 else
5047 aCellSize = Size( nOutWidth, nOutHeight ); // scale is one
5049 tools::Long nGridWidth = nEngineWidth;
5050 bool bNegative = false;
5051 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
5053 nGridWidth = aCellSize.Width() +
5054 std::abs(static_cast<tools::Long>( aCellSize.Height() * nCos / nSin ));
5055 bNegative = ( pInfo->nRotateDir == ScRotateDir::Left );
5056 if ( bLayoutRTL )
5057 bNegative = !bNegative;
5060 // use GetOutputArea to hide the grid
5061 // (clip region is done manually below)
5062 OutputAreaParam aAreaParam;
5064 SCCOL nCellX = nX;
5065 SCROW nCellY = nY;
5066 SvxCellHorJustify eOutHorJust = eHorJust;
5067 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
5068 eOutHorJust = bNegative ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
5069 tools::Long nNeededWidth = nGridWidth; // in pixel for GetOutputArea
5070 if ( bPixelToLogic )
5071 nNeededWidth = mpRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width();
5073 GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth,
5074 *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
5075 false, false, true, aAreaParam );
5077 if ( bShrink )
5079 tools::Long nPixelWidth = bPixelToLogic ?
5080 mpRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth;
5081 tools::Long nNeededPixel = nPixelWidth + nLeftM + nRightM;
5083 aAreaParam.mbLeftClip = aAreaParam.mbRightClip = true;
5085 // always do height
5086 ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
5087 false, eOrient, nAttrRotate, bPixelToLogic,
5088 nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
5090 if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
5092 // do width only if rotating within the cell (standard mode)
5093 ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
5094 true, eOrient, nAttrRotate, bPixelToLogic,
5095 nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
5098 // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine
5099 // (but width is only valid for standard mode)
5100 nRealWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
5101 nRealHeight = mxOutputEditEngine->GetTextHeight();
5103 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
5104 nEngineWidth = static_cast<tools::Long>( nRealHeight / fabs( nSin ) );
5107 tools::Long nClipStartX = nStartX;
5108 if (nX<nX1)
5110 //! clipping is not needed when on the left side of the window
5112 if (nStartX<nScrX)
5114 tools::Long nDif = nScrX - nStartX;
5115 nClipStartX = nScrX;
5116 aClipSize.AdjustWidth( -nDif );
5120 tools::Long nClipStartY = nStartY;
5121 if (nArrY==0 && nClipStartY < nRowPosY )
5123 tools::Long nDif = nRowPosY - nClipStartY;
5124 nClipStartY = nRowPosY;
5125 aClipSize.AdjustHeight( -nDif );
5128 if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ )
5130 // only clip rotated output text at the page border
5131 nClipStartX = nScrX;
5132 aClipSize.setWidth( nScrW );
5135 if (bPixelToLogic)
5136 aAreaParam.maClipRect = mpRefDevice->PixelToLogic( tools::Rectangle(
5137 Point(nClipStartX,nClipStartY), aClipSize ) );
5138 else
5139 aAreaParam.maClipRect = tools::Rectangle(Point(nClipStartX, nClipStartY),
5140 aClipSize ); // Scale = 1
5142 if (bMetaFile)
5144 mpDev->Push();
5145 mpDev->IntersectClipRegion( aAreaParam.maClipRect );
5147 else
5148 mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
5150 Point aLogicStart;
5151 if (bPixelToLogic)
5152 aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
5153 else
5154 aLogicStart = Point(nStartX, nStartY);
5155 if ( eOrient!=SvxCellOrientation::Standard || !bBreak )
5157 tools::Long nAvailWidth = aCellSize.Width();
5158 if (eType==OUTTYPE_WINDOW &&
5159 eOrient!=SvxCellOrientation::Stacked &&
5160 pInfo->bAutoFilter)
5162 // filter drop-down width depends on row height
5163 double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
5164 fZoom = fZoom > 1.0 ? fZoom : 1.0;
5165 if (bPixelToLogic)
5166 nAvailWidth -= mpRefDevice->PixelToLogic(Size(0,fZoom * DROPDOWN_BITMAP_SIZE)).Height();
5167 else
5168 nAvailWidth -= fZoom * DROPDOWN_BITMAP_SIZE;
5169 tools::Long nComp = nEngineWidth;
5170 if (nAvailWidth<nComp) nAvailWidth=nComp;
5173 // horizontal orientation
5175 if (eOrient==SvxCellOrientation::Standard && !nAttrRotate)
5177 if (eHorJust==SvxCellHorJustify::Right ||
5178 eHorJust==SvxCellHorJustify::Center)
5180 mxOutputEditEngine->SetUpdateLayout( false );
5182 SvxAdjust eSvxAdjust =
5183 (eHorJust==SvxCellHorJustify::Right) ?
5184 SvxAdjust::Right : SvxAdjust::Center;
5185 mxOutputEditEngine->SetDefaultItem(
5186 SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
5188 aPaperSize.setWidth( nOutWidth );
5189 if (bPixelToLogic)
5190 mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
5191 else
5192 mxOutputEditEngine->SetPaperSize(aPaperSize);
5194 mxOutputEditEngine->SetUpdateLayout( true );
5197 else
5199 // rotated text is centered by default
5200 if (eHorJust==SvxCellHorJustify::Right)
5201 aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
5202 else if (eHorJust==SvxCellHorJustify::Center ||
5203 eHorJust==SvxCellHorJustify::Standard)
5204 aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
5208 if ( bLayoutRTL )
5210 if (bPixelToLogic)
5211 aLogicStart.AdjustX( -(mpRefDevice->PixelToLogic(
5212 Size( nCellWidth, 0 ) ).Width()) );
5213 else
5214 aLogicStart.AdjustX( -nCellWidth );
5217 if ( eOrient==SvxCellOrientation::Standard ||
5218 eOrient==SvxCellOrientation::Stacked || !bBreak )
5220 if (eVerJust==SvxCellVerJustify::Bottom ||
5221 eVerJust==SvxCellVerJustify::Standard)
5223 if (bPixelToLogic)
5224 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,
5225 mpRefDevice->LogicToPixel(aCellSize).Height() -
5226 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
5227 )).Height() );
5228 else
5229 aLogicStart.AdjustY(aCellSize.Height() - nEngineHeight );
5232 else if (eVerJust==SvxCellVerJustify::Center)
5234 if (bPixelToLogic)
5235 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,(
5236 mpRefDevice->LogicToPixel(aCellSize).Height() -
5237 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height())
5238 / 2)).Height() );
5239 else
5240 aLogicStart.AdjustY((aCellSize.Height() - nEngineHeight) / 2 );
5244 // TOPBOTTOM and BOTTOMTOP are handled in DrawStrings/DrawEdit
5245 OSL_ENSURE( eOrient == SvxCellOrientation::Standard && nAttrRotate,
5246 "DrawRotated: no rotation" );
5248 Degree10 nOriVal = 0_deg10;
5249 if ( nAttrRotate )
5251 // attribute is 1/100, Font 1/10 degrees
5252 nOriVal = to<Degree10>(nAttrRotate);
5254 double nAddX = 0.0;
5255 double nAddY = 0.0;
5256 if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD )
5258 //! limit !!!
5259 double nH = nRealHeight * nCos;
5260 nAddX += nH * ( nCos / fabs(nSin) );
5262 if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD )
5263 nAddX -= nRealWidth * nCos;
5264 if ( nSin < 0.0 )
5265 nAddX -= nRealHeight * nSin;
5266 if ( nSin > 0.0 )
5267 nAddY += nRealWidth * nSin;
5268 if ( nCos < 0.0 )
5269 nAddY -= nRealHeight * nCos;
5271 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
5273 //! limit !!!
5274 double nSkew = nTotalHeight * nCos / fabs(nSin);
5275 if ( eRotMode == SVX_ROTATE_MODE_CENTER )
5276 nAddX -= nSkew * 0.5;
5277 if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) ||
5278 ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) )
5279 nAddX -= nSkew;
5281 tools::Long nUp = 0;
5282 if ( eVerJust == SvxCellVerJustify::Center )
5283 nUp = ( aCellSize.Height() - nEngineHeight ) / 2;
5284 else if ( eVerJust == SvxCellVerJustify::Top )
5286 if ( nSin > 0.0 )
5287 nUp = aCellSize.Height() - nEngineHeight;
5289 else // BOTTOM / STANDARD
5291 if ( nSin < 0.0 )
5292 nUp = aCellSize.Height() - nEngineHeight;
5294 if ( nUp )
5295 nAddX += ( nUp * nCos / fabs(nSin) );
5298 aLogicStart.AdjustX(static_cast<tools::Long>(nAddX) );
5299 aLogicStart.AdjustY(static_cast<tools::Long>(nAddY) );
5302 // bSimClip is not used here (because nOriVal is set)
5304 mxOutputEditEngine->Draw(*mpDev, aLogicStart, nOriVal);
5306 if (bMetaFile)
5307 mpDev->Pop();
5308 else
5309 mpDev->SetClipRegion();
5314 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
5317 nRowPosY += pRowInfo[nArrY].nHeight;
5321 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */