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