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