xref: /core/sc/source/ui/view/output2.cxx (revision f9395a12)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <scitems.hxx>
21 #include <editeng/eeitem.hxx>
22 
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/vcllayout.hxx>
58 #include <vcl/glyphitemcache.hxx>
59 #include <sal/log.hxx>
60 #include <unotools/charclass.hxx>
61 #include <osl/diagnose.h>
62 #include <tools/stream.hxx>
63 
64 #include <output.hxx>
65 #include <document.hxx>
66 #include <formulacell.hxx>
67 #include <attrib.hxx>
68 #include <patattr.hxx>
69 #include <cellform.hxx>
70 #include <editutil.hxx>
71 #include <progress.hxx>
72 #include <scmod.hxx>
73 #include <fillinfo.hxx>
74 #include <stlsheet.hxx>
75 #include <spellcheckcontext.hxx>
76 #include <scopetools.hxx>
77 
78 #include <com/sun/star/i18n/DirectionProperty.hpp>
79 #include <comphelper/scopeguard.hxx>
80 #include <comphelper/string.hxx>
81 
82 #include <memory>
83 #include <vector>
84 #include <o3tl/lru_map.hxx>
85 #include <o3tl/hash_combine.hxx>
86 
87 #include <math.h>
88 
89 using namespace com::sun::star;
90 
91 //! Merge Autofilter width with column.cxx
92 #define DROPDOWN_BITMAP_SIZE        18
93 
94 #define DRAWTEXT_MAX    32767
95 
96 const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;
97 constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
98 
99 class ScDrawStringsVars
100 {
101     ScOutputData*       pOutput;                // connection
102 
103     const ScPatternAttr* pPattern;              // attribute
104     const SfxItemSet*   pCondSet;               // from conditional formatting
105 
106     vcl::Font           aFont;                  // created from attributes
107     FontMetric          aMetric;
108     tools::Long                nAscentPixel;           // always pixels
109     SvxCellOrientation  eAttrOrient;
110     SvxCellHorJustify   eAttrHorJust;
111     SvxCellVerJustify   eAttrVerJust;
112     SvxCellJustifyMethod eAttrHorJustMethod;
113     const SvxMarginItem* pMargin;
114     sal_uInt16          nIndent;
115     bool                bRotated;
116 
117     OUString            aString;                // contents
118     Size                aTextSize;
119     tools::Long                nOriginalWidth;
120     tools::Long                nMaxDigitWidth;
121     tools::Long                nSignWidth;
122     tools::Long                nDotWidth;
123     tools::Long                nExpWidth;
124 
125     ScRefCellValue      maLastCell;
126     sal_uLong           nValueFormat;
127     bool                bLineBreak;
128     bool                bRepeat;
129     bool                bShrink;
130 
131     bool                bPixelToLogic;
132     bool                bCellContrast;
133 
134     Color               aBackConfigColor;       // used for ScPatternAttr::GetFont calls
135     Color               aTextConfigColor;
136     sal_Int32           nRepeatPos;
137     sal_Unicode         nRepeatChar;
138 
139 public:
140                 ScDrawStringsVars(ScOutputData* pData, bool bPTL);
141 
142                 //  SetPattern = ex-SetVars
143                 //  SetPatternSimple: without Font
144 
145     void SetPattern(
146         const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
147         SvtScriptType nScript );
148 
149     void        SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );
150 
151     bool SetText( const ScRefCellValue& rCell );   // TRUE -> drop pOldPattern
152     void        SetHashText();
153     void SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth );
154     void        SetAutoText( const OUString& rAutoText );
155 
156     SvxCellOrientation      GetOrient() const        { return eAttrOrient; }
157     SvxCellHorJustify       GetHorJust() const       { return eAttrHorJust; }
158     SvxCellVerJustify       GetVerJust() const       { return eAttrVerJust; }
159     SvxCellJustifyMethod    GetHorJustMethod() const { return eAttrHorJustMethod; }
160     const SvxMarginItem*    GetMargin() const        { return pMargin; }
161 
162     sal_uInt16              GetLeftTotal() const     { return pMargin->GetLeftMargin() + nIndent; }
163     sal_uInt16              GetRightTotal() const    { return pMargin->GetRightMargin() + nIndent; }
164 
165     const OUString&         GetString() const        { return aString; }
166     const Size&             GetTextSize() const      { return aTextSize; }
167     tools::Long                    GetOriginalWidth() const { return nOriginalWidth; }
168     tools::Long             GetFmtTextWidth(const OUString& rString);
169 
170     // Get the effective number format, including formula result types.
171     // This assumes that a formula cell has already been calculated.
172     sal_uLong GetResultValueFormat() const { return nValueFormat;}
173 
174     bool    GetLineBreak() const                    { return bLineBreak; }
175     bool    IsRepeat() const                        { return bRepeat; }
176     bool    IsShrink() const                        { return bShrink; }
177     void        RepeatToFill( tools::Long nColWidth );
178 
179     tools::Long    GetAscent() const   { return nAscentPixel; }
180     bool    IsRotated() const   { return bRotated; }
181 
182     void    SetShrinkScale( tools::Long nScale, SvtScriptType nScript );
183 
184     bool    HasCondHeight() const   { return pCondSet && SfxItemState::SET ==
185                                         pCondSet->GetItemState( ATTR_FONT_HEIGHT ); }
186 
187     bool    HasEditCharacters() const;
188 
189     // ScOutputData::LayoutStrings() usually triggers a number of calls that require
190     // to lay out the text, which is relatively slow, so cache that operation.
191     const SalLayoutGlyphs*  GetLayoutGlyphs(const OUString& rString) const
192     {
193         return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString);
194     }
195 
196 private:
197     tools::Long        GetMaxDigitWidth();     // in logic units
198     tools::Long        GetSignWidth();
199     tools::Long        GetDotWidth();
200     tools::Long        GetExpWidth();
201     void        TextChanged();
202 };
203 
204 ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, bool bPTL) :
205     pOutput     ( pData ),
206     pPattern    ( nullptr ),
207     pCondSet    ( nullptr ),
208     nAscentPixel(0),
209     eAttrOrient ( SvxCellOrientation::Standard ),
210     eAttrHorJust( SvxCellHorJustify::Standard ),
211     eAttrVerJust( SvxCellVerJustify::Bottom ),
212     eAttrHorJustMethod( SvxCellJustifyMethod::Auto ),
213     pMargin     ( nullptr ),
214     nIndent     ( 0 ),
215     bRotated    ( false ),
216     nOriginalWidth( 0 ),
217     nMaxDigitWidth( 0 ),
218     nSignWidth( 0 ),
219     nDotWidth( 0 ),
220     nExpWidth( 0 ),
221     nValueFormat( 0 ),
222     bLineBreak  ( false ),
223     bRepeat     ( false ),
224     bShrink     ( false ),
225     bPixelToLogic( bPTL ),
226     nRepeatPos( -1 ),
227     nRepeatChar( 0x0 )
228 {
229     ScModule* pScMod = SC_MOD();
230     bCellContrast = pOutput->mbUseStyleColor &&
231             Application::GetSettings().GetStyleSettings().GetHighContrastMode();
232 
233     const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig();
234     aBackConfigColor = rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
235     aTextConfigColor = rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor;
236 }
237 
238 void ScDrawStringsVars::SetShrinkScale( tools::Long nScale, SvtScriptType nScript )
239 {
240     // text remains valid, size is updated
241 
242     OutputDevice* pDev = pOutput->mpDev;
243     OutputDevice* pRefDevice = pOutput->mpRefDevice;
244     OutputDevice* pFmtDevice = pOutput->pFmtDevice;
245 
246     // call GetFont with a modified fraction, use only the height
247 
248     Fraction aFraction( nScale, 100 );
249     if ( !bPixelToLogic )
250         aFraction *= pOutput->aZoomY;
251     vcl::Font aTmpFont;
252     pPattern->GetFont( aTmpFont, SC_AUTOCOL_RAW, pFmtDevice, &aFraction, pCondSet, nScript );
253     tools::Long nNewHeight = aTmpFont.GetFontHeight();
254     if ( nNewHeight > 0 )
255         aFont.SetFontHeight( nNewHeight );
256 
257     // set font and dependent variables as in SetPattern
258 
259     pDev->SetFont( aFont );
260     if ( pFmtDevice != pDev )
261         pFmtDevice->SetFont( aFont );
262 
263     aMetric = pFmtDevice->GetFontMetric();
264     if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
265     {
266         OutputDevice* pDefaultDev = Application::GetDefaultDevice();
267         MapMode aOld = pDefaultDev->GetMapMode();
268         pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
269         aMetric = pDefaultDev->GetFontMetric( aFont );
270         pDefaultDev->SetMapMode( aOld );
271     }
272 
273     nAscentPixel = aMetric.GetAscent();
274     if ( bPixelToLogic )
275         nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
276 
277     SetAutoText( aString );     // same text again, to get text size
278 }
279 
280 namespace {
281 
282 template<typename ItemType, typename EnumType>
283 EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
284 {
285     const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet));
286     return static_cast<EnumType>(rItem.GetValue());
287 }
288 
289 bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
290 {
291     return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet);
292 }
293 
294 }
295 
296 static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB nTab )
297 {
298     sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, nTab );
299     SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
300     return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT;
301 }
302 
303 void ScDrawStringsVars::SetPattern(
304     const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
305     SvtScriptType nScript )
306 {
307     nMaxDigitWidth = 0;
308     nSignWidth     = 0;
309     nDotWidth      = 0;
310     nExpWidth      = 0;
311 
312     pPattern = pNew;
313     pCondSet = pSet;
314 
315     // evaluate pPattern
316 
317     OutputDevice* pDev = pOutput->mpDev;
318     OutputDevice* pRefDevice = pOutput->mpRefDevice;
319     OutputDevice* pFmtDevice = pOutput->pFmtDevice;
320 
321     // font
322 
323     ScAutoFontColorMode eColorMode;
324     if ( pOutput->mbUseStyleColor )
325     {
326         if ( pOutput->mbForceAutoColor )
327             eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREALL : SC_AUTOCOL_IGNOREFONT;
328         else
329             eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREBACK : SC_AUTOCOL_DISPLAY;
330     }
331     else
332         eColorMode = SC_AUTOCOL_PRINT;
333 
334     if ( bPixelToLogic )
335         pPattern->GetFont( aFont, eColorMode, pFmtDevice, nullptr, pCondSet, nScript,
336                             &aBackConfigColor, &aTextConfigColor );
337     else
338         pPattern->GetFont( aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript,
339                             &aBackConfigColor, &aTextConfigColor );
340     aFont.SetAlignment(ALIGN_BASELINE);
341 
342     // orientation
343 
344     eAttrOrient = pPattern->GetCellOrientation( pCondSet );
345 
346     //  alignment
347 
348     eAttrHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
349 
350     eAttrVerJust = pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet ).GetValue();
351     if ( eAttrVerJust == SvxCellVerJustify::Standard )
352         eAttrVerJust = SvxCellVerJustify::Bottom;
353 
354     // justification method
355 
356     eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet);
357 
358     //  line break
359 
360     bLineBreak = pPattern->GetItem( ATTR_LINEBREAK, pCondSet ).GetValue();
361 
362     //  handle "repeat" alignment
363 
364     bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat );
365     if ( bRepeat )
366     {
367         // "repeat" disables rotation (before constructing the font)
368         eAttrOrient = SvxCellOrientation::Standard;
369 
370         // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
371         if ( bLineBreak )
372             eAttrHorJust = SvxCellHorJustify::Standard;
373     }
374 
375     sal_Int16 nRot;
376     switch (eAttrOrient)
377     {
378         case SvxCellOrientation::Standard:
379             nRot = 0;
380             bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 &&
381                        !bRepeat;
382             break;
383         case SvxCellOrientation::Stacked:
384             nRot = 0;
385             bRotated = false;
386             break;
387         case SvxCellOrientation::TopBottom:
388             nRot = 2700;
389             bRotated = false;
390             break;
391         case SvxCellOrientation::BottomUp:
392             nRot = 900;
393             bRotated = false;
394             break;
395         default:
396             OSL_FAIL("Invalid SvxCellOrientation value");
397             nRot = 0;
398             bRotated = false;
399             break;
400     }
401     aFont.SetOrientation( Degree10(nRot) );
402 
403     // syntax mode
404 
405     if (pOutput->mbSyntaxMode)
406         pOutput->SetSyntaxColor(&aFont, rCell);
407 
408     // There is no cell attribute for kerning, default is kerning OFF, all
409     // kerning is stored at an EditText object that is drawn using EditEngine.
410     aFont.SetKerning( FontKerning::NONE);
411 
412     pDev->SetFont( aFont );
413     if ( pFmtDevice != pDev )
414         pFmtDevice->SetFont( aFont );
415 
416     aMetric = pFmtDevice->GetFontMetric();
417 
418     // if there is the leading 0 on a printer device, we have problems
419     // -> take metric from the screen (as for EditEngine!)
420     if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
421     {
422         OutputDevice* pDefaultDev = Application::GetDefaultDevice();
423         MapMode aOld = pDefaultDev->GetMapMode();
424         pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
425         aMetric = pDefaultDev->GetFontMetric( aFont );
426         pDefaultDev->SetMapMode( aOld );
427     }
428 
429     nAscentPixel = aMetric.GetAscent();
430     if ( bPixelToLogic )
431         nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
432 
433     Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() );
434     pDev->SetTextLineColor( aULineColor );
435 
436     Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() );
437     pDev->SetOverlineColor( aOLineColor );
438 
439     // number format
440 
441     nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
442 
443     // margins
444     pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
445     if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right )
446         nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
447     else
448         nIndent = 0;
449 
450     // "Shrink to fit"
451 
452     bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
453 
454     // at least the text size needs to be retrieved again
455     //! differentiate and do not get the text again from the number format?
456     maLastCell.clear();
457 }
458 
459 void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet )
460 {
461     nMaxDigitWidth = 0;
462     nSignWidth     = 0;
463     nDotWidth      = 0;
464     nExpWidth      = 0;
465 
466     // Is called, when the font variables do not change (!StringDiffer)
467 
468     pPattern = pNew;
469     pCondSet = pSet;        //! is this needed ???
470 
471     // number format
472 
473     sal_uLong nOld = nValueFormat;
474     nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
475 
476     if (nValueFormat != nOld)
477         maLastCell.clear();           // always reformat
478 
479     // margins
480 
481     pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
482 
483     if ( eAttrHorJust == SvxCellHorJustify::Left )
484         nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
485     else
486         nIndent = 0;
487 
488     // "Shrink to fit"
489 
490     bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
491 }
492 
493 static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell )
494 {
495     return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE &&
496         rCell.getDouble() == rOldCell.getDouble();
497 }
498 
499 bool ScDrawStringsVars::SetText( const ScRefCellValue& rCell )
500 {
501     bool bChanged = false;
502 
503     if (!rCell.isEmpty())
504     {
505         if (!SameValue(rCell, maLastCell))
506         {
507             maLastCell = rCell;          // store cell
508 
509             const Color* pColor;
510             sal_uLong nFormat = nValueFormat;
511             aString = ScCellFormat::GetString( rCell,
512                                      nFormat, &pColor,
513                                      *pOutput->mpDoc->GetFormatTable(),
514                                      *pOutput->mpDoc,
515                                      pOutput->mbShowNullValues,
516                                      pOutput->mbShowFormulas,
517                                      true );
518             if ( nFormat )
519             {
520                 nRepeatPos = aString.indexOf( 0x1B );
521                 if ( nRepeatPos != -1 )
522                 {
523                     if (nRepeatPos + 1 == aString.getLength())
524                         nRepeatPos = -1;
525                     else
526                     {
527                         nRepeatChar = aString[ nRepeatPos + 1 ];
528                         // delete placeholder and char to repeat
529                         aString = aString.replaceAt( nRepeatPos, 2, u"" );
530                         // Do not cache/reuse a repeat-filled string, column
531                         // widths or fonts or sizes may differ.
532                         maLastCell.clear();
533                     }
534                 }
535             }
536             else
537             {
538                 nRepeatPos = -1;
539                 nRepeatChar = 0x0;
540             }
541             if (aString.getLength() > DRAWTEXT_MAX)
542                 aString = aString.copy(0, DRAWTEXT_MAX);
543 
544             if ( pColor && !pOutput->mbSyntaxMode && !( pOutput->mbUseStyleColor && pOutput->mbForceAutoColor ) )
545             {
546                 OutputDevice* pDev = pOutput->mpDev;
547                 aFont.SetColor(*pColor);
548                 pDev->SetFont( aFont );   // only for output
549                 bChanged = true;
550                 maLastCell.clear();       // next time return here again
551             }
552 
553             TextChanged();
554         }
555         // otherwise keep string/size
556     }
557     else
558     {
559         aString.clear();
560         maLastCell.clear();
561         aTextSize = Size(0,0);
562         nOriginalWidth = 0;
563     }
564 
565     return bChanged;
566 }
567 
568 void ScDrawStringsVars::SetHashText()
569 {
570     SetAutoText("###");
571 }
572 
573 void ScDrawStringsVars::RepeatToFill( tools::Long nColWidth )
574 {
575     if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() )
576         return;
577 
578     tools::Long nCharWidth = GetFmtTextWidth(OUString(nRepeatChar));
579 
580     if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) )
581         return;
582 
583     // Are there restrictions on the cell type we should filter out here ?
584     tools::Long nTextWidth = aTextSize.Width();
585     if ( bPixelToLogic )
586     {
587         nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
588         nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
589     }
590 
591     tools::Long nSpaceToFill = nColWidth - nTextWidth;
592     if ( nSpaceToFill <= nCharWidth )
593         return;
594 
595     sal_Int32 nCharsToInsert = nSpaceToFill / nCharWidth;
596     OUStringBuffer aFill(nCharsToInsert);
597     comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
598     aString = aString.replaceAt( nRepeatPos, 0, aFill );
599     TextChanged();
600 }
601 
602 void ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth )
603 {
604     // #i113045# do the single-character width calculations in logic units
605     if (bPixelToLogic)
606         nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
607 
608     CellType eType = rCell.getType();
609     if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA)
610         // must be a value or formula cell.
611         return;
612 
613     if (eType == CELLTYPE_FORMULA)
614     {
615         ScFormulaCell* pFCell = rCell.getFormula();
616         if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
617         {
618             SetHashText();      // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
619             return;
620         }
621         // If it's formula, the result must be a value.
622         if (!pFCell->IsValue())
623             return;
624     }
625 
626     sal_uLong nFormat = GetResultValueFormat();
627     if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
628     {
629         // Not 'General' number format.  Set hash text and bail out.
630         SetHashText();
631         return;
632     }
633 
634     double fVal = rCell.getValue();
635 
636     const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat);
637     if (!pNumFormat)
638         return;
639 
640     tools::Long nMaxDigit = GetMaxDigitWidth();
641     if (!nMaxDigit)
642         return;
643 
644     sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
645     {
646         OUString sTempOut(aString);
647         if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
648         {
649             aString = sTempOut;
650             // Failed to get output string.  Bail out.
651             return;
652         }
653         aString = sTempOut;
654     }
655     sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
656     sal_Int32 nLen = aString.getLength();
657     sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0];
658     for( sal_Int32 i = 0; i < nLen; ++i )
659     {
660         sal_Unicode c = aString[i];
661         if (c == '-')
662             ++nSignCount;
663         else if (c == cDecSep)
664             ++nDecimalCount;
665         else if (c == 'E')
666             ++nExpCount;
667     }
668 
669     // #i112250# A small value might be formatted as "0" when only counting the digits,
670     // but fit into the column when considering the smaller width of the decimal separator.
671     if (aString == "0" && fVal != 0.0)
672         nDecimalCount = 1;
673 
674     if (nDecimalCount)
675         nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
676     if (nSignCount)
677         nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
678     if (nExpCount)
679         nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
680 
681     if (nDecimalCount || nSignCount || nExpCount)
682     {
683         // Re-calculate.
684         nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
685         OUString sTempOut(aString);
686         if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
687         {
688             aString = sTempOut;
689             // Failed to get output string.  Bail out.
690             return;
691         }
692         aString = sTempOut;
693     }
694 
695     tools::Long nActualTextWidth = GetFmtTextWidth(aString);
696     if (nActualTextWidth > nWidth)
697     {
698         // Even after the decimal adjustment the text doesn't fit.  Give up.
699         SetHashText();
700         return;
701     }
702 
703     TextChanged();
704     maLastCell.clear();   // #i113022# equal cell and format in another column may give different string
705 }
706 
707 void ScDrawStringsVars::SetAutoText( const OUString& rAutoText )
708 {
709     aString = rAutoText;
710 
711     OutputDevice* pRefDevice = pOutput->mpRefDevice;
712     OutputDevice* pFmtDevice = pOutput->pFmtDevice;
713     aTextSize.setWidth( GetFmtTextWidth( aString ) );
714     aTextSize.setHeight( pFmtDevice->GetTextHeight() );
715 
716     if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
717     {
718         double fMul = pOutput->GetStretch();
719         aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
720     }
721 
722     aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
723     if ( GetOrient() != SvxCellOrientation::Standard )
724     {
725         tools::Long nTemp = aTextSize.Height();
726         aTextSize.setHeight( aTextSize.Width() );
727         aTextSize.setWidth( nTemp );
728     }
729 
730     nOriginalWidth = aTextSize.Width();
731     if ( bPixelToLogic )
732         aTextSize = pRefDevice->LogicToPixel( aTextSize );
733 
734     maLastCell.clear();       // the same text may fit in the next cell
735 }
736 
737 tools::Long ScDrawStringsVars::GetMaxDigitWidth()
738 {
739     if (nMaxDigitWidth > 0)
740         return nMaxDigitWidth;
741 
742     for (char i = 0; i < 10; ++i)
743     {
744         char cDigit = '0' + i;
745         // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
746         tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
747         nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
748     }
749     return nMaxDigitWidth;
750 }
751 
752 tools::Long ScDrawStringsVars::GetSignWidth()
753 {
754     if (nSignWidth > 0)
755         return nSignWidth;
756 
757     nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-'));
758     return nSignWidth;
759 }
760 
761 tools::Long ScDrawStringsVars::GetDotWidth()
762 {
763     if (nDotWidth > 0)
764         return nDotWidth;
765 
766     const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator;
767     nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
768     return nDotWidth;
769 }
770 
771 tools::Long ScDrawStringsVars::GetExpWidth()
772 {
773     if (nExpWidth > 0)
774         return nExpWidth;
775 
776     nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E'));
777     return nExpWidth;
778 }
779 
780 tools::Long ScDrawStringsVars::GetFmtTextWidth( const OUString& rString )
781 {
782     return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString ));
783 }
784 
785 void ScDrawStringsVars::TextChanged()
786 {
787     OutputDevice* pRefDevice = pOutput->mpRefDevice;
788     OutputDevice* pFmtDevice = pOutput->pFmtDevice;
789     aTextSize.setWidth( GetFmtTextWidth( aString ) );
790     aTextSize.setHeight( pFmtDevice->GetTextHeight() );
791 
792     if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
793     {
794         double fMul = pOutput->GetStretch();
795         aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
796     }
797 
798     aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
799     if ( GetOrient() != SvxCellOrientation::Standard )
800     {
801         tools::Long nTemp = aTextSize.Height();
802         aTextSize.setHeight( aTextSize.Width() );
803         aTextSize.setWidth( nTemp );
804     }
805 
806     nOriginalWidth = aTextSize.Width();
807     if ( bPixelToLogic )
808         aTextSize = pRefDevice->LogicToPixel( aTextSize );
809 }
810 
811 bool ScDrawStringsVars::HasEditCharacters() const
812 {
813     for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
814     {
815         switch(aString[nIdx])
816         {
817             case CHAR_NBSP:
818             case CHAR_SHY:
819             case CHAR_ZWSP:
820             case CHAR_LRM:
821             case CHAR_RLM:
822             case CHAR_NBHY:
823             case CHAR_WJ:
824                 return true;
825             default:
826                 break;
827         }
828     }
829 
830     return false;
831 }
832 
833 double ScOutputData::GetStretch() const
834 {
835     if ( mpRefDevice->IsMapModeEnabled() )
836     {
837         //  If a non-trivial MapMode is set, its scale is now already
838         //  taken into account in the OutputDevice's font handling
839         //  (OutputDevice::ImplNewFont, see #95414#).
840         //  The old handling below is only needed for pixel output.
841         return 1.0;
842     }
843 
844     // calculation in double is faster than Fraction multiplication
845     // and doesn't overflow
846 
847     if ( mpRefDevice == pFmtDevice )
848     {
849         MapMode aOld = mpRefDevice->GetMapMode();
850         return static_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
851     }
852     else
853     {
854         // when formatting for printer, device map mode has already been taken care of
855         return static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
856     }
857 }
858 
859 //  output strings
860 
861 static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, ScRefCellValue& rCell )
862 {
863     vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
864 
865     OUString aURL;
866     OUString aCellText;
867     if (rCell.getType() == CELLTYPE_FORMULA)
868     {
869         ScFormulaCell* pFCell = rCell.getFormula();
870         if ( pFCell->IsHyperLinkCell() )
871             pFCell->GetURLResult( aURL, aCellText );
872     }
873 
874     if ( !aURL.isEmpty() && pPDFData )
875     {
876         vcl::PDFExtOutDevBookmarkEntry aBookmark;
877         aBookmark.nLinkId = pPDFData->CreateLink(rRect, aCellText);
878         aBookmark.aBookmark = aURL;
879         std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
880         rBookmarks.push_back( aBookmark );
881     }
882 }
883 
884 void ScOutputData::SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell )
885 {
886     switch (rCell.getType())
887     {
888         case CELLTYPE_VALUE:
889             pFont->SetColor(*mxValueColor);
890         break;
891         case CELLTYPE_STRING:
892             pFont->SetColor(*mxTextColor);
893         break;
894         case CELLTYPE_FORMULA:
895             pFont->SetColor(*mxFormulaColor);
896         break;
897         default:
898         {
899             // added to avoid warnings
900         }
901     }
902 }
903 
904 static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
905 {
906     ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
907     SfxItemSet aSet( rEngine.GetEmptyItemSet() );
908     aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
909     rEngine.QuickSetAttribs( aSet, aSel );
910     // function is called with update mode set to FALSE
911 }
912 
913 void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell )
914 {
915     Color aColor;
916     switch (rCell.getType())
917     {
918         case CELLTYPE_VALUE:
919             aColor = *mxValueColor;
920             break;
921         case CELLTYPE_STRING:
922             aColor = *mxTextColor;
923             break;
924         case CELLTYPE_FORMULA:
925             aColor = *mxFormulaColor;
926             break;
927         default:
928         {
929             // added to avoid warnings
930         }
931     }
932     lcl_SetEditColor( rEngine, aColor );
933 }
934 
935 bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
936                                     SCCOL& rOverX, SCROW& rOverY,
937                                     bool bVisRowChanged )
938 {
939     bool bDoMerge = false;
940     bool bIsLeft = ( nX == nVisX1 );
941     bool bIsTop  = ( nY == nVisY1 ) || bVisRowChanged;
942 
943     bool bHOver;
944     bool bVOver;
945     bool bHidden;
946 
947     if (!mpDoc->ColHidden(nX, nTab) && nX >= nX1 && nX <= nX2
948             && !mpDoc->RowHidden(nY, nTab) && nY >= nY1 && nY <= nY2)
949     {
950         ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
951         bHOver = pInfo->bHOverlapped;
952         bVOver = pInfo->bVOverlapped;
953     }
954     else
955     {
956         ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG)->GetValue();
957         bHOver = bool(nOverlap2 & ScMF::Hor);
958         bVOver = bool(nOverlap2 & ScMF::Ver);
959     }
960 
961     if ( bHOver && bVOver )
962         bDoMerge = bIsLeft && bIsTop;
963     else if ( bHOver )
964         bDoMerge = bIsLeft;
965     else if ( bVOver )
966         bDoMerge = bIsTop;
967 
968     rOverX = nX;
969     rOverY = nY;
970 
971     while (bHOver)              // nY constant
972     {
973         --rOverX;
974         bHidden = mpDoc->ColHidden(rOverX, nTab);
975         if ( !bDoMerge && !bHidden )
976             return false;
977 
978         if (rOverX >= nX1 && !bHidden)
979         {
980             bHOver = pRowInfo[nArrY].cellInfo(rOverX).bHOverlapped;
981             bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
982         }
983         else
984         {
985             ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, nTab, ATTR_MERGE_FLAG)->GetValue();
986             bHOver = bool(nOverlap & ScMF::Hor);
987             bVOver = bool(nOverlap & ScMF::Ver);
988         }
989     }
990 
991     while (bVOver)
992     {
993         --rOverY;
994         bHidden = mpDoc->RowHidden(rOverY, nTab);
995         if ( !bDoMerge && !bHidden )
996             return false;
997 
998         if (nArrY>0)
999             --nArrY;                        // local copy !
1000 
1001         if (rOverX >= nX1 && rOverY >= nY1 &&
1002             !mpDoc->ColHidden(rOverX, nTab) &&
1003             !mpDoc->RowHidden(rOverY, nTab) &&
1004             pRowInfo[nArrY].nRowNo == rOverY)
1005         {
1006             bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
1007         }
1008         else
1009         {
1010             ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, nTab, ATTR_MERGE_FLAG )->GetValue();
1011             bVOver = bool(nOverlap & ScMF::Ver);
1012         }
1013     }
1014 
1015     return true;
1016 }
1017 
1018 static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern )
1019 {
1020     OSL_ENSURE( pNewPattern, "pNewPattern" );
1021 
1022     if ( pNewPattern == rpOldPattern )
1023         return false;
1024     else if ( !rpOldPattern )
1025         return true;
1026     else if ( &pNewPattern->GetItem( ATTR_FONT ) != &rpOldPattern->GetItem( ATTR_FONT ) )
1027         return true;
1028     else if ( &pNewPattern->GetItem( ATTR_CJK_FONT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT ) )
1029         return true;
1030     else if ( &pNewPattern->GetItem( ATTR_CTL_FONT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT ) )
1031         return true;
1032     else if ( &pNewPattern->GetItem( ATTR_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) )
1033         return true;
1034     else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) )
1035         return true;
1036     else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) )
1037         return true;
1038     else if ( &pNewPattern->GetItem( ATTR_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) )
1039         return true;
1040     else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) )
1041         return true;
1042     else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) )
1043         return true;
1044     else if ( &pNewPattern->GetItem( ATTR_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_FONT_POSTURE ) )
1045         return true;
1046     else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) )
1047         return true;
1048     else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) )
1049         return true;
1050     else if ( &pNewPattern->GetItem( ATTR_FONT_UNDERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) )
1051         return true;
1052     else if ( &pNewPattern->GetItem( ATTR_FONT_OVERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) )
1053         return true;
1054     else if ( &pNewPattern->GetItem( ATTR_FONT_WORDLINE ) != &rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) )
1055         return true;
1056     else if ( &pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ) != &rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) )
1057         return true;
1058     else if ( &pNewPattern->GetItem( ATTR_FONT_CONTOUR ) != &rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) )
1059         return true;
1060     else if ( &pNewPattern->GetItem( ATTR_FONT_SHADOWED ) != &rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) )
1061         return true;
1062     else if ( &pNewPattern->GetItem( ATTR_FONT_COLOR ) != &rpOldPattern->GetItem( ATTR_FONT_COLOR ) )
1063         return true;
1064     else if ( &pNewPattern->GetItem( ATTR_HOR_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) )
1065         return true;
1066     else if ( &pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) )
1067         return true;
1068     else if ( &pNewPattern->GetItem( ATTR_VER_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) )
1069         return true;
1070     else if ( &pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) )
1071         return true;
1072     else if ( &pNewPattern->GetItem( ATTR_STACKED ) != &rpOldPattern->GetItem( ATTR_STACKED ) )
1073         return true;
1074     else if ( &pNewPattern->GetItem( ATTR_LINEBREAK ) != &rpOldPattern->GetItem( ATTR_LINEBREAK ) )
1075         return true;
1076     else if ( &pNewPattern->GetItem( ATTR_MARGIN ) != &rpOldPattern->GetItem( ATTR_MARGIN ) )
1077         return true;
1078     else if ( &pNewPattern->GetItem( ATTR_ROTATE_VALUE ) != &rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) )
1079         return true;
1080     else if ( &pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ) != &rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) )
1081         return true;
1082     else if ( &pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ) != &rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) )
1083         return true;
1084     else if ( &pNewPattern->GetItem( ATTR_FONT_RELIEF ) != &rpOldPattern->GetItem( ATTR_FONT_RELIEF ) )
1085         return true;
1086     else if ( &pNewPattern->GetItem( ATTR_BACKGROUND ) != &rpOldPattern->GetItem( ATTR_BACKGROUND ) )
1087         return true;    // needed with automatic text color
1088     else
1089     {
1090         rpOldPattern = pNewPattern;
1091         return false;
1092     }
1093 }
1094 
1095 static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc,
1096         const ScFormulaCell* pFCell )
1097 {
1098     if ( !bProgress && pFCell->GetDirty() )
1099     {
1100         ScProgress::CreateInterpretProgress( pDoc );
1101         bProgress = true;
1102     }
1103 }
1104 
1105 static bool IsAmbiguousScript( SvtScriptType nScript )
1106 {
1107     return ( nScript != SvtScriptType::LATIN &&
1108              nScript != SvtScriptType::ASIAN &&
1109              nScript != SvtScriptType::COMPLEX );
1110 }
1111 
1112 bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
1113 {
1114     // pThisRowInfo may be NULL
1115 
1116     bool bEmpty;
1117     if ( pThisRowInfo && nX <= nX2 )
1118         bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1119     else
1120     {
1121         ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1122         bEmpty = aCell.isEmpty();
1123     }
1124 
1125     if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
1126     {
1127         //  for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
1128         //  into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
1129 
1130         bool bIsPrint = ( eType == OUTTYPE_PRINTER );
1131 
1132         if ( bIsPrint || bTabProtected )
1133         {
1134             const ScProtectionAttr* pAttr =
1135                     mpDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
1136             if ( bIsPrint && pAttr->GetHidePrint() )
1137                 bEmpty = true;
1138             else if ( bTabProtected )
1139             {
1140                 if ( pAttr->GetHideCell() )
1141                     bEmpty = true;
1142                 else if ( mbShowFormulas && pAttr->GetHideFormula() )
1143                 {
1144                     if (mpDoc->GetCellType(ScAddress(nX, nY, nTab)) == CELLTYPE_FORMULA)
1145                         bEmpty = true;
1146                 }
1147             }
1148         }
1149     }
1150     return bEmpty;
1151 }
1152 
1153 void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScRefCellValue& rCell )
1154 {
1155     rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP));
1156     if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow))
1157         rCell.clear();
1158 }
1159 
1160 bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
1161 {
1162     //  apply the same logic here as in DrawStrings/DrawEdit:
1163     //  Stop at non-empty or merged or overlapped cell,
1164     //  where a note is empty as well as a cell that's hidden by protection settings
1165 
1166     ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1167     if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY))
1168         return false;
1169 
1170     const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, nTab );
1171     return !(pPattern->GetItem(ATTR_MERGE).IsMerged() ||
1172          pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped());
1173 }
1174 
1175 // nX, nArrY:       loop variables from DrawStrings / DrawEdit
1176 // nPosX, nPosY:    corresponding positions for nX, nArrY
1177 // nCellX, nCellY:  position of the cell that contains the text
1178 // nNeeded:         Text width, including margin
1179 // rPattern:        cell format at nCellX, nCellY
1180 // nHorJustify:     horizontal alignment (visual) to determine which cells to use for long strings
1181 // bCellIsValue:    if set, don't extend into empty cells
1182 // bBreak:          if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
1183 // bOverwrite:      if set, also extend into non-empty cells (for rotated text)
1184 // rParam           output: various area parameters.
1185 
1186 void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY,
1187                                   SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
1188                                   const ScPatternAttr& rPattern,
1189                                   sal_uInt16 nHorJustify, bool bCellIsValue,
1190                                   bool bBreak, bool bOverwrite,
1191                                   OutputAreaParam& rParam )
1192 {
1193     //  rThisRowInfo may be for a different row than nCellY, is still used for clip marks
1194     RowInfo& rThisRowInfo = pRowInfo[nArrY];
1195 
1196     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1197 
1198     tools::Long nCellPosX = nPosX;         // find nCellX position, starting at nX/nPosX
1199     SCCOL nCompCol = nX;
1200     while ( nCellX > nCompCol )
1201     {
1202         //! extra member function for width?
1203         tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1204                 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1205                 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1206         nCellPosX += nColWidth * nLayoutSign;
1207         ++nCompCol;
1208     }
1209     while ( nCellX < nCompCol )
1210     {
1211         --nCompCol;
1212         tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1213                 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1214                 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1215         nCellPosX -= nColWidth * nLayoutSign;
1216     }
1217 
1218     tools::Long nCellPosY = nPosY;         // find nCellY position, starting at nArrY/nPosY
1219     SCSIZE nCompArr = nArrY;
1220     SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
1221     while ( nCellY > nCompRow )
1222     {
1223         if ( nCompArr + 1 < nArrCount )
1224         {
1225             nCellPosY += pRowInfo[nCompArr].nHeight;
1226             ++nCompArr;
1227             nCompRow = pRowInfo[nCompArr].nRowNo;
1228         }
1229         else
1230         {
1231             sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, nTab );
1232             if ( nDocHeight )
1233                 nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY );
1234             ++nCompRow;
1235         }
1236     }
1237     nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, mnPPTY );
1238 
1239     const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE );
1240     bool bMerged = pMerge->IsMerged();
1241     tools::Long nMergeCols = pMerge->GetColMerge();
1242     if ( nMergeCols == 0 )
1243         nMergeCols = 1;
1244     tools::Long nMergeRows = pMerge->GetRowMerge();
1245     if ( nMergeRows == 0 )
1246         nMergeRows = 1;
1247 
1248     tools::Long nMergeSizeX = 0;
1249     for ( tools::Long i=0; i<nMergeCols; i++ )
1250     {
1251         tools::Long nColWidth = ( nCellX+i <= nX2 ) ?
1252                 pRowInfo[0].basicCellInfo(nCellX+i).nWidth :
1253                 static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * mnPPTX );
1254         nMergeSizeX += nColWidth;
1255     }
1256     tools::Long nMergeSizeY = 0;
1257     short nDirect = 0;
1258     if ( rThisRowInfo.nRowNo == nCellY )
1259     {
1260         // take first row's height from row info
1261         nMergeSizeY += rThisRowInfo.nHeight;
1262         nDirect = 1;        // skip in loop
1263     }
1264     // following rows always from document
1265     nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, mnPPTY);
1266 
1267     --nMergeSizeX;      // leave out the grid horizontally, also for alignment (align between grid lines)
1268 
1269     rParam.mnColWidth = nMergeSizeX; // store the actual column width.
1270     rParam.mnLeftClipLength = rParam.mnRightClipLength = 0;
1271 
1272     // construct the rectangles using logical left/right values (justify is called at the end)
1273 
1274     // rAlignRect is the single cell or merged area, used for alignment.
1275 
1276     rParam.maAlignRect.SetLeft( nCellPosX );
1277     rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign );
1278     rParam.maAlignRect.SetTop( nCellPosY );
1279     rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 );
1280 
1281     //  rClipRect is all cells that are used for output.
1282     //  For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.
1283 
1284     rParam.maClipRect = rParam.maAlignRect;
1285     if ( nNeeded > nMergeSizeX )
1286     {
1287         SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify);
1288 
1289         tools::Long nMissing = nNeeded - nMergeSizeX;
1290         tools::Long nLeftMissing = 0;
1291         tools::Long nRightMissing = 0;
1292         switch ( eHorJust )
1293         {
1294             case SvxCellHorJustify::Left:
1295                 nRightMissing = nMissing;
1296                 break;
1297             case SvxCellHorJustify::Right:
1298                 nLeftMissing = nMissing;
1299                 break;
1300             case SvxCellHorJustify::Center:
1301                 nLeftMissing = nMissing / 2;
1302                 nRightMissing = nMissing - nLeftMissing;
1303                 break;
1304             default:
1305             {
1306                 // added to avoid warnings
1307             }
1308         }
1309 
1310         // nLeftMissing, nRightMissing are logical, eHorJust values are visual
1311         if ( bLayoutRTL )
1312             ::std::swap( nLeftMissing, nRightMissing );
1313 
1314         SCCOL nRightX = nCellX;
1315         SCCOL nLeftX = nCellX;
1316         if ( !bMerged && !bCellIsValue && !bBreak )
1317         {
1318             //  look for empty cells into which the text can be extended
1319 
1320             while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
1321             {
1322                 ++nRightX;
1323                 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, nTab ) * mnPPTX );
1324                 nRightMissing -= nAdd;
1325                 rParam.maClipRect.AdjustRight(nAdd * nLayoutSign );
1326 
1327                 if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
1328                     rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true;
1329             }
1330 
1331             while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
1332             {
1333                 if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
1334                     rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true;
1335 
1336                 --nLeftX;
1337                 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, nTab ) * mnPPTX );
1338                 nLeftMissing -= nAdd;
1339                 rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) );
1340             }
1341         }
1342 
1343         //  Set flag and reserve space for clipping mark triangle,
1344         //  even if rThisRowInfo isn't for nCellY (merged cells).
1345         if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
1346         {
1347             rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right;
1348             bAnyClipped = true;
1349             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1350             rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
1351         }
1352         if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
1353         {
1354             rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left;
1355             bAnyClipped = true;
1356             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1357             rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign );
1358         }
1359 
1360         rParam.mbLeftClip = ( nLeftMissing > 0 );
1361         rParam.mbRightClip = ( nRightMissing > 0 );
1362         rParam.mnLeftClipLength = nLeftMissing;
1363         rParam.mnRightClipLength = nRightMissing;
1364     }
1365     else
1366     {
1367         rParam.mbLeftClip = rParam.mbRightClip = false;
1368 
1369         // leave space for AutoFilter on screen
1370         // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)
1371 
1372         if ( eType==OUTTYPE_WINDOW &&
1373              ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) &&
1374              ( !bBreak || mpRefDevice == pFmtDevice ) )
1375         {
1376             // filter drop-down width depends on row height
1377             double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
1378             fZoom = fZoom > 1.0 ? fZoom : 1.0;
1379             const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE;
1380             bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
1381             if ( bFit )
1382             {
1383                 // content fits even in the remaining area without the filter button
1384                 // -> align within that remaining area
1385 
1386                 rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) );
1387                 rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) );
1388             }
1389         }
1390     }
1391 
1392     //  justify both rectangles for alignment calculation, use with DrawText etc.
1393 
1394     rParam.maAlignRect.Normalize();
1395     rParam.maClipRect.Normalize();
1396 }
1397 
1398 namespace {
1399 
1400 bool beginsWithRTLCharacter(const OUString& rStr)
1401 {
1402     if (rStr.isEmpty())
1403         return false;
1404 
1405     switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
1406     {
1407         case i18n::DirectionProperty_RIGHT_TO_LEFT:
1408         case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC:
1409         case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING:
1410         case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE:
1411             return true;
1412         default:
1413             ;
1414     }
1415 
1416     return false;
1417 }
1418 
1419 }
1420 
1421 /** Get left, right or centered alignment from RTL context.
1422 
1423     Does not return standard, block or repeat, for these the contextual left or
1424     right alignment is returned.
1425  */
1426 static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust,
1427         bool bCellIsValue, const OUString& rText,
1428         const ScPatternAttr& rPattern, const SfxItemSet* pCondSet,
1429         const ScDocument* pDoc, SCTAB nTab, const bool  bNumberFormatIsText )
1430 {
1431     SvxCellHorJustify eHorJustContext = eInHorJust;
1432     bool bUseWritingDirection = false;
1433     if (eInHorJust == SvxCellHorJustify::Standard)
1434     {
1435         // fdo#32530: Default alignment depends on value vs
1436         // string, and the direction of the 1st letter.
1437         if (beginsWithRTLCharacter( rText)) //If language is RTL
1438         {
1439             if (bCellIsValue)
1440                eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1441             else
1442                 eHorJustContext = SvxCellHorJustify::Right;
1443         }
1444         else if (bCellIsValue) //If language is not RTL
1445             eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right;
1446         else
1447             bUseWritingDirection = true;
1448     }
1449 
1450     if (bUseWritingDirection ||
1451             eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
1452     {
1453         SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet);
1454         if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
1455             eHorJustContext = SvxCellHorJustify::Left;
1456         else if (nDirection == SvxFrameDirection::Environment)
1457         {
1458             SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL");
1459             // fdo#73588: The content of the cell must also
1460             // begin with a RTL character to be right
1461             // aligned; otherwise, it should be left aligned.
1462             eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1463         }
1464         else
1465             eHorJustContext = SvxCellHorJustify::Right;
1466     }
1467     return eHorJustContext;
1468 }
1469 
1470 void ScOutputData::DrawStrings( bool bPixelToLogic )
1471 {
1472     LayoutStrings(bPixelToLogic);
1473 }
1474 
1475 tools::Rectangle ScOutputData::LayoutStrings(bool bPixelToLogic, bool bPaint, const ScAddress &rAddress)
1476 {
1477     bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings();
1478     mpDoc->SetLayoutStrings(true);
1479     comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings] {
1480         mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings);
1481     });
1482 
1483     OSL_ENSURE( mpDev == mpRefDevice ||
1484                 mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(),
1485                 "LayoutStrings: different MapUnits ?!?!" );
1486 
1487     vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(mpDev->GetExtOutDevData() );
1488 
1489     sc::IdleSwitch aIdleSwitch(*mpDoc, false);
1490     ScDrawStringsVars aVars( this, bPixelToLogic );
1491 
1492     bool bProgress = false;
1493 
1494     tools::Long nInitPosX = nScrX;
1495     if ( bLayoutRTL )
1496         nInitPosX += nMirrorW - 1;              // pixels
1497     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1498 
1499     SCCOL nLastContentCol = mpDoc->MaxCol();
1500     if ( nX2 < mpDoc->MaxCol() )
1501         nLastContentCol = sal::static_int_cast<SCCOL>(
1502             nLastContentCol - mpDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, mpDoc->MaxCol(), nY2, nTab, DIR_RIGHT ) );
1503     SCCOL nLoopStartX = nX1;
1504     if ( nX1 > 0 )
1505         --nLoopStartX;          // start before nX1 for rest of long text to the left
1506 
1507     // variables for GetOutputArea
1508     OutputAreaParam aAreaParam;
1509     bool bCellIsValue = false;
1510     tools::Long nNeededWidth = 0;
1511     const ScPatternAttr* pPattern = nullptr;
1512     const SfxItemSet* pCondSet = nullptr;
1513     const ScPatternAttr* pOldPattern = nullptr;
1514     const SfxItemSet* pOldCondSet = nullptr;
1515     SvtScriptType nOldScript = SvtScriptType::NONE;
1516 
1517     // Try to limit interpreting to only visible cells. Calling e.g. IsValue()
1518     // on a formula cell that needs interpreting would call Interpret()
1519     // for the entire formula group, which could be large.
1520     mpDoc->InterpretCellsIfNeeded( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ));
1521 
1522     // alternative pattern instances in case we need to modify the pattern
1523     // before processing the cell value.
1524     std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
1525 
1526     KernArray aDX;
1527     tools::Long nPosY = nScrY;
1528     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
1529     {
1530         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1531         SCROW nY = pThisRowInfo->nRowNo;
1532         if ((bPaint && pThisRowInfo->bChanged) || (!bPaint && rAddress.Row() == nY))
1533         {
1534             tools::Long nPosX = nInitPosX;
1535             if ( nLoopStartX < nX1 )
1536                 nPosX -= pRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign;
1537             for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
1538             {
1539                 bool bMergeEmpty = false;
1540                 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
1541                 bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1542 
1543                 SCCOL nCellX = nX;                  // position where the cell really starts
1544                 SCROW nCellY = nY;
1545                 bool bDoCell = false;
1546                 bool bUseEditEngine = false;
1547 
1548                 //  Part of a merged cell?
1549 
1550                 bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped);
1551                 if ( bOverlapped )
1552                 {
1553                     bEmpty = true;
1554 
1555                     SCCOL nOverX;                   // start of the merged cells
1556                     SCROW nOverY;
1557                     bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
1558                     if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
1559                     {
1560                         nCellX = nOverX;
1561                         nCellY = nOverY;
1562                         bDoCell = true;
1563                     }
1564                     else
1565                         bMergeEmpty = true;
1566                 }
1567 
1568                 //  Rest of a long text further to the left?
1569 
1570                 if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
1571                 {
1572                     SCCOL nTempX=nX1;
1573                     while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1574                         --nTempX;
1575 
1576                     if ( nTempX < nX1 &&
1577                          !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1578                          !mpDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1579                     {
1580                         nCellX = nTempX;
1581                         bDoCell = true;
1582                     }
1583                 }
1584 
1585                 //  Rest of a long text further to the right?
1586 
1587                 if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
1588                 {
1589                     //  don't have to look further than nLastContentCol
1590 
1591                     SCCOL nTempX=nX;
1592                     while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1593                         ++nTempX;
1594 
1595                     if ( nTempX > nX &&
1596                          !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1597                          !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1598                     {
1599                         nCellX = nTempX;
1600                         bDoCell = true;
1601                     }
1602                 }
1603 
1604                 //  normal visible cell
1605 
1606                 if (!bEmpty)
1607                     bDoCell = true;
1608 
1609                 //  don't output the cell that's being edited
1610 
1611                 if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
1612                     bDoCell = false;
1613 
1614                 // skip text in cell if data bar/icon set is set and only value selected
1615                 if ( bDoCell )
1616                 {
1617                     if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
1618                         bDoCell = false;
1619                     if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
1620                         bDoCell = false;
1621                 }
1622 
1623                 //  output the cell text
1624 
1625                 ScRefCellValue aCell;
1626                 if (bDoCell)
1627                 {
1628                     if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
1629                         aCell = pThisRowInfo->cellInfo(nCellX).maCell;
1630                     else
1631                         GetVisibleCell( nCellX, nCellY, nTab, aCell );      // get from document
1632                     if (aCell.isEmpty())
1633                         bDoCell = false;
1634                     else if (aCell.getType() == CELLTYPE_EDIT)
1635                         bUseEditEngine = true;
1636                 }
1637 
1638                 // Check if this cell is mis-spelled.
1639                 if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
1640                 {
1641                     if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
1642                         bUseEditEngine = true;
1643                 }
1644 
1645                 if (bDoCell && !bUseEditEngine)
1646                 {
1647                     if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1648                     {
1649                         ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
1650                         pPattern = rCellInfo.pPatternAttr;
1651                         pCondSet = rCellInfo.pConditionSet;
1652 
1653                         if ( !pPattern )
1654                         {
1655                             // #i68085# pattern from cell info for hidden columns is null,
1656                             // test for null is quicker than using column flags
1657                             pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1658                             pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1659                         }
1660                     }
1661                     else        // get from document
1662                     {
1663                         pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1664                         pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1665                     }
1666                     if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() )
1667                     {
1668                         aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1669                         ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1670                         if (  ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
1671                         {
1672                             pAltPattern->SetStyleSheet(pPreviewStyle);
1673                         }
1674                         else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
1675                         {
1676                             if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
1677                                 pAltPattern->GetItemSet().Put( *pItem );
1678                             if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
1679                                 pAltPattern->GetItemSet().Put( *pItem );
1680                             if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
1681                                 pAltPattern->GetItemSet().Put( *pItem );
1682                         }
1683                         pPattern = pAltPattern;
1684                     }
1685 
1686                     if (aCell.hasNumeric() &&
1687                             pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
1688                     {
1689                         // Disable line break when the cell content is numeric.
1690                         aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1691                         ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1692                         ScLineBreakCell aLineBreak(false);
1693                         pAltPattern->GetItemSet().Put(aLineBreak);
1694                         pPattern = pAltPattern;
1695                     }
1696 
1697                     SvtScriptType nScript = mpDoc->GetCellScriptType(
1698                         ScAddress(nCellX, nCellY, nTab),
1699                         pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet));
1700 
1701                     if (nScript == SvtScriptType::NONE)
1702                         nScript = ScGlobal::GetDefaultScriptType();
1703 
1704                     if ( pPattern != pOldPattern || pCondSet != pOldCondSet ||
1705                          nScript != nOldScript || mbSyntaxMode )
1706                     {
1707                         if ( StringDiffer(pOldPattern,pPattern) ||
1708                              pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode )
1709                         {
1710                             aVars.SetPattern(pPattern, pCondSet, aCell, nScript);
1711                         }
1712                         else
1713                             aVars.SetPatternSimple( pPattern, pCondSet );
1714                         pOldPattern = pPattern;
1715                         pOldCondSet = pCondSet;
1716                         nOldScript = nScript;
1717                     }
1718 
1719                     //  use edit engine for rotated, stacked or mixed-script text
1720                     if ( aVars.GetOrient() == SvxCellOrientation::Stacked ||
1721                          aVars.IsRotated() || IsAmbiguousScript(nScript) )
1722                         bUseEditEngine = true;
1723                 }
1724                 if (bDoCell && !bUseEditEngine)
1725                 {
1726                     bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA);
1727                     if ( bFormulaCell )
1728                         lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula());
1729                     if ( aVars.SetText(aCell) )
1730                         pOldPattern = nullptr;
1731                     bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult());
1732                 }
1733                 tools::Long nTotalMargin = 0;
1734                 SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard;
1735                 if (bDoCell && !bUseEditEngine)
1736                 {
1737                     CellType eCellType = aCell.getType();
1738                     bCellIsValue = ( eCellType == CELLTYPE_VALUE );
1739                     if ( eCellType == CELLTYPE_FORMULA )
1740                     {
1741                         ScFormulaCell* pFCell = aCell.getFormula();
1742                         bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
1743                     }
1744 
1745                     const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
1746                     eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(),
1747                             *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText );
1748 
1749                     bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block );
1750                     // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats
1751                     // Must be synchronized with ScColumn::GetNeededSize()
1752                     SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1753                     if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER))
1754                         bBreak = false;
1755 
1756                     bool bRepeat = aVars.IsRepeat() && !bBreak;
1757                     bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;
1758 
1759                     nTotalMargin =
1760                         static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) +
1761                         static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX);
1762 
1763                     nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;
1764 
1765                     // GetOutputArea gives justified rectangles
1766                     GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
1767                                    *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
1768                                    bCellIsValue || bRepeat || bShrink, bBreak, false,
1769                                    aAreaParam );
1770 
1771                     aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin );
1772                     if ( bShrink )
1773                     {
1774                         if ( aVars.GetOrient() != SvxCellOrientation::Standard )
1775                         {
1776                             // Only horizontal scaling is handled here.
1777                             // DrawEdit is used to vertically scale 90 deg rotated text.
1778                             bUseEditEngine = true;
1779                         }
1780                         else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip )     // horizontal
1781                         {
1782                             tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1783                             tools::Long nScaleSize = aVars.GetTextSize().Width();         // without margin
1784 
1785                             if ( nAvailable > 0 && nScaleSize > 0 )       // 0 if the text is empty (formulas, number formats)
1786                             {
1787                                 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
1788 
1789                                 aVars.SetShrinkScale( nScale, nOldScript );
1790                                 tools::Long nNewSize = aVars.GetTextSize().Width();
1791 
1792                                 sal_uInt16 nShrinkAgain = 0;
1793                                 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
1794                                 {
1795                                     // If the text is still too large, reduce the scale again by 10%, until it fits,
1796                                     // at most 7 times (it's less than 50% of the calculated scale then).
1797 
1798                                     nScale = ( nScale * 9 ) / 10;
1799                                     aVars.SetShrinkScale( nScale, nOldScript );
1800                                     nNewSize = aVars.GetTextSize().Width();
1801                                     ++nShrinkAgain;
1802                                 }
1803                                 // If even at half the size the font still isn't rendered smaller,
1804                                 // fall back to normal clipping (showing ### for numbers).
1805                                 if ( nNewSize <= nAvailable )
1806                                 {
1807                                     // Reset relevant parameters.
1808                                     aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1809                                     aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1810                                 }
1811 
1812                                 pOldPattern = nullptr;
1813                             }
1814                         }
1815                     }
1816 
1817                     if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
1818                     {
1819                         tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1820                         tools::Long nRepeatSize = aVars.GetTextSize().Width();         // without margin
1821                         // When formatting for the printer, the text sizes don't always add up.
1822                         // Round down (too few repetitions) rather than exceeding the cell size then:
1823                         if ( pFmtDevice != mpRefDevice )
1824                             ++nRepeatSize;
1825                         if ( nRepeatSize > 0 )
1826                         {
1827                             tools::Long nRepeatCount = nAvailable / nRepeatSize;
1828                             if ( nRepeatCount > 1 )
1829                             {
1830                                 OUString aCellStr = aVars.GetString();
1831                                 OUStringBuffer aRepeated(aCellStr);
1832                                 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
1833                                     aRepeated.append(aCellStr);
1834                                 aVars.SetAutoText( aRepeated.makeStringAndClear() );
1835                             }
1836                         }
1837                     }
1838 
1839                     //  use edit engine if automatic line breaks are needed
1840                     if ( bBreak )
1841                     {
1842                         if ( aVars.GetOrient() == SvxCellOrientation::Standard )
1843                             bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
1844                         else
1845                         {
1846                             tools::Long nHeight = aVars.GetTextSize().Height() +
1847                                             static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) +
1848                                             static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY);
1849                             bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() );
1850                         }
1851                     }
1852                     if (!bUseEditEngine)
1853                     {
1854                         bUseEditEngine =
1855                             aVars.GetHorJust() == SvxCellHorJustify::Block &&
1856                             aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute;
1857                     }
1858                 }
1859                 if (bUseEditEngine)
1860                 {
1861                     //  mark the cell in ScCellInfo to be drawn in DrawEdit:
1862                     //  Cells to the left are marked directly, cells to the
1863                     //  right are handled by the flag for nX2
1864                     SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
1865                     RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0];
1866                     pMarkRowInfo->basicCellInfo(nMarkX).bEditEngine = true;
1867                     bDoCell = false;    // don't draw here
1868                 }
1869                 if ( bDoCell )
1870                 {
1871                     if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
1872                     {
1873                         if (mbShowFormulas)
1874                             aVars.SetHashText();
1875                         else
1876                             // Adjust the decimals to fit the available column width.
1877                             aVars.SetTextToWidthOrHash(aCell, aAreaParam.mnColWidth - nTotalMargin);
1878 
1879                         nNeededWidth = aVars.GetTextSize().Width() +
1880                                     static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) +
1881                                     static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX );
1882                         if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
1883                         {
1884                             // Cell value is no longer clipped.  Reset relevant parameters.
1885                             aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1886                             aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1887                         }
1888 
1889                         //  If the "###" replacement doesn't fit into the cells, no clip marks
1890                         //  are shown, as the "###" already denotes too little space.
1891                         //  The rectangles from the first GetOutputArea call remain valid.
1892                     }
1893 
1894                     tools::Long nJustPosX = aAreaParam.maAlignRect.Left();     // "justified" - effect of alignment will be added
1895                     tools::Long nJustPosY = aAreaParam.maAlignRect.Top();
1896                     tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
1897                     tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight();
1898 
1899                     bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
1900                     // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip
1901                     bool bVClip = AdjustAreaParamClipRect(aAreaParam);
1902                     bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
1903 
1904                     // check horizontal space
1905 
1906                     if ( !bOutside )
1907                     {
1908                         bool bRightAdjusted = false;        // to correct text width calculation later
1909                         switch (eOutHorJust)
1910                         {
1911                             case SvxCellHorJustify::Left:
1912                                 nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX );
1913                                 break;
1914                             case SvxCellHorJustify::Right:
1915                                 nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
1916                                             static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX );
1917                                 bRightAdjusted = true;
1918                                 break;
1919                             case SvxCellHorJustify::Center:
1920                                 nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
1921                                             static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) -
1922                                             static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2;
1923                                 break;
1924                             default:
1925                             {
1926                                 // added to avoid warnings
1927                             }
1928                         }
1929 
1930                         tools::Long nTestClipHeight = aVars.GetTextSize().Height();
1931                         switch (aVars.GetVerJust())
1932                         {
1933                             case SvxCellVerJustify::Top:
1934                             case SvxCellVerJustify::Block:
1935                                 {
1936                                     tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
1937                                     nJustPosY += nTop;
1938                                     nTestClipHeight += nTop;
1939                                 }
1940                                 break;
1941                             case SvxCellVerJustify::Bottom:
1942                                 {
1943                                     tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
1944                                     nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
1945                                     nTestClipHeight += nBot;
1946                                 }
1947                                 break;
1948                             case SvxCellVerJustify::Center:
1949                                 {
1950                                     tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
1951                                     tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
1952                                     nJustPosY += ( nOutHeight + nTop -
1953                                                     aVars.GetTextSize().Height() - nBot ) / 2;
1954                                     nTestClipHeight += std::abs( nTop - nBot );
1955                                 }
1956                                 break;
1957                             default:
1958                             {
1959                                 // added to avoid warnings
1960                             }
1961                         }
1962 
1963                         if ( nTestClipHeight > nOutHeight )
1964                         {
1965                             // no vertical clipping when printing cells with optimal height,
1966                             // except when font size is from conditional formatting.
1967                             if ( eType != OUTTYPE_PRINTER ||
1968                                     ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) ||
1969                                     ( aVars.HasCondHeight() ) )
1970                                 bVClip = true;
1971                         }
1972 
1973                         if ( bHClip || bVClip )
1974                         {
1975                             // only clip the affected dimension so that not all right-aligned
1976                             // columns are cut off when performing a non-proportional resize
1977                             if (!bHClip)
1978                             {
1979                                 aAreaParam.maClipRect.SetLeft( nScrX );
1980                                 aAreaParam.maClipRect.SetRight( nScrX+nScrW );
1981                             }
1982                             if (!bVClip)
1983                             {
1984                                 aAreaParam.maClipRect.SetTop( nScrY );
1985                                 aAreaParam.maClipRect.SetBottom( nScrY+nScrH );
1986                             }
1987 
1988                             //  aClipRect is not used after SetClipRegion/IntersectClipRegion,
1989                             //  so it can be modified here
1990                             if (bPixelToLogic)
1991                                 aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect );
1992 
1993                             if (bMetaFile)
1994                             {
1995                                 mpDev->Push();
1996                                 mpDev->IntersectClipRegion( aAreaParam.maClipRect );
1997                             }
1998                             else
1999                                 mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
2000                         }
2001 
2002                         Point aURLStart( nJustPosX, nJustPosY );    // copy before modifying for orientation
2003 
2004                         switch (aVars.GetOrient())
2005                         {
2006                             case SvxCellOrientation::Standard:
2007                                 nJustPosY += aVars.GetAscent();
2008                                 break;
2009                             case SvxCellOrientation::TopBottom:
2010                                 nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
2011                                 break;
2012                             case SvxCellOrientation::BottomUp:
2013                                 nJustPosY += aVars.GetTextSize().Height();
2014                                 nJustPosX += aVars.GetAscent();
2015                                 break;
2016                             default:
2017                             {
2018                                 // added to avoid warnings
2019                             }
2020                         }
2021 
2022                         // When clipping, the visible part is now completely defined by the alignment,
2023                         // there's no more special handling to show the right part of RTL text.
2024 
2025                         Point aDrawTextPos( nJustPosX, nJustPosY );
2026                         if ( bPixelToLogic )
2027                         {
2028                             //  undo text width adjustment in pixels
2029                             if (bRightAdjusted)
2030                                 aDrawTextPos.AdjustX(aVars.GetTextSize().Width() );
2031 
2032                             aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos );
2033 
2034                             //  redo text width adjustment in logic units
2035                             if (bRightAdjusted)
2036                                 aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) );
2037                         }
2038 
2039                         // in Metafiles always use DrawTextArray to ensure that positions are
2040                         // recorded (for non-proportional resize):
2041 
2042                         const OUString& aString = aVars.GetString();
2043                         if (!aString.isEmpty())
2044                         {
2045                             // If the string is clipped, make it shorter for
2046                             // better performance since drawing by HarfBuzz is
2047                             // quite expensive especially for long string.
2048 
2049                             OUString aShort = aString;
2050 
2051                             // But never fiddle with numeric values.
2052                             // (Which was the cause of tdf#86024).
2053                             // The General automatic format output takes
2054                             // care of this, or fixed width numbers either fit
2055                             // or display as ###.
2056                             if (!bCellIsValue)
2057                             {
2058                                 double fVisibleRatio = 1.0;
2059                                 double fTextWidth = aVars.GetTextSize().Width();
2060                                 sal_Int32 nTextLen = aString.getLength();
2061                                 if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0)
2062                                 {
2063                                     fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth;
2064                                     if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2065                                     {
2066                                         // Only show the left-end segment.
2067                                         sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2068                                         aShort = aShort.copy(0, nShortLen);
2069                                     }
2070                                 }
2071                                 else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0)
2072                                 {
2073                                     fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth;
2074                                     if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2075                                     {
2076                                         // Only show the right-end segment.
2077                                         sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2078                                         aShort = aShort.copy(nTextLen-nShortLen);
2079 
2080                                         // Adjust the text position after shortening of the string.
2081                                         double fShortWidth = aVars.GetFmtTextWidth(aShort);
2082                                         double fOffset = fTextWidth - fShortWidth;
2083                                         aDrawTextPos.Move(fOffset, 0);
2084                                     }
2085                                 }
2086                             }
2087 
2088                             // if we are not painting, it means we are interested in
2089                             // the area of the text that covers the specified cell
2090                             if (!bPaint && rAddress.Col() == nX)
2091                             {
2092                                 tools::Rectangle aRect;
2093                                 mpDev->GetTextBoundRect(aRect, aShort);
2094                                 aRect += aDrawTextPos;
2095                                 return aRect;
2096                             }
2097 
2098                             if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY)
2099                             {
2100                                 size_t nLen = aShort.getLength();
2101                                 if (aDX.size() < nLen)
2102                                     aDX.resize(nLen, 0);
2103 
2104                                 pFmtDevice->GetTextArray(aShort, &aDX);
2105 
2106                                 if ( !mpRefDevice->GetConnectMetaFile() ||
2107                                         mpRefDevice->GetOutDevType() == OUTDEV_PRINTER )
2108                                 {
2109                                     double fMul = GetStretch();
2110                                     for (size_t i = 0; i < nLen; ++i)
2111                                         aDX.set(i, static_cast<sal_Int32>(aDX[i] / fMul + 0.5));
2112                                 }
2113 
2114                                 if (bPaint)
2115                                     mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
2116                             }
2117                             else
2118                             {
2119                                 if (bPaint)
2120                                     mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr,
2121                                         aVars.GetLayoutGlyphs(aShort));
2122                             }
2123                         }
2124 
2125                         if ( bHClip || bVClip )
2126                         {
2127                             if (bMetaFile)
2128                                 mpDev->Pop();
2129                             else
2130                                 mpDev->SetClipRegion();
2131                         }
2132 
2133                         // PDF: whole-cell hyperlink from formula?
2134                         bool bHasURL = pPDFData && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell();
2135                         if (bPaint && bHasURL)
2136                         {
2137                             tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
2138                             lcl_DoHyperlinkResult(mpDev, aURLRect, aCell);
2139                         }
2140                     }
2141                 }
2142                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2143             }
2144         }
2145         nPosY += pRowInfo[nArrY].nHeight;
2146     }
2147     if ( bProgress )
2148         ScProgress::DeleteInterpretProgress();
2149 
2150     return tools::Rectangle();
2151 }
2152 
2153 std::unique_ptr<ScFieldEditEngine> ScOutputData::CreateOutputEditEngine()
2154 {
2155     std::unique_ptr<ScFieldEditEngine> pEngine(new ScFieldEditEngine(mpDoc, mpDoc->GetEnginePool()));
2156     pEngine->SetUpdateLayout( false );
2157     // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
2158     pEngine->SetRefDevice( pFmtDevice );
2159     EEControlBits nCtrl = pEngine->GetControlWord();
2160     if ( bShowSpellErrors )
2161         nCtrl |= EEControlBits::ONLINESPELLING;
2162     if ( eType == OUTTYPE_PRINTER )
2163         nCtrl &= ~EEControlBits::MARKFIELDS;
2164     else
2165         nCtrl &= ~EEControlBits::MARKURLFIELDS;   // URLs not shaded for output
2166     if ( eType == OUTTYPE_WINDOW && mpRefDevice == pFmtDevice )
2167         nCtrl &= ~EEControlBits::FORMAT100;       // use the actual MapMode
2168     pEngine->SetControlWord( nCtrl );
2169     mpDoc->ApplyAsianEditSettings( *pEngine );
2170     pEngine->EnableAutoColor( mbUseStyleColor );
2171     pEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( nTab ) );
2172     return pEngine;
2173 }
2174 
2175 static void lcl_ClearEdit( EditEngine& rEngine )       // text and attributes
2176 {
2177     rEngine.SetUpdateLayout( false );
2178 
2179     rEngine.SetText(OUString());
2180     // do not keep any para-attributes
2181     const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
2182     if (rPara.Count())
2183         rEngine.SetParaAttribs( 0,
2184                     SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
2185     rEngine.EnableSkipOutsideFormat(false);
2186 }
2187 
2188 static bool lcl_SafeIsValue( ScRefCellValue& rCell )
2189 {
2190     switch (rCell.getType())
2191     {
2192         case CELLTYPE_VALUE:
2193             return true;
2194         case CELLTYPE_FORMULA:
2195         {
2196             ScFormulaCell* pFCell = rCell.getFormula();
2197             if (pFCell->IsRunning() || pFCell->IsValue())
2198                 return true;
2199         }
2200         break;
2201         default:
2202         {
2203             // added to avoid warnings
2204         }
2205     }
2206     return false;
2207 }
2208 
2209 static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent )
2210 {
2211     bool bUpdateMode = rEngine.SetUpdateLayout( false );
2212 
2213     sal_Int32 nParCount = rEngine.GetParagraphCount();
2214     for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
2215     {
2216         std::vector<sal_Int32> aPortions;
2217         rEngine.GetPortions( nPar, aPortions );
2218 
2219         sal_Int32 nStart = 0;
2220         for ( const sal_Int32 nEnd : aPortions )
2221         {
2222             ESelection aSel( nPar, nStart, nPar, nEnd );
2223             SfxItemSet aAttribs = rEngine.GetAttribs( aSel );
2224 
2225             tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight();
2226             tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight();
2227             tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight();
2228 
2229             nWestern = ( nWestern * nPercent ) / 100;
2230             nCJK     = ( nCJK     * nPercent ) / 100;
2231             nCTL     = ( nCTL     * nPercent ) / 100;
2232 
2233             aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
2234             aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
2235             aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );
2236 
2237             rEngine.QuickSetAttribs( aAttribs, aSel );      //! remove paragraph attributes from aAttribs?
2238 
2239             nStart = nEnd;
2240         }
2241     }
2242 
2243     if ( bUpdateMode )
2244         rEngine.SetUpdateLayout( true );
2245 }
2246 
2247 static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate )
2248 {
2249     if ( bSwap )
2250         bWidth = !bWidth;
2251 
2252     if ( nAttrRotate )
2253     {
2254         tools::Long nRealWidth  = static_cast<tools::Long>(rEngine.CalcTextWidth());
2255         tools::Long nRealHeight = rEngine.GetTextHeight();
2256 
2257         // assuming standard mode, otherwise width isn't used
2258 
2259         double nRealOrient = toRadians(nAttrRotate);   // 1/100th degrees
2260         double nAbsCos = fabs( cos( nRealOrient ) );
2261         double nAbsSin = fabs( sin( nRealOrient ) );
2262         if ( bWidth )
2263             return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
2264         else
2265             return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
2266     }
2267     else if ( bWidth )
2268         return static_cast<tools::Long>(rEngine.CalcTextWidth());
2269     else
2270         return rEngine.GetTextHeight();
2271 }
2272 
2273 void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect,
2274             tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
2275             bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
2276             tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
2277 {
2278     if ( !bWidth )
2279     {
2280         // vertical
2281 
2282         tools::Long nScaleSize = bPixelToLogic ?
2283             mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2284 
2285         // Don't scale if it fits already.
2286         // Allowing to extend into the margin, to avoid scaling at optimal height.
2287         if ( nScaleSize <= rAlignRect.GetHeight() )
2288             return;
2289 
2290         bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp );
2291         tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
2292         tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2293 
2294         lcl_ScaleFonts( rEngine, nScale );
2295         rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2296         tools::Long nNewSize = bPixelToLogic ?
2297             mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2298 
2299         sal_uInt16 nShrinkAgain = 0;
2300         while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2301         {
2302             // further reduce, like in DrawStrings
2303             lcl_ScaleFonts( rEngine, 90 );     // reduce by 10%
2304             rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2305             nNewSize = bPixelToLogic ?
2306                 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2307             ++nShrinkAgain;
2308         }
2309 
2310         // sizes for further processing (alignment etc):
2311         rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate );
2312         tools::Long nPixelWidth = bPixelToLogic ?
2313             mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2314         rNeededPixel = nPixelWidth + nLeftM + nRightM;
2315     }
2316     else if ( rLeftClip || rRightClip )
2317     {
2318         // horizontal
2319 
2320         tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
2321         tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM;      // without margin
2322 
2323         if ( nScaleSize <= nAvailable )
2324             return;
2325 
2326         tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2327 
2328         lcl_ScaleFonts( rEngine, nScale );
2329         rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2330         tools::Long nNewSize = bPixelToLogic ?
2331             mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2332 
2333         sal_uInt16 nShrinkAgain = 0;
2334         while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2335         {
2336             // further reduce, like in DrawStrings
2337             lcl_ScaleFonts( rEngine, 90 );     // reduce by 10%
2338             rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2339             nNewSize = bPixelToLogic ?
2340                 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2341             ++nShrinkAgain;
2342         }
2343         if ( nNewSize <= nAvailable )
2344             rLeftClip = rRightClip = false;
2345 
2346         // sizes for further processing (alignment etc):
2347         rNeededPixel = nNewSize + nLeftM + nRightM;
2348         rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate );
2349     }
2350 }
2351 
2352 ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue) :
2353     meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ),
2354     meHorJustContext( meHorJustAttr ),
2355     meHorJustResult( meHorJustAttr ),
2356     meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ),
2357     meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ),
2358     meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ),
2359     meOrient( pPattern->GetCellOrientation(pCondSet) ),
2360     mnArrY(0),
2361     mnX(0), mnCellX(0), mnCellY(0),
2362     mnPosX(0), mnPosY(0), mnInitPosX(0),
2363     mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ),
2364     mbCellIsValue(bCellIsValue),
2365     mbAsianVertical(false),
2366     mbPixelToLogic(false),
2367     mbHyphenatorSet(false),
2368     mpEngine(nullptr),
2369     mpPattern(pPattern),
2370     mpCondSet(pCondSet),
2371     mpPreviewFontSet(nullptr),
2372     mpOldPattern(nullptr),
2373     mpOldCondSet(nullptr),
2374     mpOldPreviewFontSet(nullptr),
2375     mpThisRowInfo(nullptr),
2376     mpMisspellRanges(nullptr)
2377 {}
2378 
2379 bool ScOutputData::DrawEditParam::readCellContent(
2380     const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields)
2381 {
2382     if (maCell.getType() == CELLTYPE_EDIT)
2383     {
2384         const EditTextObject* pData = maCell.getEditText();
2385         if (pData)
2386         {
2387             mpEngine->SetTextCurrentDefaults(*pData);
2388 
2389             if ( mbBreak && !mbAsianVertical && pData->HasField() )
2390             {
2391                 //  Fields aren't wrapped, so clipping is enabled to prevent
2392                 //  a field from being drawn beyond the cell size
2393 
2394                 rWrapFields = true;
2395             }
2396         }
2397         else
2398         {
2399             OSL_FAIL("pData == 0");
2400             return false;
2401         }
2402     }
2403     else
2404     {
2405         sal_uInt32 nFormat = mpPattern->GetNumberFormat(
2406                                     pDoc->GetFormatTable(), mpCondSet );
2407         const Color* pColor;
2408         OUString aString = ScCellFormat::GetString( maCell,
2409                                  nFormat, &pColor,
2410                                  *pDoc->GetFormatTable(),
2411                                  *pDoc,
2412                                  bShowNullValues,
2413                                  bShowFormulas);
2414 
2415         mpEngine->SetTextCurrentDefaults(aString);
2416         if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
2417             lcl_SetEditColor( *mpEngine, *pColor );
2418     }
2419 
2420     if (mpMisspellRanges)
2421         mpEngine->SetAllMisspellRanges(*mpMisspellRanges);
2422 
2423     return true;
2424 }
2425 
2426 void ScOutputData::DrawEditParam::setPatternToEngine(bool bUseStyleColor)
2427 {
2428     // syntax highlighting mode is ignored here
2429     // StringDiffer doesn't look at hyphenate, language items
2430 
2431     if (mpPattern == mpOldPattern && mpCondSet == mpOldCondSet && mpPreviewFontSet == mpOldPreviewFontSet )
2432         return;
2433 
2434     Color nConfBackColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2435     bool bCellContrast = bUseStyleColor &&
2436             Application::GetSettings().GetStyleSettings().GetHighContrastMode();
2437 
2438     auto pSet = std::make_unique<SfxItemSet>( mpEngine->GetEmptyItemSet() );
2439     mpPattern->FillEditItemSet( pSet.get(), mpCondSet );
2440     if ( mpPreviewFontSet )
2441     {
2442         if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) )
2443         {
2444             // tdf#125054 adapt WhichID
2445             pSet->Put(*pItem, EE_CHAR_FONTINFO);
2446         }
2447         if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
2448         {
2449             // tdf#125054 adapt WhichID
2450             pSet->Put(*pItem, EE_CHAR_FONTINFO_CJK);
2451         }
2452         if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
2453         {
2454             // tdf#125054 adapt WhichID
2455             pSet->Put(*pItem, EE_CHAR_FONTINFO_CTL);
2456         }
2457     }
2458     bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
2459     mpEngine->SetDefaults( std::move(pSet) );
2460     mpOldPattern = mpPattern;
2461     mpOldCondSet = mpCondSet;
2462     mpOldPreviewFontSet = mpPreviewFontSet;
2463 
2464     EEControlBits nControl = mpEngine->GetControlWord();
2465     if (meOrient == SvxCellOrientation::Stacked)
2466         nControl |= EEControlBits::ONECHARPERLINE;
2467     else
2468         nControl &= ~EEControlBits::ONECHARPERLINE;
2469     mpEngine->SetControlWord( nControl );
2470 
2471     if ( !mbHyphenatorSet && bParaHyphenate )
2472     {
2473         //  set hyphenator the first time it is needed
2474         css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
2475         mpEngine->SetHyphenator( xXHyphenator );
2476         mbHyphenatorSet = true;
2477     }
2478 
2479     Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor();
2480     if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
2481         aBackCol = nConfBackColor;
2482     mpEngine->SetBackgroundColor( aBackCol );
2483 }
2484 
2485 void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const
2486 {
2487     const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet);
2488 
2489     sal_uInt16 nIndent = 0;
2490     if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right)
2491         nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet);
2492 
2493     rLeftM   = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX));
2494     rTopM    = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY));
2495     rRightM  = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX));
2496     rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY));
2497     if(meHorJustAttr == SvxCellHorJustify::Right)
2498     {
2499         rLeftM   = static_cast<tools::Long>((rMargin.GetLeftMargin()  * nPPTX));
2500         rRightM  = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX));
2501     }
2502 }
2503 
2504 void ScOutputData::DrawEditParam::calcPaperSize(
2505     Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const
2506 {
2507     tools::Long nTopM, nLeftM, nBottomM, nRightM;
2508     calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY);
2509 
2510     if (isVerticallyOriented())
2511     {
2512         rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM );
2513         rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM );
2514     }
2515     else
2516     {
2517         rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM );
2518         rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2519     }
2520 
2521     if (mbAsianVertical)
2522     {
2523         rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2524         // Subtract some extra value from the height or else the text would go
2525         // outside the cell area.  The value of 5 is arbitrary, and is based
2526         // entirely on heuristics.
2527         rPaperSize.AdjustHeight( -5 );
2528     }
2529 }
2530 
2531 void ScOutputData::DrawEditParam::getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const
2532 {
2533     tools::Long nEngineWidth = 0;
2534     if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical)
2535         nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth());
2536 
2537     tools::Long nEngineHeight = pEngine->GetTextHeight();
2538 
2539     if (isVerticallyOriented())
2540         std::swap( nEngineWidth, nEngineHeight );
2541 
2542     if (meOrient == SvxCellOrientation::Stacked)
2543         nEngineWidth = nEngineWidth * 11 / 10;
2544 
2545     rWidth = nEngineWidth;
2546     rHeight = nEngineHeight;
2547 }
2548 
2549 bool ScOutputData::DrawEditParam::hasLineBreak() const
2550 {
2551     return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical);
2552 }
2553 
2554 bool ScOutputData::DrawEditParam::isHyperlinkCell() const
2555 {
2556     if (maCell.getType() != CELLTYPE_FORMULA)
2557         return false;
2558 
2559     return maCell.getFormula()->IsHyperLinkCell();
2560 }
2561 
2562 bool ScOutputData::DrawEditParam::isVerticallyOriented() const
2563 {
2564     return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp);
2565 }
2566 
2567 void ScOutputData::DrawEditParam::calcStartPosForVertical(
2568     Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice)
2569 {
2570     OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!");
2571 
2572     if (mbPixelToLogic)
2573         rLogicStart = pRefDevice->PixelToLogic(rLogicStart);
2574 
2575     if (!mbBreak)
2576         return;
2577 
2578     // vertical adjustment is within the EditEngine
2579     if (mbPixelToLogic)
2580         rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
2581     else
2582         rLogicStart.AdjustY(nTopM );
2583 
2584     switch (meHorJustResult)
2585     {
2586         case SvxCellHorJustify::Center:
2587             rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 );
2588         break;
2589         case SvxCellHorJustify::Right:
2590             rLogicStart.AdjustX(nCellWidth - nEngineWidth );
2591         break;
2592         default:
2593             ; // do nothing
2594     }
2595 }
2596 
2597 void ScOutputData::DrawEditParam::setAlignmentToEngine()
2598 {
2599     if (isVerticallyOriented() || mbAsianVertical)
2600     {
2601         SvxAdjust eSvxAdjust = SvxAdjust::Left;
2602         switch (meVerJust)
2603         {
2604             case SvxCellVerJustify::Top:
2605                 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2606                             SvxAdjust::Left : SvxAdjust::Right;
2607                 break;
2608             case SvxCellVerJustify::Center:
2609                 eSvxAdjust = SvxAdjust::Center;
2610                 break;
2611             case SvxCellVerJustify::Bottom:
2612             case SvxCellVerJustify::Standard:
2613                 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2614                             SvxAdjust::Right : SvxAdjust::Left;
2615                 break;
2616             case SvxCellVerJustify::Block:
2617                 eSvxAdjust = SvxAdjust::Block;
2618                 break;
2619         }
2620 
2621         mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2622         mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2623 
2624         if (meHorJustResult == SvxCellHorJustify::Block)
2625             mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2626     }
2627     else
2628     {
2629         //  horizontal alignment now may depend on cell content
2630         //  (for values with number formats with mixed script types)
2631         //  -> always set adjustment
2632 
2633         SvxAdjust eSvxAdjust = SvxAdjust::Left;
2634         if (meOrient == SvxCellOrientation::Stacked)
2635             eSvxAdjust = SvxAdjust::Center;
2636         else if (mbBreak)
2637         {
2638             if (meOrient == SvxCellOrientation::Standard)
2639                 switch (meHorJustResult)
2640                 {
2641                     case SvxCellHorJustify::Repeat:            // repeat is not yet implemented
2642                     case SvxCellHorJustify::Standard:
2643                         SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()");
2644                         [[fallthrough]];
2645                     case SvxCellHorJustify::Left:
2646                         eSvxAdjust = SvxAdjust::Left;
2647                         break;
2648                     case SvxCellHorJustify::Center:
2649                         eSvxAdjust = SvxAdjust::Center;
2650                         break;
2651                     case SvxCellHorJustify::Right:
2652                         eSvxAdjust = SvxAdjust::Right;
2653                         break;
2654                     case SvxCellHorJustify::Block:
2655                         eSvxAdjust = SvxAdjust::Block;
2656                         break;
2657                 }
2658             else
2659                 switch (meVerJust)
2660                 {
2661                     case SvxCellVerJustify::Top:
2662                         eSvxAdjust = SvxAdjust::Right;
2663                         break;
2664                     case SvxCellVerJustify::Center:
2665                         eSvxAdjust = SvxAdjust::Center;
2666                         break;
2667                     case SvxCellVerJustify::Bottom:
2668                     case SvxCellVerJustify::Standard:
2669                         eSvxAdjust = SvxAdjust::Left;
2670                         break;
2671                     case SvxCellVerJustify::Block:
2672                         eSvxAdjust = SvxAdjust::Block;
2673                         break;
2674                 }
2675         }
2676 
2677         mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2678 
2679         if (mbAsianVertical)
2680         {
2681             mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2682             if (meHorJustResult == SvxCellHorJustify::Block)
2683                 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2684         }
2685         else
2686         {
2687             mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) );
2688             if (meVerJust == SvxCellVerJustify::Block)
2689                 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2690         }
2691     }
2692 
2693     mpEngine->SetVertical(mbAsianVertical);
2694     if (maCell.getType() == CELLTYPE_EDIT)
2695     {
2696         // We need to synchronize the vertical mode in the EditTextObject
2697         // instance too.  No idea why we keep this state in two separate
2698         // instances.
2699         const EditTextObject* pData = maCell.getEditText();
2700         if (pData)
2701             const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical);
2702     }
2703 }
2704 
2705 bool ScOutputData::DrawEditParam::adjustHorAlignment(ScFieldEditEngine* pEngine)
2706 {
2707     if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center)
2708     {
2709         SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ?
2710             SvxAdjust::Center : SvxAdjust::Right;
2711 
2712         const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false);
2713         pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) );
2714         pEngine->SetUpdateLayout(bPrevUpdateLayout);
2715         return true;
2716     }
2717     return false;
2718 }
2719 
2720 void ScOutputData::DrawEditParam::adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev)
2721 {
2722     // PDF: whole-cell hyperlink from formula?
2723     vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
2724     bool bHasURL = pPDFData && isHyperlinkCell();
2725     if (!bHasURL)
2726         return;
2727 
2728     tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth());
2729     tools::Long nURLHeight = mpEngine->GetTextHeight();
2730     if (mbBreak)
2731     {
2732         Size aPaper = mpEngine->GetPaperSize();
2733         if ( mbAsianVertical )
2734             nURLHeight = aPaper.Height();
2735         else
2736             nURLWidth = aPaper.Width();
2737     }
2738     if (isVerticallyOriented())
2739         std::swap( nURLWidth, nURLHeight );
2740     else if (mbAsianVertical)
2741         aURLStart.AdjustX( -nURLWidth );
2742 
2743     tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
2744     lcl_DoHyperlinkResult(pDev, aURLRect, maCell);
2745 }
2746 
2747 // Returns true if the rect is clipped vertically
2748 bool ScOutputData::AdjustAreaParamClipRect(OutputAreaParam& rAreaParam)
2749 {
2750     if( rAreaParam.maClipRect.Left() < nScrX )
2751     {
2752         rAreaParam.maClipRect.SetLeft( nScrX );
2753         rAreaParam.mbLeftClip = true;
2754     }
2755     if( rAreaParam.maClipRect.Right() > nScrX + nScrW )
2756     {
2757         rAreaParam.maClipRect.SetRight( nScrX + nScrW );          //! minus one?
2758         rAreaParam.mbRightClip = true;
2759     }
2760 
2761     bool bVClip = false;
2762 
2763     if( rAreaParam.maClipRect.Top() < nScrY )
2764     {
2765         rAreaParam.maClipRect.SetTop( nScrY );
2766         bVClip = true;
2767     }
2768     if( rAreaParam.maClipRect.Bottom() > nScrY + nScrH )
2769     {
2770         rAreaParam.maClipRect.SetBottom( nScrY + nScrH );     //! minus one?
2771         bVClip = true;
2772     }
2773     return bVClip;
2774 }
2775 
2776 // Doesn't handle clip marks - should be handled in advance using GetOutputArea
2777 class ClearableClipRegion
2778 {
2779 public:
2780     ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip,
2781                         const VclPtr<OutputDevice>& pDev, bool bMetaFile )
2782         :mbMetaFile(bMetaFile)
2783     {
2784         if (!(bClip || bSimClip))
2785             return;
2786 
2787         maRect = rRect;
2788         if (bClip)  // for bSimClip only initialize aClipRect
2789         {
2790             mpDev.reset(pDev);
2791             if (mbMetaFile)
2792             {
2793                 mpDev->Push();
2794                 mpDev->IntersectClipRegion(maRect);
2795             }
2796             else
2797                 mpDev->SetClipRegion(vcl::Region(maRect));
2798         }
2799     }
2800 
2801     ~ClearableClipRegion() COVERITY_NOEXCEPT_FALSE
2802     {
2803         // Pop() or SetClipRegion() must only be called in case bClip was true
2804         // in the ctor, and only then mpDev is set.
2805         if (mpDev)
2806         {
2807             if (mbMetaFile)
2808                 mpDev->Pop();
2809             else
2810                 mpDev->SetClipRegion();
2811         }
2812     }
2813 
2814     const tools::Rectangle& getRect() const { return maRect; }
2815 
2816 private:
2817     tools::Rectangle        maRect;
2818     VclPtr<OutputDevice>    mpDev;
2819     bool                    mbMetaFile;
2820 };
2821 
2822 // Returns needed width in current units; sets rNeededPixel to needed width in pixels
2823 tools::Long ScOutputData::SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
2824                                              tools::Long& rNeededPixel, tools::Long nAddWidthPixels )
2825 {
2826     rParam.mpEngine->SetTextCurrentDefaults( rSetString );
2827     tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() );
2828     if ( rParam.mbPixelToLogic )
2829         rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width();
2830     else
2831         rNeededPixel = nEngineWidth;
2832 
2833     rNeededPixel += nAddWidthPixels;
2834 
2835     return nEngineWidth;
2836 }
2837 
2838 void ScOutputData::DrawEditStandard(DrawEditParam& rParam)
2839 {
2840     OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
2841     OSL_ASSERT(!rParam.mbAsianVertical);
2842 
2843     Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
2844 
2845     bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
2846     bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
2847     Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
2848 
2849     if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat )
2850     {
2851         // ignore orientation/rotation if "repeat" is active
2852         rParam.meOrient = SvxCellOrientation::Standard;
2853         nAttrRotate = 0_deg100;
2854 
2855         // #i31843# "repeat" with "line breaks" is treated as default alignment
2856         // (but rotation is still disabled).
2857         // Default again leads to context dependent alignment instead of
2858         // SvxCellHorJustify::Standard.
2859         if ( rParam.mbBreak )
2860             rParam.meHorJustResult = rParam.meHorJustContext;
2861     }
2862 
2863     if (nAttrRotate)
2864     {
2865         //! set flag to find the cell in DrawRotated again ?
2866         //! (or flag already set during DrawBackground, then no query here)
2867         return;     // rotated is outputted separately
2868     }
2869 
2870     SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
2871 
2872     //! mirror margin values for RTL?
2873     //! move margin down to after final GetOutputArea call
2874     tools::Long nTopM, nLeftM, nBottomM, nRightM;
2875     rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
2876 
2877     SCCOL nXForPos = rParam.mnX;
2878     if ( nXForPos < nX1 )
2879     {
2880         nXForPos = nX1;
2881         rParam.mnPosX = rParam.mnInitPosX;
2882     }
2883     SCSIZE nArrYForPos = rParam.mnArrY;
2884     if ( nArrYForPos < 1 )
2885     {
2886         nArrYForPos = 1;
2887         rParam.mnPosY = nScrY;
2888     }
2889 
2890     OutputAreaParam aAreaParam;
2891 
2892     //  Initial page size - large for normal text, cell size for automatic line breaks
2893 
2894     Size aPaperSize( 1000000, 1000000 );
2895     if (rParam.mbBreak)
2896     {
2897         //  call GetOutputArea with nNeeded=0, to get only the cell width
2898 
2899         //! handle nArrY == 0
2900         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
2901                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
2902                        rParam.mbCellIsValue, true, false, aAreaParam );
2903 
2904         //! special ScEditUtil handling if formatting for printer
2905         rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
2906     }
2907     if (rParam.mbPixelToLogic)
2908     {
2909         Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
2910         if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
2911         {
2912             // #i85342# screen display and formatting for printer,
2913             // use same GetEditArea call as in ScViewData::SetEditEngine
2914 
2915             Fraction aFract(1,1);
2916             tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
2917                 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
2918             aLogicSize.setWidth( aUtilRect.GetWidth() );
2919         }
2920         rParam.mpEngine->SetPaperSize(aLogicSize);
2921     }
2922     else
2923         rParam.mpEngine->SetPaperSize(aPaperSize);
2924 
2925     //  Fill the EditEngine (cell attributes and text)
2926 
2927     // default alignment for asian vertical mode is top-right
2928     if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard )
2929         rParam.meVerJust = SvxCellVerJustify::Top;
2930 
2931     rParam.setPatternToEngine(mbUseStyleColor);
2932     rParam.setAlignmentToEngine();
2933     // Don't format unnecessary parts if the text will be drawn from top (Standard will
2934     // act that way if text doesn't fit, see below).
2935     rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top
2936         || rParam.meVerJust==SvxCellVerJustify::Standard);
2937 
2938     //  Read content from cell
2939 
2940     bool bWrapFields = false;
2941     if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
2942         // Failed to read cell content.  Bail out.
2943         return;
2944 
2945     if ( mbSyntaxMode )
2946         SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell);
2947     else if ( mbUseStyleColor && mbForceAutoColor )
2948         lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );     //! or have a flag at EditEngine
2949 
2950     rParam.mpEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
2951 
2952     //  Get final output area using the calculated width
2953 
2954     tools::Long nEngineWidth, nEngineHeight;
2955     rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
2956 
2957     tools::Long nNeededPixel = nEngineWidth;
2958     if (rParam.mbPixelToLogic)
2959         nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
2960     nNeededPixel += nLeftM + nRightM;
2961 
2962     if (!rParam.mbBreak || bShrink)
2963     {
2964         // for break, the first GetOutputArea call is sufficient
2965         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
2966                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
2967                        rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
2968 
2969         if ( bShrink )
2970         {
2971             ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
2972                 nLeftM, nTopM, nRightM, nBottomM, true,
2973                 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
2974                 nEngineWidth, nEngineHeight, nNeededPixel,
2975                 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
2976         }
2977         if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
2978         {
2979             // First check if twice the space for the formatted text is available
2980             // (otherwise just keep it unchanged).
2981 
2982             tools::Long nFormatted = nNeededPixel - nLeftM - nRightM;      // without margin
2983             tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
2984             if ( nAvailable >= 2 * nFormatted )
2985             {
2986                 // "repeat" is handled with unformatted text (for performance reasons)
2987                 OUString aCellStr = rParam.mpEngine->GetText();
2988 
2989                 tools::Long nRepeatSize = 0;
2990                 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
2991                 if ( pFmtDevice != mpRefDevice )
2992                     ++nRepeatSize;
2993                 if ( nRepeatSize > 0 )
2994                 {
2995                     tools::Long nRepeatCount = nAvailable / nRepeatSize;
2996                     if ( nRepeatCount > 1 )
2997                     {
2998                         OUStringBuffer aRepeated(aCellStr);
2999                         for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3000                             aRepeated.append(aCellStr);
3001 
3002                         SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3003                                                   nNeededPixel, (nLeftM + nRightM ) );
3004 
3005                         nEngineHeight = rParam.mpEngine->GetTextHeight();
3006                     }
3007                 }
3008             }
3009         }
3010 
3011 
3012         if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3013         {
3014             SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
3015 
3016             //  No clip marks if "###" doesn't fit (same as in DrawStrings)
3017         }
3018 
3019         if (eOutHorJust != SvxCellHorJustify::Left)
3020         {
3021             aPaperSize.setWidth( nNeededPixel + 1 );
3022             if (rParam.mbPixelToLogic)
3023                 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
3024             else
3025                 rParam.mpEngine->SetPaperSize(aPaperSize);
3026         }
3027     }
3028 
3029     tools::Long nStartX = aAreaParam.maAlignRect.Left();
3030     tools::Long nStartY = aAreaParam.maAlignRect.Top();
3031     tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3032     tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3033     tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3034 
3035     if (rParam.mbBreak)
3036     {
3037         //  text with automatic breaks is aligned only within the
3038         //  edit engine's paper size, the output of the whole area
3039         //  is always left-aligned
3040 
3041         nStartX += nLeftM;
3042     }
3043     else
3044     {
3045         if ( eOutHorJust == SvxCellHorJustify::Right )
3046             nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3047         else if ( eOutHorJust == SvxCellHorJustify::Center )
3048             nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3049         else
3050             nStartX += nLeftM;
3051     }
3052 
3053     bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3054     if (bOutside)
3055         return;
3056 
3057     // Also take fields in a cell with automatic breaks into account: clip to cell width
3058     bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3059     bool bSimClip = false;
3060 
3061     Size aCellSize;         // output area, excluding margins, in logical units
3062     if (rParam.mbPixelToLogic)
3063         aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
3064     else
3065         aCellSize = Size( nOutWidth, nOutHeight );
3066 
3067     if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
3068     {
3069         const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3070         bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3071 
3072         //  Don't clip for text height when printing rows with optimal height,
3073         //  except when font size is from conditional formatting.
3074         //! Allow clipping when vertically merged?
3075         if ( eType != OUTTYPE_PRINTER ||
3076             ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3077             ( rParam.mpCondSet && SfxItemState::SET ==
3078                 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
3079             bClip = true;
3080         else
3081             bSimClip = true;
3082 
3083         //  Show clip marks if height is at least 5pt too small and
3084         //  there are several lines of text.
3085         //  Not for asian vertical text, because that would interfere
3086         //  with the default right position of the text.
3087         //  Only with automatic line breaks, to avoid having to find
3088         //  the cells with the horizontal end of the text again.
3089         if ( nEngineHeight - aCellSize.Height() > 100 &&
3090              rParam.mbBreak && bMarkClipped &&
3091              ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
3092         {
3093             ScCellInfo* pClipMarkCell = nullptr;
3094             if ( bMerged )
3095             {
3096                 //  anywhere in the merged area...
3097                 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
3098                 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3099             }
3100             else
3101                 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3102 
3103             pClipMarkCell->nClipMark |= ScClipMark::Right;      //! also allow left?
3104             bAnyClipped = true;
3105 
3106             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
3107             if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
3108                 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
3109 
3110             // Standard is normally treated as Bottom, but if text height is clipped, then
3111             // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat().
3112             if (rParam.meVerJust==SvxCellVerJustify::Standard)
3113                 rParam.meVerJust=SvxCellVerJustify::Top;
3114         }
3115     }
3116 
3117     Point aURLStart;
3118 
3119     {   // Clip marks are already handled in GetOutputArea
3120         ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
3121                                 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
3122 
3123         Point aLogicStart;
3124         if (rParam.mbPixelToLogic)
3125             aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
3126         else
3127             aLogicStart = Point(nStartX, nStartY);
3128 
3129         if (!rParam.mbBreak)
3130         {
3131             //  horizontal alignment
3132             if (rParam.adjustHorAlignment(rParam.mpEngine))
3133                 // reset adjustment for the next cell
3134                 rParam.mpOldPattern = nullptr;
3135         }
3136 
3137         if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
3138             rParam.meVerJust==SvxCellVerJustify::Standard)
3139         {
3140             //! if pRefDevice != pFmtDevice, keep heights in logic units,
3141             //! only converting margin?
3142 
3143             if (rParam.mbPixelToLogic)
3144                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
3145                                 mpRefDevice->LogicToPixel(aCellSize).Height() -
3146                                 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
3147                                 )).Height() );
3148             else
3149                 aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
3150         }
3151         else if (rParam.meVerJust==SvxCellVerJustify::Center)
3152         {
3153             if (rParam.mbPixelToLogic)
3154                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
3155                                 mpRefDevice->LogicToPixel(aCellSize).Height() -
3156                                 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
3157                                 / 2)).Height() );
3158             else
3159                 aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
3160         }
3161         else        // top
3162         {
3163             if (rParam.mbPixelToLogic)
3164                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
3165             else
3166                 aLogicStart.AdjustY(nTopM );
3167         }
3168 
3169         aURLStart = aLogicStart;      // copy before modifying for orientation
3170 
3171         // bMoveClipped handling has been replaced by complete alignment
3172         // handling (also extending to the left).
3173 
3174         if (bSimClip)
3175         {
3176             // no hard clip, only draw the affected rows
3177             Point aDocStart = aClip.getRect().TopLeft();
3178             aDocStart -= aLogicStart;
3179             rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
3180         }
3181         else
3182         {
3183             rParam.mpEngine->Draw(*mpDev, aLogicStart);
3184         }
3185     }
3186 
3187     rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3188 }
3189 
3190 void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
3191                                   bool bMerged, OutputAreaParam& aAreaParam, bool bTop)
3192 {
3193     //  Show clip marks if width is at least 5pt too small and
3194     //  there are several lines of text.
3195     //  Not for asian vertical text, because that would interfere
3196     //  with the default right position of the text.
3197     //  Only with automatic line breaks, to avoid having to find
3198     //  the cells with the horizontal end of the text again.
3199     if (nEngineWidth - aCellSize.Width() <= 100 || !rParam.mbBreak || !bMarkClipped
3200         || (rParam.mpEngine->GetParagraphCount() <= 1 && rParam.mpEngine->GetLineCount(0) <= 1))
3201         return;
3202 
3203     ScCellInfo* pClipMarkCell = nullptr;
3204     if (bMerged)
3205     {
3206         //  anywhere in the merged area...
3207         SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
3208         pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3209     }
3210     else
3211         pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3212 
3213     bAnyClipped = true;
3214     bVertical = true;
3215     const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
3216     if (bTop)
3217     {
3218         pClipMarkCell->nClipMark |= ScClipMark::Top;
3219         if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3220             aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
3221     }
3222     else
3223     {
3224         pClipMarkCell->nClipMark |= ScClipMark::Bottom;
3225         if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3226             aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
3227     }
3228 }
3229 
3230 ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize,
3231                                                         OutputAreaParam& aAreaParam, tools::Long nEngineWidth,
3232                                                         bool bWrapFields, bool bTop)
3233 {
3234     // Also take fields in a cell with automatic breaks into account: clip to cell width
3235     bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3236     bool bSimClip = false;
3237 
3238     const Size& aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
3239     if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() )
3240     {
3241         const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3242         const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3243 
3244         //  Don't clip for text height when printing rows with optimal height,
3245         //  except when font size is from conditional formatting.
3246         //! Allow clipping when vertically merged?
3247         if ( eType != OUTTYPE_PRINTER ||
3248             ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3249             ( rParam.mpCondSet && SfxItemState::SET ==
3250                 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
3251             bClip = true;
3252         else
3253             bSimClip = true;
3254 
3255         ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop);
3256     }
3257 
3258         // Clip marks are already handled in GetOutputArea
3259     return ClearableClipRegionPtr(new ClearableClipRegion(rParam.mbPixelToLogic ?
3260                                                 mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
3261                                               : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile));
3262 }
3263 
3264 void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam)
3265 {
3266     OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3267 
3268     const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3269     const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3270 
3271     SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3272 
3273     //! mirror margin values for RTL?
3274     //! move margin down to after final GetOutputArea call
3275     tools::Long nTopM, nLeftM, nBottomM, nRightM;
3276     rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3277 
3278     SCCOL nXForPos = rParam.mnX;
3279     if ( nXForPos < nX1 )
3280     {
3281         nXForPos = nX1;
3282         rParam.mnPosX = rParam.mnInitPosX;
3283     }
3284     SCSIZE nArrYForPos = rParam.mnArrY;
3285     if ( nArrYForPos < 1 )
3286     {
3287         nArrYForPos = 1;
3288         rParam.mnPosY = nScrY;
3289     }
3290 
3291     OutputAreaParam aAreaParam;
3292 
3293     //  Initial page size - large for normal text, cell size for automatic line breaks
3294 
3295     Size aPaperSize( 1000000, 1000000 );
3296     if (rParam.mbBreak)
3297     {
3298         //  call GetOutputArea with nNeeded=0, to get only the cell width
3299 
3300         //! handle nArrY == 0
3301         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3302                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3303                        rParam.mbCellIsValue, true, false, aAreaParam );
3304 
3305         //! special ScEditUtil handling if formatting for printer
3306         rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3307     }
3308     if (rParam.mbPixelToLogic)
3309     {
3310         Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3311         rParam.mpEngine->SetPaperSize(aLogicSize);
3312     }
3313     else
3314         rParam.mpEngine->SetPaperSize(aPaperSize);
3315 
3316     //  Fill the EditEngine (cell attributes and text)
3317 
3318     rParam.setPatternToEngine(mbUseStyleColor);
3319     rParam.setAlignmentToEngine();
3320 
3321     //  Read content from cell
3322 
3323     bool bWrapFields = false;
3324     if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3325         // Failed to read cell content.  Bail out.
3326         return;
3327 
3328     if ( mbSyntaxMode )
3329         SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3330     else if ( mbUseStyleColor && mbForceAutoColor )
3331         lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );     //! or have a flag at EditEngine
3332 
3333     rParam.mpEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
3334 
3335     //  Get final output area using the calculated width
3336 
3337     tools::Long nEngineWidth, nEngineHeight;
3338     rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3339 
3340     tools::Long nNeededPixel = nEngineWidth;
3341     if (rParam.mbPixelToLogic)
3342         nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3343     nNeededPixel += nLeftM + nRightM;
3344 
3345     if (!rParam.mbBreak || bShrink)
3346     {
3347         // for break, the first GetOutputArea call is sufficient
3348         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3349                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3350                        rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
3351 
3352         if ( bShrink )
3353         {
3354             ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3355                 nLeftM, nTopM, nRightM, nBottomM, false,
3356                 (rParam.meOrient), 0_deg100, rParam.mbPixelToLogic,
3357                 nEngineWidth, nEngineHeight, nNeededPixel,
3358                 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3359         }
3360         if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
3361         {
3362             // First check if twice the space for the formatted text is available
3363             // (otherwise just keep it unchanged).
3364 
3365             const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM;      // without margin
3366             const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
3367             if ( nAvailable >= 2 * nFormatted )
3368             {
3369                 // "repeat" is handled with unformatted text (for performance reasons)
3370                 OUString aCellStr = rParam.mpEngine->GetText();
3371 
3372                 tools::Long nRepeatSize = 0;
3373                 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
3374                 if ( pFmtDevice != mpRefDevice )
3375                     ++nRepeatSize;
3376                 if ( nRepeatSize > 0 )
3377                 {
3378                     const tools::Long nRepeatCount = nAvailable / nRepeatSize;
3379                     if ( nRepeatCount > 1 )
3380                     {
3381                         OUStringBuffer aRepeated(aCellStr);
3382                         for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3383                             aRepeated.append(aCellStr);
3384 
3385                         nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3386                                                             nNeededPixel, (nLeftM + nRightM ) );
3387 
3388                         nEngineHeight = rParam.mpEngine->GetTextHeight();
3389                     }
3390                 }
3391             }
3392         }
3393         if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3394         {
3395             nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
3396 
3397             //  No clip marks if "###" doesn't fit (same as in DrawStrings)
3398         }
3399     }
3400 
3401     tools::Long nStartX = aAreaParam.maAlignRect.Left();
3402     const tools::Long nStartY = aAreaParam.maAlignRect.Top();
3403     const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3404     const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3405     const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3406 
3407     if (rParam.mbBreak)
3408     {
3409         //  text with automatic breaks is aligned only within the
3410         //  edit engine's paper size, the output of the whole area
3411         //  is always left-aligned
3412 
3413         nStartX += nLeftM;
3414     }
3415     else
3416     {
3417         if ( eOutHorJust == SvxCellHorJustify::Right )
3418             nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3419         else if ( eOutHorJust == SvxCellHorJustify::Center )
3420             nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3421         else
3422             nStartX += nLeftM;
3423     }
3424 
3425     const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3426     if (bOutside)
3427         return;
3428 
3429     // output area, excluding margins, in logical units
3430     const Size& aCellSize = rParam.mbPixelToLogic
3431         ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
3432         : Size( nOutWidth, nOutHeight );
3433 
3434     Point aURLStart;
3435 
3436     {
3437         const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true );
3438 
3439         Point aLogicStart(nStartX, nStartY);
3440         rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
3441 
3442         aURLStart = aLogicStart;      // copy before modifying for orientation
3443 
3444         if (rParam.meHorJustResult == SvxCellHorJustify::Block || rParam.mbBreak)
3445         {
3446             Size aPSize = rParam.mpEngine->GetPaperSize();
3447             aPSize.setWidth( aCellSize.Height() );
3448             rParam.mpEngine->SetPaperSize(aPSize);
3449             aLogicStart.AdjustY(
3450                 rParam.mbBreak ? aPSize.Width() : nEngineHeight );
3451         }
3452         else
3453         {
3454             // Note that the "paper" is rotated 90 degrees to the left, so
3455             // paper's width is in vertical direction.  Also, the whole text
3456             // is on a single line, as text wrap is not in effect.
3457 
3458             // Set the paper width to be the width of the text.
3459             Size aPSize = rParam.mpEngine->GetPaperSize();
3460             aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
3461             rParam.mpEngine->SetPaperSize(aPSize);
3462 
3463             tools::Long nGap = 0;
3464             tools::Long nTopOffset = 0;
3465             if (rParam.mbPixelToLogic)
3466             {
3467                 nGap = mpRefDevice->LogicToPixel(aCellSize).Height() - mpRefDevice->LogicToPixel(aPSize).Width();
3468                 nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
3469                 nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
3470             }
3471             else
3472             {
3473                 nGap = aCellSize.Height() - aPSize.Width();
3474                 nTopOffset = nTopM;
3475             }
3476 
3477             // First, align text to bottom.
3478             aLogicStart.AdjustY(aCellSize.Height() );
3479             aLogicStart.AdjustY(nTopOffset );
3480 
3481             switch (rParam.meVerJust)
3482             {
3483                 case SvxCellVerJustify::Standard:
3484                 case SvxCellVerJustify::Bottom:
3485                     // align to bottom (do nothing).
3486                 break;
3487                 case SvxCellVerJustify::Center:
3488                     // center it.
3489                     aLogicStart.AdjustY( -(nGap / 2) );
3490                 break;
3491                 case SvxCellVerJustify::Block:
3492                 case SvxCellVerJustify::Top:
3493                     // align to top
3494                     aLogicStart.AdjustY( -nGap );
3495                 break;
3496                 default:
3497                     ;
3498             }
3499         }
3500 
3501         rParam.mpEngine->Draw(*mpDev, aLogicStart, 900_deg10);
3502     }
3503 
3504     rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3505 }
3506 
3507 void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam)
3508 {
3509     OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3510 
3511     const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3512     const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3513 
3514     SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3515 
3516     //! mirror margin values for RTL?
3517     //! move margin down to after final GetOutputArea call
3518     tools::Long nTopM, nLeftM, nBottomM, nRightM;
3519     rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3520 
3521     SCCOL nXForPos = rParam.mnX;
3522     if ( nXForPos < nX1 )
3523     {
3524         nXForPos = nX1;
3525         rParam.mnPosX = rParam.mnInitPosX;
3526     }
3527     SCSIZE nArrYForPos = rParam.mnArrY;
3528     if ( nArrYForPos < 1 )
3529     {
3530         nArrYForPos = 1;
3531         rParam.mnPosY = nScrY;
3532     }
3533 
3534     OutputAreaParam aAreaParam;
3535 
3536     //  Initial page size - large for normal text, cell size for automatic line breaks
3537 
3538     Size aPaperSize( 1000000, 1000000 );
3539     if (rParam.hasLineBreak())
3540     {
3541         //  call GetOutputArea with nNeeded=0, to get only the cell width
3542 
3543         //! handle nArrY == 0
3544         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3545                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3546                        rParam.mbCellIsValue, true, false, aAreaParam );
3547 
3548         //! special ScEditUtil handling if formatting for printer
3549         rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3550     }
3551     if (rParam.mbPixelToLogic)
3552     {
3553         Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3554         rParam.mpEngine->SetPaperSize(aLogicSize);
3555     }
3556     else
3557         rParam.mpEngine->SetPaperSize(aPaperSize);
3558 
3559     //  Fill the EditEngine (cell attributes and text)
3560 
3561     rParam.setPatternToEngine(mbUseStyleColor);
3562     rParam.setAlignmentToEngine();
3563 
3564     //  Read content from cell
3565 
3566     bool bWrapFields = false;
3567     if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3568         // Failed to read cell content.  Bail out.
3569         return;
3570 
3571     if ( mbSyntaxMode )
3572         SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3573     else if ( mbUseStyleColor && mbForceAutoColor )
3574         lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );     //! or have a flag at EditEngine
3575 
3576     rParam.mpEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
3577 
3578     //  Get final output area using the calculated width
3579 
3580     tools::Long nEngineWidth, nEngineHeight;
3581     rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3582 
3583     tools::Long nNeededPixel = nEngineWidth;
3584     if (rParam.mbPixelToLogic)
3585         nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3586     nNeededPixel += nLeftM + nRightM;
3587 
3588     if (!rParam.mbBreak || bShrink)
3589     {
3590         // for break, the first GetOutputArea call is sufficient
3591         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3592                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3593                        rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
3594 
3595         if ( bShrink )
3596         {
3597             ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3598                 nLeftM, nTopM, nRightM, nBottomM, false,
3599                 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
3600                 nEngineWidth, nEngineHeight, nNeededPixel,
3601                 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3602         }
3603         if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
3604         {
3605             // First check if twice the space for the formatted text is available
3606             // (otherwise just keep it unchanged).
3607 
3608             const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM;      // without margin
3609             const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
3610             if ( nAvailable >= 2 * nFormatted )
3611             {
3612                 // "repeat" is handled with unformatted text (for performance reasons)
3613                 OUString aCellStr = rParam.mpEngine->GetText();
3614 
3615                 tools::Long nRepeatSize = 0;
3616                 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
3617 
3618                 if ( pFmtDevice != mpRefDevice )
3619                     ++nRepeatSize;
3620                 if ( nRepeatSize > 0 )
3621                 {
3622                     const tools::Long nRepeatCount = nAvailable / nRepeatSize;
3623                     if ( nRepeatCount > 1 )
3624                     {
3625                         OUStringBuffer aRepeated(aCellStr);
3626                         for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
3627                             aRepeated.append(aCellStr);
3628 
3629                         nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3630                                                             nNeededPixel, (nLeftM + nRightM ) );
3631 
3632                         nEngineHeight = rParam.mpEngine->GetTextHeight();
3633                     }
3634                 }
3635             }
3636         }
3637         if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3638         {
3639             nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
3640 
3641             //  No clip marks if "###" doesn't fit (same as in DrawStrings)
3642         }
3643     }
3644 
3645     tools::Long nStartX = aAreaParam.maAlignRect.Left();
3646     const tools::Long nStartY = aAreaParam.maAlignRect.Top();
3647     const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3648     const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3649     const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3650 
3651     if (rParam.mbBreak)
3652     {
3653         //  text with automatic breaks is aligned only within the
3654         //  edit engine's paper size, the output of the whole area
3655         //  is always left-aligned
3656 
3657         nStartX += nLeftM;
3658         if (rParam.meHorJustResult == SvxCellHorJustify::Block)
3659             nStartX += aPaperSize.Height();
3660     }
3661     else
3662     {
3663         if ( eOutHorJust == SvxCellHorJustify::Right )
3664             nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3665         else if ( eOutHorJust == SvxCellHorJustify::Center )
3666             nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3667         else
3668             nStartX += nLeftM;
3669     }
3670 
3671     const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3672     if (bOutside)
3673         return;
3674 
3675     // output area, excluding margins, in logical units
3676     const Size& aCellSize = rParam.mbPixelToLogic
3677         ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
3678         : Size( nOutWidth, nOutHeight );
3679 
3680     Point aURLStart;
3681 
3682     {
3683         const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false );
3684 
3685         Point aLogicStart(nStartX, nStartY);
3686         rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
3687 
3688         aURLStart = aLogicStart;      // copy before modifying for orientation
3689 
3690         if (rParam.meHorJustResult != SvxCellHorJustify::Block)
3691         {
3692             aLogicStart.AdjustX(nEngineWidth );
3693             if (!rParam.mbBreak)
3694             {
3695                 // Set the paper width to text size.
3696                 Size aPSize = rParam.mpEngine->GetPaperSize();
3697                 aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
3698                 rParam.mpEngine->SetPaperSize(aPSize);
3699 
3700                 tools::Long nGap = 0;
3701                 tools::Long nTopOffset = 0; // offset by top margin
3702                 if (rParam.mbPixelToLogic)
3703                 {
3704                     nGap = mpRefDevice->LogicToPixel(aPSize).Width() - mpRefDevice->LogicToPixel(aCellSize).Height();
3705                     nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
3706                     nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
3707                 }
3708                 else
3709                 {
3710                     nGap = aPSize.Width() - aCellSize.Height();
3711                     nTopOffset = nTopM;
3712                 }
3713                 aLogicStart.AdjustY(nTopOffset );
3714 
3715                 switch (rParam.meVerJust)
3716                 {
3717                     case SvxCellVerJustify::Standard:
3718                     case SvxCellVerJustify::Bottom:
3719                         // align to bottom
3720                         aLogicStart.AdjustY( -nGap );
3721                     break;
3722                     case SvxCellVerJustify::Center:
3723                         // center it.
3724                         aLogicStart.AdjustY( -(nGap / 2) );
3725                     break;
3726                     case SvxCellVerJustify::Block:
3727                     case SvxCellVerJustify::Top:
3728                         // align to top (do nothing)
3729                     default:
3730                         ;
3731                 }
3732             }
3733         }
3734 
3735         // bMoveClipped handling has been replaced by complete alignment
3736         // handling (also extending to the left).
3737 
3738         rParam.mpEngine->Draw(*mpDev, aLogicStart, 2700_deg10);
3739     }
3740 
3741     rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
3742 }
3743 
3744 void ScOutputData::DrawEditStacked(DrawEditParam& rParam)
3745 {
3746     OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
3747     Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
3748 
3749     bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
3750     bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
3751 
3752     rParam.mbAsianVertical =
3753         lcl_GetBoolValue(*rParam.mpPattern, ATTR_VERTICAL_ASIAN, rParam.mpCondSet);
3754 
3755     if ( rParam.mbAsianVertical )
3756     {
3757         // in asian mode, use EditEngine::SetVertical instead of EEControlBits::ONECHARPERLINE
3758         rParam.meOrient = SvxCellOrientation::Standard;
3759         DrawEditAsianVertical(rParam);
3760         return;
3761     }
3762 
3763     SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
3764 
3765     //! mirror margin values for RTL?
3766     //! move margin down to after final GetOutputArea call
3767     tools::Long nTopM, nLeftM, nBottomM, nRightM;
3768     rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
3769 
3770     SCCOL nXForPos = rParam.mnX;
3771     if ( nXForPos < nX1 )
3772     {
3773         nXForPos = nX1;
3774         rParam.mnPosX = rParam.mnInitPosX;
3775     }
3776     SCSIZE nArrYForPos = rParam.mnArrY;
3777     if ( nArrYForPos < 1 )
3778     {
3779         nArrYForPos = 1;
3780         rParam.mnPosY = nScrY;
3781     }
3782 
3783     OutputAreaParam aAreaParam;
3784 
3785     //  Initial page size - large for normal text, cell size for automatic line breaks
3786 
3787     Size aPaperSize( 1000000, 1000000 );
3788     //  call GetOutputArea with nNeeded=0, to get only the cell width
3789 
3790     //! handle nArrY == 0
3791     GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
3792                    *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3793                    rParam.mbCellIsValue, true, false, aAreaParam );
3794 
3795     //! special ScEditUtil handling if formatting for printer
3796     rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
3797 
3798     if (rParam.mbPixelToLogic)
3799     {
3800         Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
3801         if ( rParam.mbBreak && mpRefDevice != pFmtDevice )
3802         {
3803             // #i85342# screen display and formatting for printer,
3804             // use same GetEditArea call as in ScViewData::SetEditEngine
3805 
3806             Fraction aFract(1,1);
3807             tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
3808                 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
3809             aLogicSize.setWidth( aUtilRect.GetWidth() );
3810         }
3811         rParam.mpEngine->SetPaperSize(aLogicSize);
3812     }
3813     else
3814         rParam.mpEngine->SetPaperSize(aPaperSize);
3815 
3816     //  Fill the EditEngine (cell attributes and text)
3817 
3818     rParam.setPatternToEngine(mbUseStyleColor);
3819     rParam.setAlignmentToEngine();
3820 
3821     //  Read content from cell
3822 
3823     bool bWrapFields = false;
3824     if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
3825         // Failed to read cell content.  Bail out.
3826         return;
3827 
3828     if ( mbSyntaxMode )
3829         SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
3830     else if ( mbUseStyleColor && mbForceAutoColor )
3831         lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );     //! or have a flag at EditEngine
3832 
3833     rParam.mpEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
3834 
3835     //  Get final output area using the calculated width
3836 
3837     tools::Long nEngineWidth, nEngineHeight;
3838     rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
3839 
3840     tools::Long nNeededPixel = nEngineWidth;
3841     if (rParam.mbPixelToLogic)
3842         nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
3843     nNeededPixel += nLeftM + nRightM;
3844 
3845     if (bShrink)
3846     {
3847         // for break, the first GetOutputArea call is sufficient
3848         GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
3849                        *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
3850                        true, false, false, aAreaParam );
3851 
3852         ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
3853             nLeftM, nTopM, nRightM, nBottomM, true,
3854             rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
3855             nEngineWidth, nEngineHeight, nNeededPixel,
3856             aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
3857 
3858         if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3859         {
3860             nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
3861 
3862             //  No clip marks if "###" doesn't fit (same as in DrawStrings)
3863         }
3864 
3865         if ( eOutHorJust != SvxCellHorJustify::Left )
3866         {
3867             aPaperSize.setWidth( nNeededPixel + 1 );
3868             if (rParam.mbPixelToLogic)
3869                 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
3870             else
3871                 rParam.mpEngine->SetPaperSize(aPaperSize);
3872         }
3873     }
3874 
3875     tools::Long nStartX = aAreaParam.maAlignRect.Left();
3876     tools::Long nStartY = aAreaParam.maAlignRect.Top();
3877     tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
3878     tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3879     tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3880 
3881     if (rParam.mbBreak)
3882     {
3883         //  text with automatic breaks is aligned only within the
3884         //  edit engine's paper size, the output of the whole area
3885         //  is always left-aligned
3886 
3887         nStartX += nLeftM;
3888     }
3889     else
3890     {
3891         if ( eOutHorJust == SvxCellHorJustify::Right )
3892             nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3893         else if ( eOutHorJust == SvxCellHorJustify::Center )
3894             nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3895         else
3896             nStartX += nLeftM;
3897     }
3898 
3899     bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3900     if (bOutside)
3901         return;
3902 
3903     // Also take fields in a cell with automatic breaks into account: clip to cell width
3904     bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3905     bool bSimClip = false;
3906 
3907     Size aCellSize;         // output area, excluding margins, in logical units
3908     if (rParam.mbPixelToLogic)
3909         aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
3910     else
3911         aCellSize = Size( nOutWidth, nOutHeight );
3912 
3913     if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
3914     {
3915         const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3916         bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3917 
3918         //  Don't clip for text height when printing rows with optimal height,
3919         //  except when font size is from conditional formatting.
3920         //! Allow clipping when vertically merged?
3921         if ( eType != OUTTYPE_PRINTER ||
3922             ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3923             ( rParam.mpCondSet && SfxItemState::SET ==
3924                 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
3925             bClip = true;
3926         else
3927             bSimClip = true;
3928 
3929         //  Show clip marks if height is at least 5pt too small and
3930         //  there are several lines of text.
3931         //  Not for asian vertical text, because that would interfere
3932         //  with the default right position of the text.
3933         //  Only with automatic line breaks, to avoid having to find
3934         //  the cells with the horizontal end of the text again.
3935         if ( nEngineHeight - aCellSize.Height() > 100 &&
3936              rParam.mbBreak && bMarkClipped &&
3937              ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
3938         {
3939             ScCellInfo* pClipMarkCell = nullptr;
3940             if ( bMerged )
3941             {
3942                 //  anywhere in the merged area...
3943                 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
3944                 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3945             }
3946             else
3947                 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3948 
3949             pClipMarkCell->nClipMark |= ScClipMark::Right;      //! also allow left?
3950             bAnyClipped = true;
3951 
3952             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
3953             if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
3954                 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
3955         }
3956     }
3957 
3958     Point aURLStart;
3959 
3960     {   // Clip marks are already handled in GetOutputArea
3961         ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
3962                                 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
3963 
3964         Point aLogicStart;
3965         if (rParam.mbPixelToLogic)
3966             aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
3967         else
3968             aLogicStart = Point(nStartX, nStartY);
3969 
3970         if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
3971             rParam.meVerJust==SvxCellVerJustify::Standard)
3972         {
3973             //! if pRefDevice != pFmtDevice, keep heights in logic units,
3974             //! only converting margin?
3975 
3976             if (rParam.mbPixelToLogic)
3977                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
3978                                 mpRefDevice->LogicToPixel(aCellSize).Height() -
3979                                 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
3980                                 )).Height() );
3981             else
3982                 aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
3983         }
3984         else if (rParam.meVerJust==SvxCellVerJustify::Center)
3985         {
3986             if (rParam.mbPixelToLogic)
3987                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
3988                                 mpRefDevice->LogicToPixel(aCellSize).Height() -
3989                                 mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
3990                                 / 2)).Height() );
3991             else
3992                 aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
3993         }
3994         else        // top
3995         {
3996             if (rParam.mbPixelToLogic)
3997                 aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
3998             else
3999                 aLogicStart.AdjustY(nTopM );
4000         }
4001 
4002         aURLStart = aLogicStart;      // copy before modifying for orientation
4003 
4004         Size aPaperLogic = rParam.mpEngine->GetPaperSize();
4005         aPaperLogic.setWidth( nEngineWidth );
4006         rParam.mpEngine->SetPaperSize(aPaperLogic);
4007 
4008         // bMoveClipped handling has been replaced by complete alignment
4009         // handling (also extending to the left).
4010 
4011         if (bSimClip)
4012         {
4013             // no hard clip, only draw the affected rows
4014             Point aDocStart = aClip.getRect().TopLeft();
4015             aDocStart -= aLogicStart;
4016             rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
4017         }
4018         else
4019         {
4020             rParam.mpEngine->Draw(*mpDev, aLogicStart);
4021         }
4022     }
4023 
4024     rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
4025 }
4026 
4027 void ScOutputData::DrawEditAsianVertical(DrawEditParam& rParam)
4028 {
4029     // When in asian vertical orientation, the orientation value is STANDARD,
4030     // and the asian vertical boolean is true.
4031     OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
4032     OSL_ASSERT(rParam.mbAsianVertical);
4033     OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
4034 
4035     Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
4036 
4037     bool bHidden = false;
4038     bool bShrink = !rParam.mbBreak && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
4039     Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
4040 
4041     if (nAttrRotate)
4042     {
4043         //! set flag to find the cell in DrawRotated again ?
4044         //! (or flag already set during DrawBackground, then no query here)
4045         bHidden = true;     // rotated is outputted separately
4046     }
4047 
4048     // default alignment for asian vertical mode is top-right
4049     /* TODO: is setting meHorJustContext and meHorJustResult unconditionally to
4050      * SvxCellHorJustify::Right really wanted? Seems this was done all the time,
4051      * also before context was introduced and everything was attr only. */
4052     if ( rParam.meHorJustAttr == SvxCellHorJustify::Standard )
4053         rParam.meHorJustResult = rParam.meHorJustContext = SvxCellHorJustify::Right;
4054 
4055     if (bHidden)
4056         return;
4057 
4058     SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
4059 
4060     //! mirror margin values for RTL?
4061     //! move margin down to after final GetOutputArea call
4062     tools::Long nTopM, nLeftM, nBottomM, nRightM;
4063     rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
4064 
4065     SCCOL nXForPos = rParam.mnX;
4066     if ( nXForPos < nX1 )
4067     {
4068         nXForPos = nX1;
4069         rParam.mnPosX = rParam.mnInitPosX;
4070     }
4071     SCSIZE nArrYForPos = rParam.mnArrY;
4072     if ( nArrYForPos < 1 )
4073     {
4074         nArrYForPos = 1;
4075         rParam.mnPosY = nScrY;
4076     }
4077 
4078     OutputAreaParam aAreaParam;
4079 
4080     //  Initial page size - large for normal text, cell size for automatic line breaks
4081 
4082     Size aPaperSize( 1000000, 1000000 );
4083     //  call GetOutputArea with nNeeded=0, to get only the cell width
4084 
4085     //! handle nArrY == 0
4086     GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
4087                    *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4088                    rParam.mbCellIsValue, true, false, aAreaParam );
4089 
4090     //! special ScEditUtil handling if formatting for printer
4091     rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
4092 
4093     if (rParam.mbPixelToLogic)
4094     {
4095         Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
4096         if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
4097         {
4098             // #i85342# screen display and formatting for printer,
4099             // use same GetEditArea call as in ScViewData::SetEditEngine
4100 
4101             Fraction aFract(1,1);
4102             tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
4103                 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
4104             aLogicSize.setWidth( aUtilRect.GetWidth() );
4105         }
4106         rParam.mpEngine->SetPaperSize(aLogicSize);
4107     }
4108     else
4109         rParam.mpEngine->SetPaperSize(aPaperSize);
4110 
4111     //  Fill the EditEngine (cell attributes and text)
4112 
4113     // default alignment for asian vertical mode is top-right
4114     if ( rParam.meVerJust == SvxCellVerJustify::Standard )
4115         rParam.meVerJust = SvxCellVerJustify::Top;
4116 
4117     rParam.setPatternToEngine(mbUseStyleColor);
4118     rParam.setAlignmentToEngine();
4119 
4120     //  Read content from cell
4121 
4122     bool bWrapFields = false;
4123     if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
4124         // Failed to read cell content.  Bail out.
4125         return;
4126 
4127     if ( mbSyntaxMode )
4128         SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
4129     else if ( mbUseStyleColor && mbForceAutoColor )
4130         lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );     //! or have a flag at EditEngine
4131 
4132     rParam.mpEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
4133 
4134     //  Get final output area using the calculated width
4135 
4136     tools::Long nEngineWidth, nEngineHeight;
4137     rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
4138 
4139     tools::Long nNeededPixel = nEngineWidth;
4140     if (rParam.mbPixelToLogic)
4141         nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
4142     nNeededPixel += nLeftM + nRightM;
4143 
4144     // for break, the first GetOutputArea call is sufficient
4145     GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
4146                    *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4147                    rParam.mbCellIsValue || bShrink, false, false, aAreaParam );
4148 
4149     if ( bShrink )
4150     {
4151         ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
4152             nLeftM, nTopM, nRightM, nBottomM, false,
4153             rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
4154             nEngineWidth, nEngineHeight, nNeededPixel,
4155             aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
4156     }
4157     if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
4158     {
4159         nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
4160 
4161         //  No clip marks if "###" doesn't fit (same as in DrawStrings)
4162     }
4163 
4164     if (eOutHorJust != SvxCellHorJustify::Left)
4165     {
4166         aPaperSize.setWidth( nNeededPixel + 1 );
4167         if (rParam.mbPixelToLogic)
4168             rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4169         else
4170             rParam.mpEngine->SetPaperSize(aPaperSize);
4171     }
4172 
4173     tools::Long nStartX = aAreaParam.maAlignRect.Left();
4174     tools::Long nStartY = aAreaParam.maAlignRect.Top();
4175     tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
4176     tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
4177     tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
4178 
4179     //  text with automatic breaks is aligned only within the
4180     //  edit engine's paper size, the output of the whole area
4181     //  is always left-aligned
4182 
4183     nStartX += nLeftM;
4184 
4185     bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
4186     if (bOutside)
4187         return;
4188 
4189     // Also take fields in a cell with automatic breaks into account: clip to cell width
4190     bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
4191     bool bSimClip = false;
4192 
4193     Size aCellSize;         // output area, excluding margins, in logical units
4194     if (rParam.mbPixelToLogic)
4195         aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
4196     else
4197         aCellSize = Size( nOutWidth, nOutHeight );
4198 
4199     if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
4200     {
4201         const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
4202         bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
4203 
4204         //  Don't clip for text height when printing rows with optimal height,
4205         //  except when font size is from conditional formatting.
4206         //! Allow clipping when vertically merged?
4207         if ( eType != OUTTYPE_PRINTER ||
4208             ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
4209             ( rParam.mpCondSet && SfxItemState::SET ==
4210                 rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
4211             bClip = true;
4212         else
4213             bSimClip = true;
4214 
4215         //  Show clip marks if height is at least 5pt too small and
4216         //  there are several lines of text.
4217         //  Not for asian vertical text, because that would interfere
4218         //  with the default right position of the text.
4219         //  Only with automatic line breaks, to avoid having to find
4220         //  the cells with the horizontal end of the text again.
4221         if ( nEngineHeight - aCellSize.Height() > 100 &&
4222              ( rParam.mbBreak || rParam.meOrient == SvxCellOrientation::Stacked ) &&
4223              !rParam.mbAsianVertical && bMarkClipped &&
4224              ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
4225         {
4226             ScCellInfo* pClipMarkCell = nullptr;
4227             if ( bMerged )
4228             {
4229                 //  anywhere in the merged area...
4230                 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
4231                 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
4232             }
4233             else
4234                 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
4235 
4236             pClipMarkCell->nClipMark |= ScClipMark::Right;      //! also allow left?
4237             bAnyClipped = true;
4238 
4239             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
4240             if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
4241                 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
4242         }
4243     }
4244 
4245     Point aURLStart;
4246 
4247     {   // Clip marks are already handled in GetOutputArea
4248         ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
4249                                 : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
4250 
4251         Point aLogicStart;
4252         if (rParam.mbPixelToLogic)
4253             aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
4254         else
4255             aLogicStart = Point(nStartX, nStartY);
4256 
4257         tools::Long nAvailWidth = aCellSize.Width();
4258         // space for AutoFilter is already handled in GetOutputArea
4259 
4260         //  horizontal alignment
4261 
4262         if (rParam.meHorJustResult==SvxCellHorJustify::Right)
4263             aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
4264         else if (rParam.meHorJustResult==SvxCellHorJustify::Center)
4265             aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
4266 
4267         // paper size is subtracted below
4268         aLogicStart.AdjustX(nEngineWidth );
4269 
4270         // vertical adjustment is within the EditEngine
4271         if (rParam.mbPixelToLogic)
4272             aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
4273         else
4274             aLogicStart.AdjustY(nTopM );
4275 
4276         aURLStart = aLogicStart;      // copy before modifying for orientation
4277 
4278         // bMoveClipped handling has been replaced by complete alignment
4279         // handling (also extending to the left).
4280 
4281         // with SetVertical, the start position is top left of
4282         // the whole output area, not the text itself
4283         aLogicStart.AdjustX( -(rParam.mpEngine->GetPaperSize().Width()) );
4284 
4285         rParam.mpEngine->Draw(*mpDev, aLogicStart);
4286     }
4287 
4288     rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
4289 }
4290 
4291 void ScOutputData::DrawEdit(bool bPixelToLogic)
4292 {
4293     std::unique_ptr<ScFieldEditEngine> pEngine;
4294     bool bHyphenatorSet = false;
4295     const ScPatternAttr* pOldPattern = nullptr;
4296     const SfxItemSet*    pOldCondSet = nullptr;
4297     const SfxItemSet*    pOldPreviewFontSet = nullptr;
4298     ScRefCellValue aCell;
4299 
4300     tools::Long nInitPosX = nScrX;
4301     if ( bLayoutRTL )
4302     {
4303         nInitPosX += nMirrorW - 1;
4304     }
4305     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4306 
4307     //! store nLastContentCol as member!
4308     SCCOL nLastContentCol = mpDoc->MaxCol();
4309     if ( nX2 < mpDoc->MaxCol() )
4310         nLastContentCol = sal::static_int_cast<SCCOL>(
4311             nLastContentCol - mpDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, mpDoc->MaxCol(), nY2, nTab, DIR_RIGHT ) );
4312 
4313     tools::Long nRowPosY = nScrY;
4314     for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++)            // 0 of the rest of the merged
4315     {
4316         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
4317 
4318         if (nArrY==1) nRowPosY = nScrY;                         // positions before are calculated individually
4319 
4320         if ( pThisRowInfo->bChanged || nArrY==0 )
4321         {
4322             tools::Long nPosX = 0;
4323             for (SCCOL nX=0; nX<=nX2; nX++)                     // due to overflow
4324             {
4325                 std::unique_ptr< ScPatternAttr > pPreviewPattr;
4326                 if (nX==nX1) nPosX = nInitPosX;                 // positions before nX1 are calculated individually
4327 
4328                 if (pThisRowInfo->basicCellInfo(nX).bEditEngine)
4329                 {
4330                     SCROW nY = pThisRowInfo->nRowNo;
4331 
4332                     SCCOL nCellX = nX;                  // position where the cell really starts
4333                     SCROW nCellY = nY;
4334                     bool bDoCell = false;
4335 
4336                     tools::Long nPosY = nRowPosY;
4337                     if ( nArrY == 0 )
4338                     {
4339                         nPosY = nScrY;
4340                         nY = pRowInfo[1].nRowNo;
4341                         SCCOL nOverX;                   // start of the merged cells
4342                         SCROW nOverY;
4343                         if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, true ))
4344                         {
4345                             nCellX = nOverX;
4346                             nCellY = nOverY;
4347                             bDoCell = true;
4348                         }
4349                     }
4350                     else if ( nX == nX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() )
4351                     {
4352                         //  Rest of a long text further to the right?
4353 
4354                         SCCOL nTempX=nX;
4355                         while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
4356                             ++nTempX;
4357 
4358                         if ( nTempX > nX &&
4359                              !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
4360                              !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
4361                         {
4362                             nCellX = nTempX;
4363                             bDoCell = true;
4364                         }
4365                     }
4366                     else
4367                     {
4368                         bDoCell = true;
4369                     }
4370 
4371                     if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
4372                         bDoCell = false;
4373 
4374                     const ScPatternAttr* pPattern = nullptr;
4375                     const SfxItemSet* pCondSet = nullptr;
4376                     if (bDoCell)
4377                     {
4378                         if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
4379                              !mpDoc->ColHidden(nCellX, nTab) )
4380                         {
4381                             ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
4382                             pPattern = rCellInfo.pPatternAttr;
4383                             pCondSet = rCellInfo.pConditionSet;
4384                             aCell = rCellInfo.maCell;
4385                         }
4386                         else        // get from document
4387                         {
4388                             pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
4389                             pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
4390                             GetVisibleCell( nCellX, nCellY, nTab, aCell );
4391                         }
4392                         if (aCell.isEmpty())
4393                             bDoCell = false;
4394                     }
4395                     if (bDoCell)
4396                     {
4397                         if ( mpDoc->GetPreviewCellStyle() )
4398                         {
4399                             if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
4400                             {
4401                                 pPreviewPattr.reset( new ScPatternAttr(*pPattern) );
4402                                 pPreviewPattr->SetStyleSheet(pPreviewStyle);
4403                                 pPattern = pPreviewPattr.get();
4404                             }
4405                         }
4406                         SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab );
4407                         if (!pEngine)
4408                             pEngine = CreateOutputEditEngine();
4409                         else
4410                             lcl_ClearEdit( *pEngine );      // also calls SetUpdateMode(sal_False)
4411 
4412                         // fdo#32530: Check if the first character is RTL.
4413                         OUString aStr = mpDoc->GetString(nCellX, nCellY, nTab);
4414 
4415                         DrawEditParam aParam(pPattern, pCondSet, lcl_SafeIsValue(aCell));
4416                         const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
4417                         aParam.meHorJustContext = getAlignmentFromContext( aParam.meHorJustAttr,
4418                                 aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText);
4419                         aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ?
4420                                 SvxCellHorJustify::Block : aParam.meHorJustContext;
4421                         aParam.mbPixelToLogic = bPixelToLogic;
4422                         aParam.mbHyphenatorSet = bHyphenatorSet;
4423                         aParam.mpEngine = pEngine.get();
4424                         aParam.maCell = aCell;
4425                         aParam.mnArrY = nArrY;
4426                         aParam.mnX = nX;
4427                         aParam.mnCellX = nCellX;
4428                         aParam.mnCellY = nCellY;
4429                         aParam.mnPosX = nPosX;
4430                         aParam.mnPosY = nPosY;
4431                         aParam.mnInitPosX = nInitPosX;
4432                         aParam.mpPreviewFontSet = pPreviewFontSet;
4433                         aParam.mpOldPattern = pOldPattern;
4434                         aParam.mpOldCondSet = pOldCondSet;
4435                         aParam.mpOldPreviewFontSet = pOldPreviewFontSet;
4436                         aParam.mpThisRowInfo = pThisRowInfo;
4437                         if (mpSpellCheckCxt)
4438                             aParam.mpMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY);
4439 
4440                         if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat)
4441                         {
4442                             // ignore orientation/rotation if "repeat" is active
4443                             aParam.meOrient = SvxCellOrientation::Standard;
4444                         }
4445                         switch (aParam.meOrient)
4446                         {
4447                             case SvxCellOrientation::BottomUp:
4448                                 DrawEditBottomTop(aParam);
4449                             break;
4450                             case SvxCellOrientation::TopBottom:
4451                                 DrawEditTopBottom(aParam);
4452                             break;
4453                             case SvxCellOrientation::Stacked:
4454                                 // this can be vertically stacked or asian vertical.
4455                                 DrawEditStacked(aParam);
4456                             break;
4457                             default:
4458                                 DrawEditStandard(aParam);
4459                         }
4460 
4461                         // Retrieve parameters for next iteration.
4462                         pOldPattern = aParam.mpOldPattern;
4463                         pOldCondSet = aParam.mpOldCondSet;
4464                         pOldPreviewFontSet = aParam.mpOldPreviewFontSet;
4465                         bHyphenatorSet = aParam.mbHyphenatorSet;
4466                     }
4467                 }
4468                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
4469             }
4470         }
4471         nRowPosY += pRowInfo[nArrY].nHeight;
4472     }
4473 
4474     pEngine.reset();
4475 
4476     if (mrTabInfo.maArray.HasCellRotation())
4477     {
4478         DrawRotated(bPixelToLogic);     //! call from outside ?
4479     }
4480 }
4481 
4482 void ScOutputData::DrawRotated(bool bPixelToLogic)
4483 {
4484     //! store nRotMax
4485     SCCOL nRotMax = nX2;
4486     for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
4487         if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
4488             nRotMax = pRowInfo[nRotY].nRotMaxCol;
4489 
4490     ScModule* pScMod = SC_MOD();
4491     Color nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
4492     bool bCellContrast = mbUseStyleColor &&
4493             Application::GetSettings().GetStyleSettings().GetHighContrastMode();
4494 
4495     std::unique_ptr<ScFieldEditEngine> pEngine;
4496     bool bHyphenatorSet = false;
4497     const ScPatternAttr* pPattern;
4498     const SfxItemSet*    pCondSet;
4499     const ScPatternAttr* pOldPattern = nullptr;
4500     const SfxItemSet*    pOldCondSet = nullptr;
4501     ScRefCellValue aCell;
4502 
4503     tools::Long nInitPosX = nScrX;
4504     if ( bLayoutRTL )
4505     {
4506         nInitPosX += nMirrorW - 1;
4507     }
4508     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4509 
4510     tools::Long nRowPosY = nScrY;
4511     for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++)            // 0 for the rest of the merged
4512     {
4513         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
4514         tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight);
4515         if (nArrY==1) nRowPosY = nScrY;                         // positions before are calculated individually
4516 
4517         if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
4518         {
4519             tools::Long nPosX = 0;
4520             for (SCCOL nX=0; nX<=nRotMax; nX++)
4521             {
4522                 if (nX==nX1) nPosX = nInitPosX;                 // positions before nX1 are calculated individually
4523 
4524                 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
4525                 if ( pInfo->nRotateDir != ScRotateDir::NONE )
4526                 {
4527                     SCROW nY = pThisRowInfo->nRowNo;
4528 
4529                     bool bHidden = false;
4530                     if (bEditMode)
4531                         if ( nX == nEditCol && nY == nEditRow )
4532                             bHidden = true;
4533 
4534                     if (!bHidden)
4535                     {
4536                         if (!pEngine)
4537                             pEngine = CreateOutputEditEngine();
4538                         else
4539                             lcl_ClearEdit( *pEngine );      // also calls SetUpdateMode(sal_False)
4540 
4541                         tools::Long nPosY = nRowPosY;
4542 
4543                         //! rest from merged cells further up do not work!
4544 
4545                         bool bFromDoc = false;
4546                         pPattern = pInfo->pPatternAttr;
4547                         pCondSet = pInfo->pConditionSet;
4548                         if (!pPattern)
4549                         {
4550                             pPattern = mpDoc->GetPattern( nX, nY, nTab );
4551                             bFromDoc = true;
4552                         }
4553                         aCell = pInfo->maCell;
4554                         if (bFromDoc)
4555                             pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
4556 
4557                         if (aCell.isEmpty() && nX>nX2)
4558                             GetVisibleCell( nX, nY, nTab, aCell );
4559 
4560                         if (aCell.isEmpty() || IsEmptyCellText(pThisRowInfo, nX, nY))
4561                             bHidden = true;     // nRotateDir is also set without a cell
4562 
4563                         tools::Long nCellWidth = static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nX).nWidth);
4564 
4565                         SvxCellHorJustify eHorJust =
4566                                             pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet).GetValue();
4567                         bool bBreak = ( eHorJust == SvxCellHorJustify::Block ) ||
4568                                     pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue();
4569                         bool bRepeat = ( eHorJust == SvxCellHorJustify::Repeat && !bBreak );
4570                         bool bShrink = !bBreak && !bRepeat &&
4571                                         pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
4572                         SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
4573 
4574                         const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
4575                         bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
4576 
4577                         tools::Long nStartX = nPosX;
4578                         tools::Long nStartY = nPosY;
4579                         if (nX<nX1)
4580                         {
4581                             if ((bBreak || eOrient!=SvxCellOrientation::Standard) && !bMerged)
4582                                 bHidden = true;
4583                             else
4584                             {
4585                                 nStartX = nInitPosX;
4586                                 SCCOL nCol = nX1;
4587                                 while (nCol > nX)
4588                                 {
4589                                     --nCol;
4590                                     nStartX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
4591                                 }
4592                             }
4593                         }
4594                         tools::Long nCellStartX = nStartX;
4595 
4596                         // omit substitute representation of small text
4597 
4598                         if (!bHidden)
4599                         {
4600                             tools::Long nOutWidth = nCellWidth - 1;
4601                             tools::Long nOutHeight = nCellHeight;
4602 
4603                             if ( bMerged )
4604                             {
4605                                 SCCOL nCountX = pMerge->GetColMerge();
4606                                 for (SCCOL i=1; i<nCountX; i++)
4607                                     nOutWidth += mpDoc->GetColWidth(nX+i,nTab) * mnPPTX;
4608                                 SCROW nCountY = pMerge->GetRowMerge();
4609                                 nOutHeight += mpDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, mnPPTY);
4610                             }
4611 
4612                             SvxCellVerJustify eVerJust =
4613                                                 pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet).GetValue();
4614 
4615                             // syntax mode is ignored here...
4616 
4617                             // StringDiffer doesn't look at hyphenate, language items
4618                             if ( pPattern != pOldPattern || pCondSet != pOldCondSet )
4619                             {
4620                                 auto pSet = std::make_unique<SfxItemSet>( pEngine->GetEmptyItemSet() );
4621                                 pPattern->FillEditItemSet( pSet.get(), pCondSet );
4622 
4623                                                                     // adjustment for EditEngine
4624                                 SvxAdjust eSvxAdjust = SvxAdjust::Left;
4625                                 if (eOrient==SvxCellOrientation::Stacked)
4626                                     eSvxAdjust = SvxAdjust::Center;
4627                                 // adjustment for bBreak is omitted here
4628                                 pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
4629 
4630                                 bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
4631                                 pEngine->SetDefaults( std::move(pSet) );
4632                                 pOldPattern = pPattern;
4633                                 pOldCondSet = pCondSet;
4634 
4635                                 EEControlBits nControl = pEngine->GetControlWord();
4636                                 if (eOrient==SvxCellOrientation::Stacked)
4637                                     nControl |= EEControlBits::ONECHARPERLINE;
4638                                 else
4639                                     nControl &= ~EEControlBits::ONECHARPERLINE;
4640                                 pEngine->SetControlWord( nControl );
4641 
4642                                 if ( !bHyphenatorSet && bParaHyphenate )
4643                                 {
4644                                     //  set hyphenator the first time it is needed
4645                                     css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
4646                                     pEngine->SetHyphenator( xXHyphenator );
4647                                     bHyphenatorSet = true;
4648                                 }
4649 
4650                                 Color aBackCol =
4651                                     pPattern->GetItem( ATTR_BACKGROUND, pCondSet ).GetColor();
4652                                 if ( mbUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
4653                                     aBackCol = nConfBackColor;
4654                                 pEngine->SetBackgroundColor( aBackCol );
4655                             }
4656 
4657                             // margins
4658 
4659                             //! change position and paper size to EditUtil !!!
4660 
4661                             const SvxMarginItem* pMargin =
4662                                                     &pPattern->GetItem(ATTR_MARGIN, pCondSet);
4663                             sal_uInt16 nIndent = 0;
4664                             if ( eHorJust == SvxCellHorJustify::Left )
4665                                 nIndent = pPattern->GetItem(ATTR_INDENT, pCondSet).GetValue();
4666 
4667                             tools::Long nTotalHeight = nOutHeight; // without subtracting the margin
4668                             if ( bPixelToLogic )
4669                                 nTotalHeight = mpRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height();
4670 
4671                             tools::Long nLeftM = static_cast<tools::Long>( (pMargin->GetLeftMargin() + nIndent) * mnPPTX );
4672                             tools::Long nTopM  = static_cast<tools::Long>( pMargin->GetTopMargin() * mnPPTY );
4673                             tools::Long nRightM  = static_cast<tools::Long>( pMargin->GetRightMargin() * mnPPTX );
4674                             tools::Long nBottomM = static_cast<tools::Long>( pMargin->GetBottomMargin() * mnPPTY );
4675                             nStartX += nLeftM;
4676                             nStartY += nTopM;
4677                             nOutWidth -= nLeftM + nRightM;
4678                             nOutHeight -= nTopM + nBottomM;
4679 
4680                             // rotate here already, to adjust paper size for page breaks
4681                             Degree100 nAttrRotate;
4682                             double nSin = 0.0;
4683                             double nCos = 1.0;
4684                             SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
4685                             if ( eOrient == SvxCellOrientation::Standard )
4686                             {
4687                                 nAttrRotate = pPattern->
4688                                                     GetItem(ATTR_ROTATE_VALUE, pCondSet).GetValue();
4689                                 if ( nAttrRotate )
4690                                 {
4691                                     eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
4692 
4693                                     // tdf#143377 To use the same limits to avoid too big Skew
4694                                     // with TextOrientation in Calc, use 1/2 degree here, too.
4695                                     // This equals '50' in the notation here (100th degree)
4696                                     static const sal_Int32 nMinRad(50);
4697 
4698                                     // bring nAttrRotate to the range [0..36000[
4699                                     nAttrRotate = Degree100(((nAttrRotate.get() % 36000) + 36000) % 36000);
4700 
4701                                     // check for to be avoided extreme values and correct
4702                                     if (nAttrRotate < Degree100(nMinRad))
4703                                     {
4704                                         // range [0..50]
4705                                         nAttrRotate = Degree100(nMinRad);
4706                                         eRotMode = SVX_ROTATE_MODE_STANDARD;    // no overflow
4707                                     }
4708                                     else if (nAttrRotate > Degree100(36000 - nMinRad))
4709                                     {
4710                                         // range [35950..36000[
4711                                         nAttrRotate = Degree100(36000 - nMinRad);
4712                                         eRotMode = SVX_ROTATE_MODE_STANDARD;    // no overflow
4713                                     }
4714                                     else if (nAttrRotate > Degree100(18000 - nMinRad) && (nAttrRotate < Degree100(18000 + nMinRad)))
4715                                     {
4716                                         // range 50 around 18000, [17950..18050]
4717                                         nAttrRotate = (nAttrRotate > Degree100(18000))
4718                                             ? Degree100(18000 + nMinRad)
4719                                             : Degree100(18000 - nMinRad);
4720                                         eRotMode = SVX_ROTATE_MODE_STANDARD;    // no overflow
4721                                     }
4722 
4723                                     if ( bLayoutRTL )
4724                                     {
4725                                         // keep in range [0..36000[
4726                                         nAttrRotate = Degree100(36000 - nAttrRotate.get());
4727                                     }
4728 
4729                                     double nRealOrient = toRadians(nAttrRotate);   // 1/100 degree
4730                                     nCos = cos( nRealOrient );
4731 
4732                                     // tdf#143377 new strategy: instead of using zero for nSin, which
4733                                     // would be the *correct* value, continue with the corrected maximum
4734                                     // allowed value which is then *not* zero. This is similar to
4735                                     // the behaviour before where (just due to numerical unprecisions)
4736                                     // nSin was also not zero (pure coincidence), but very close to it.
4737                                     // I checked and tried to make safe all places below that use
4738                                     // nSin and divide by it, but there is too much going on and that
4739                                     // would not be safe, so rely on the same values as before, but
4740                                     // now numerically limited to not get the Skew go havoc
4741                                     nSin = sin( nRealOrient );
4742                                 }
4743                             }
4744 
4745                             Size aPaperSize( 1000000, 1000000 );
4746                             if (eOrient==SvxCellOrientation::Stacked)
4747                                 aPaperSize.setWidth( nOutWidth );             // to center
4748                             else if (bBreak)
4749                             {
4750                                 if (nAttrRotate)
4751                                 {
4752                                     //! the correct paper size for break depends on the number
4753                                     //! of rows, as long as the rows can not be outputted individually
4754                                     //! offsetted -> therefore unlimited, so no wrapping.
4755                                     //! With offset rows the following would be correct:
4756                                     aPaperSize.setWidth( static_cast<tools::Long>(nOutHeight / fabs(nSin)) );
4757                                 }
4758                                 else if (eOrient == SvxCellOrientation::Standard)
4759                                     aPaperSize.setWidth( nOutWidth );
4760                                 else
4761                                     aPaperSize.setWidth( nOutHeight - 1 );
4762                             }
4763                             if (bPixelToLogic)
4764                                 pEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4765                             else
4766                                 pEngine->SetPaperSize(aPaperSize);  // scale is always 1
4767 
4768                             // read data from cell
4769 
4770                             if (aCell.getType() == CELLTYPE_EDIT)
4771                             {
4772                                 if (aCell.getEditText())
4773                                     pEngine->SetTextCurrentDefaults(*aCell.getEditText());
4774                                 else
4775                                 {
4776                                     OSL_FAIL("pData == 0");
4777                                 }
4778                             }
4779                             else
4780                             {
4781                                 sal_uInt32 nFormat = pPattern->GetNumberFormat(
4782                                                             mpDoc->GetFormatTable(), pCondSet );
4783                                 const Color* pColor;
4784                                 OUString aString = ScCellFormat::GetString( aCell,
4785                                                          nFormat, &pColor,
4786                                                          *mpDoc->GetFormatTable(),
4787                                                          *mpDoc,
4788                                                          mbShowNullValues,
4789                                                          mbShowFormulas);
4790 
4791                                 pEngine->SetTextCurrentDefaults(aString);
4792                                 if ( pColor && !mbSyntaxMode && !( mbUseStyleColor && mbForceAutoColor ) )
4793                                     lcl_SetEditColor( *pEngine, *pColor );
4794                             }
4795 
4796                             if ( mbSyntaxMode )
4797                             {
4798                                 SetEditSyntaxColor(*pEngine, aCell);
4799                             }
4800                             else if ( mbUseStyleColor && mbForceAutoColor )
4801                                 lcl_SetEditColor( *pEngine, COL_AUTO );     //! or have a flag at EditEngine
4802 
4803                             pEngine->SetUpdateLayout( true );     // after SetText, before CalcTextWidth/GetTextHeight
4804 
4805                             tools::Long nEngineWidth  = static_cast<tools::Long>(pEngine->CalcTextWidth());
4806                             tools::Long nEngineHeight = pEngine->GetTextHeight();
4807 
4808                             if (nAttrRotate && bBreak)
4809                             {
4810                                 double nAbsCos = fabs( nCos );
4811                                 double nAbsSin = fabs( nSin );
4812 
4813                                 // adjust width of papersize for height of text
4814                                 int nSteps = 5;
4815                                 while (nSteps > 0)
4816                                 {
4817                                     // everything is in pixels
4818                                     tools::Long nEnginePixel = mpRefDevice->LogicToPixel(
4819                                                             Size(0,nEngineHeight)).Height();
4820                                     tools::Long nEffHeight = nOutHeight - static_cast<tools::Long>(nEnginePixel * nAbsCos) + 2;
4821                                     tools::Long nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
4822                                     bool bFits = ( nNewWidth >= aPaperSize.Width() );
4823                                     if ( bFits )
4824                                         nSteps = 0;
4825                                     else
4826                                     {
4827                                         if ( nNewWidth < 4 )
4828                                         {
4829                                             // can't fit -> fall back to using half height
4830                                             nEffHeight = nOutHeight / 2;
4831                                             nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
4832                                             nSteps = 0;
4833                                         }
4834                                         else
4835                                             --nSteps;
4836 
4837                                         // set paper width and get new text height
4838                                         aPaperSize.setWidth( nNewWidth );
4839                                         if (bPixelToLogic)
4840                                             pEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
4841                                         else
4842                                             pEngine->SetPaperSize(aPaperSize);  // Scale is always 1
4843                                         //pEngine->QuickFormatDoc( sal_True );
4844 
4845                                         nEngineWidth  = static_cast<tools::Long>(pEngine->CalcTextWidth());
4846                                         nEngineHeight = pEngine->GetTextHeight();
4847                                     }
4848                                 }
4849                             }
4850 
4851                             tools::Long nRealWidth  = nEngineWidth;
4852                             tools::Long nRealHeight = nEngineHeight;
4853 
4854                             // when rotated, adjust size
4855                             if (nAttrRotate)
4856                             {
4857                                 double nAbsCos = fabs( nCos );
4858                                 double nAbsSin = fabs( nSin );
4859 
4860                                 if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
4861                                     nEngineWidth = static_cast<tools::Long>( nRealWidth * nAbsCos +
4862                                                             nRealHeight * nAbsSin );
4863                                 else
4864                                     nEngineWidth = static_cast<tools::Long>( nRealHeight / nAbsSin );
4865                                 //! limit !!!
4866 
4867                                 nEngineHeight = static_cast<tools::Long>( nRealHeight * nAbsCos +
4868                                                          nRealWidth * nAbsSin );
4869                             }
4870 
4871                             if (!nAttrRotate)           //  only rotated text here
4872                                 bHidden = true;         //! check first !!!
4873 
4874                             //! omit which doesn't stick out
4875 
4876                             if (!bHidden)
4877                             {
4878                                 Size aClipSize( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY );
4879 
4880                                 // go on writing
4881 
4882                                 Size aCellSize;
4883                                 if (bPixelToLogic)
4884                                     aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
4885                                 else
4886                                     aCellSize = Size( nOutWidth, nOutHeight );  // scale is one
4887 
4888                                 tools::Long nGridWidth = nEngineWidth;
4889                                 bool bNegative = false;
4890                                 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
4891                                 {
4892                                     nGridWidth = aCellSize.Width() +
4893                                             std::abs(static_cast<tools::Long>( aCellSize.Height() * nCos / nSin ));
4894                                     bNegative = ( pInfo->nRotateDir == ScRotateDir::Left );
4895                                     if ( bLayoutRTL )
4896                                         bNegative = !bNegative;
4897                                 }
4898 
4899                                 // use GetOutputArea to hide the grid
4900                                 // (clip region is done manually below)
4901                                 OutputAreaParam aAreaParam;
4902 
4903                                 SCCOL nCellX = nX;
4904                                 SCROW nCellY = nY;
4905                                 SvxCellHorJustify eOutHorJust = eHorJust;
4906                                 if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
4907                                     eOutHorJust = bNegative ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
4908                                 tools::Long nNeededWidth = nGridWidth;     // in pixel for GetOutputArea
4909                                 if ( bPixelToLogic )
4910                                     nNeededWidth =  mpRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width();
4911 
4912                                 GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth,
4913                                                 *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
4914                                                 false, false, true, aAreaParam );
4915 
4916                                 if ( bShrink )
4917                                 {
4918                                     tools::Long nPixelWidth = bPixelToLogic ?
4919                                         mpRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth;
4920                                     tools::Long nNeededPixel = nPixelWidth + nLeftM + nRightM;
4921 
4922                                     aAreaParam.mbLeftClip = aAreaParam.mbRightClip = true;
4923 
4924                                     // always do height
4925                                     ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
4926                                         false, eOrient, nAttrRotate, bPixelToLogic,
4927                                         nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
4928 
4929                                     if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
4930                                     {
4931                                         // do width only if rotating within the cell (standard mode)
4932                                         ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
4933                                             true, eOrient, nAttrRotate, bPixelToLogic,
4934                                             nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
4935                                     }
4936 
4937                                     // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine
4938                                     // (but width is only valid for standard mode)
4939                                     nRealWidth  = static_cast<tools::Long>(pEngine->CalcTextWidth());
4940                                     nRealHeight = pEngine->GetTextHeight();
4941 
4942                                     if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
4943                                         nEngineWidth = static_cast<tools::Long>( nRealHeight / fabs( nSin ) );
4944                                 }
4945 
4946                                 tools::Long nClipStartX = nStartX;
4947                                 if (nX<nX1)
4948                                 {
4949                                     //! clipping is not needed when on the left side of the window
4950 
4951                                     if (nStartX<nScrX)
4952                                     {
4953                                         tools::Long nDif = nScrX - nStartX;
4954                                         nClipStartX = nScrX;
4955                                         aClipSize.AdjustWidth( -nDif );
4956                                     }
4957                                 }
4958 
4959                                 tools::Long nClipStartY = nStartY;
4960                                 if (nArrY==0 && nClipStartY < nRowPosY )
4961                                 {
4962                                     tools::Long nDif = nRowPosY - nClipStartY;
4963                                     nClipStartY = nRowPosY;
4964                                     aClipSize.AdjustHeight( -nDif );
4965                                 }
4966 
4967                                 if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ )
4968                                 {
4969                                     // only clip rotated output text at the page border
4970                                     nClipStartX = nScrX;
4971                                     aClipSize.setWidth( nScrW );
4972                                 }
4973 
4974                                 if (bPixelToLogic)
4975                                     aAreaParam.maClipRect = mpRefDevice->PixelToLogic( tools::Rectangle(
4976                                                     Point(nClipStartX,nClipStartY), aClipSize ) );
4977                                 else
4978                                     aAreaParam.maClipRect = tools::Rectangle(Point(nClipStartX, nClipStartY),
4979                                                             aClipSize );    // Scale = 1
4980 
4981                                 if (bMetaFile)
4982                                 {
4983                                     mpDev->Push();
4984                                     mpDev->IntersectClipRegion( aAreaParam.maClipRect );
4985                                 }
4986                                 else
4987                                     mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
4988 
4989                                 Point aLogicStart;
4990                                 if (bPixelToLogic)
4991                                     aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
4992                                 else
4993                                     aLogicStart = Point(nStartX, nStartY);
4994                                 if ( eOrient!=SvxCellOrientation::Standard || !bBreak )
4995                                 {
4996                                     tools::Long nAvailWidth = aCellSize.Width();
4997                                     if (eType==OUTTYPE_WINDOW &&
4998                                             eOrient!=SvxCellOrientation::Stacked &&
4999                                             pInfo->bAutoFilter)
5000                                     {
5001                                         // filter drop-down width depends on row height
5002                                         double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
5003                                         fZoom = fZoom > 1.0 ? fZoom : 1.0;
5004                                         if (bPixelToLogic)
5005                                             nAvailWidth -= mpRefDevice->PixelToLogic(Size(0,fZoom * DROPDOWN_BITMAP_SIZE)).Height();
5006                                         else
5007                                             nAvailWidth -= fZoom * DROPDOWN_BITMAP_SIZE;
5008                                         tools::Long nComp = nEngineWidth;
5009                                         if (nAvailWidth<nComp) nAvailWidth=nComp;
5010                                     }
5011 
5012                                     // horizontal orientation
5013 
5014                                     if (eOrient==SvxCellOrientation::Standard && !nAttrRotate)
5015                                     {
5016                                         if (eHorJust==SvxCellHorJustify::Right ||
5017                                             eHorJust==SvxCellHorJustify::Center)
5018                                         {
5019                                             pEngine->SetUpdateLayout( false );
5020 
5021                                             SvxAdjust eSvxAdjust =
5022                                                 (eHorJust==SvxCellHorJustify::Right) ?
5023                                                     SvxAdjust::Right : SvxAdjust::Center;
5024                                             pEngine->SetDefaultItem(
5025                                                 SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
5026 
5027                                             aPaperSize.setWidth( nOutWidth );
5028                                             if (bPixelToLogic)
5029                                                 pEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
5030                                             else
5031                                                 pEngine->SetPaperSize(aPaperSize);
5032 
5033                                             pEngine->SetUpdateLayout( true );
5034                                         }
5035                                     }
5036                                     else
5037                                     {
5038                                         // rotated text is centered by default
5039                                         if (eHorJust==SvxCellHorJustify::Right)
5040                                             aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
5041                                         else if (eHorJust==SvxCellHorJustify::Center ||
5042                                                  eHorJust==SvxCellHorJustify::Standard)
5043                                             aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
5044                                     }
5045                                 }
5046 
5047                                 if ( bLayoutRTL )
5048                                 {
5049                                     if (bPixelToLogic)
5050                                         aLogicStart.AdjustX( -(mpRefDevice->PixelToLogic(
5051                                                         Size( nCellWidth, 0 ) ).Width()) );
5052                                     else
5053                                         aLogicStart.AdjustX( -nCellWidth );
5054                                 }
5055 
5056                                 if ( eOrient==SvxCellOrientation::Standard ||
5057                                      eOrient==SvxCellOrientation::Stacked || !bBreak )
5058                                 {
5059                                     if (eVerJust==SvxCellVerJustify::Bottom ||
5060                                         eVerJust==SvxCellVerJustify::Standard)
5061                                     {
5062                                         if (bPixelToLogic)
5063                                             aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,
5064                                                             mpRefDevice->LogicToPixel(aCellSize).Height() -
5065                                                             mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
5066                                                             )).Height() );
5067                                         else
5068                                             aLogicStart.AdjustY(aCellSize.Height() - nEngineHeight );
5069                                     }
5070 
5071                                     else if (eVerJust==SvxCellVerJustify::Center)
5072                                     {
5073                                         if (bPixelToLogic)
5074                                             aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,(
5075                                                             mpRefDevice->LogicToPixel(aCellSize).Height() -
5076                                                             mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height())
5077                                                             / 2)).Height() );
5078                                         else
5079                                             aLogicStart.AdjustY((aCellSize.Height() - nEngineHeight) / 2 );
5080                                     }
5081                                 }
5082 
5083                                 // TOPBOTTOM and BOTTOMTOP are handled in DrawStrings/DrawEdit
5084                                 OSL_ENSURE( eOrient == SvxCellOrientation::Standard && nAttrRotate,
5085                                             "DrawRotated: no rotation" );
5086 
5087                                 Degree10 nOriVal = 0_deg10;
5088                                 if ( nAttrRotate )
5089                                 {
5090                                     // attribute is 1/100, Font 1/10 degrees
5091                                     nOriVal = to<Degree10>(nAttrRotate);
5092 
5093                                     double nAddX = 0.0;
5094                                     double nAddY = 0.0;
5095                                     if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD )
5096                                     {
5097                                         //! limit !!!
5098                                         double nH = nRealHeight * nCos;
5099                                         nAddX += nH * ( nCos / fabs(nSin) );
5100                                     }
5101                                     if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD )
5102                                         nAddX -= nRealWidth * nCos;
5103                                     if ( nSin < 0.0 )
5104                                         nAddX -= nRealHeight * nSin;
5105                                     if ( nSin > 0.0 )
5106                                         nAddY += nRealWidth * nSin;
5107                                     if ( nCos < 0.0 )
5108                                         nAddY -= nRealHeight * nCos;
5109 
5110                                     if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
5111                                     {
5112                                         //! limit !!!
5113                                         double nSkew = nTotalHeight * nCos / fabs(nSin);
5114                                         if ( eRotMode == SVX_ROTATE_MODE_CENTER )
5115                                             nAddX -= nSkew * 0.5;
5116                                         if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) ||
5117                                              ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) )
5118                                             nAddX -= nSkew;
5119 
5120                                         tools::Long nUp = 0;
5121                                         if ( eVerJust == SvxCellVerJustify::Center )
5122                                             nUp = ( aCellSize.Height() - nEngineHeight ) / 2;
5123                                         else if ( eVerJust == SvxCellVerJustify::Top )
5124                                         {
5125                                             if ( nSin > 0.0 )
5126                                                 nUp = aCellSize.Height() - nEngineHeight;
5127                                         }
5128                                         else    // BOTTOM / STANDARD
5129                                         {
5130                                             if ( nSin < 0.0 )
5131                                                 nUp = aCellSize.Height() - nEngineHeight;
5132                                         }
5133                                         if ( nUp )
5134                                             nAddX += ( nUp * nCos / fabs(nSin) );
5135                                     }
5136 
5137                                     aLogicStart.AdjustX(static_cast<tools::Long>(nAddX) );
5138                                     aLogicStart.AdjustY(static_cast<tools::Long>(nAddY) );
5139                                 }
5140 
5141                                 //  bSimClip is not used here (because nOriVal is set)
5142 
5143                                 pEngine->Draw(*mpDev, aLogicStart, nOriVal);
5144 
5145                                 if (bMetaFile)
5146                                     mpDev->Pop();
5147                                 else
5148                                     mpDev->SetClipRegion();
5149                             }
5150                         }
5151                     }
5152                 }
5153                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
5154             }
5155         }
5156         nRowPosY += pRowInfo[nArrY].nHeight;
5157     }
5158 }
5159 
5160 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5161