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