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