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