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