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