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
24#include <svx/algitem.hxx>
25#include <editeng/brushitem.hxx>
26#include <svtools/colorcfg.hxx>
27#include <editeng/colritem.hxx>
31#include <editeng/editobj.hxx>
32#include <editeng/editstat.hxx>
34#include <editeng/fhgtitem.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>
47#include <svl/numformat.hxx>
48#include <svl/zforlist.hxx>
49#include <svl/zformat.hxx>
50#include <vcl/svapp.hxx>
51#include <vcl/metric.hxx>
52#include <vcl/outdev.hxx>
54#include <vcl/settings.hxx>
55#include <vcl/glyphitem.hxx>
56#include <vcl/vcllayout.hxx>
58#include <sal/log.hxx>
60#include <osl/diagnose.h>
61#include <tools/stream.hxx>
62
63#include <output.hxx>
64#include <document.hxx>
65#include <formulacell.hxx>
66#include <attrib.hxx>
67#include <patattr.hxx>
68#include <cellform.hxx>
69#include <editutil.hxx>
70#include <progress.hxx>
71#include <scmod.hxx>
72#include <fillinfo.hxx>
73#include <stlsheet.hxx>
74#include <spellcheckcontext.hxx>
75#include <scopetools.hxx>
76
77#include <com/sun/star/i18n/DirectionProperty.hpp>
79#include <comphelper/string.hxx>
80
81#include <memory>
82#include <vector>
83#include <o3tl/lru_map.hxx>
84#include <o3tl/hash_combine.hxx>
85
86#include <math.h>
87
88using namespace com::sun::star;
89
91#define DROPDOWN_BITMAP_SIZE 18
92
93#define DRAWTEXT_MAX 32767
94
95const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;
97
99{
100 ScOutputData* pOutput; // connection
101
102 const ScPatternAttr* pPattern; // attribute
103 const SfxItemSet* pCondSet; // from conditional formatting
104
105 vcl::Font aFont; // created from attributes
107 tools::Long nAscentPixel; // always pixels
113 sal_uInt16 nIndent;
115
116 OUString aString; // contents
123
129
132
133 Color aBackConfigColor; // used for ScPatternAttr::GetFont calls
135 sal_Int32 nRepeatPos;
137
138public:
139 ScDrawStringsVars(ScOutputData* pData, bool bPTL);
140
141 // SetPattern = ex-SetVars
142 // SetPatternSimple: without Font
143
144 void SetPattern(
145 const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
146 SvtScriptType nScript );
147
148 void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );
149
150 bool SetText( const ScRefCellValue& rCell ); // TRUE -> drop pOldPattern
151 void SetHashText();
152 void SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth );
153 void SetAutoText( const OUString& rAutoText );
154
159 const SvxMarginItem* GetMargin() const { return pMargin; }
160
161 sal_uInt16 GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; }
162 sal_uInt16 GetRightTotal() const { return pMargin->GetRightMargin() + nIndent; }
163
164 const OUString& GetString() const { return aString; }
165 const Size& GetTextSize() const { return aTextSize; }
167 tools::Long GetFmtTextWidth(const OUString& rString);
168
169 // Get the effective number format, including formula result types.
170 // This assumes that a formula cell has already been calculated.
172
173 bool GetLineBreak() const { return bLineBreak; }
174 bool IsRepeat() const { return bRepeat; }
175 bool IsShrink() const { return bShrink; }
176 void RepeatToFill( tools::Long nColWidth );
177
179 bool IsRotated() const { return bRotated; }
180
181 void SetShrinkScale( tools::Long nScale, SvtScriptType nScript );
182
183 bool HasCondHeight() const { return pCondSet && SfxItemState::SET ==
185
186 bool HasEditCharacters() const;
187
188 // ScOutputData::LayoutStrings() usually triggers a number of calls that require
189 // to lay out the text, which is relatively slow, so cache that operation.
190 const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const
191 {
193 }
194
195private:
196 tools::Long GetMaxDigitWidth(); // in logic units
200 void TextChanged();
201};
202
204 pOutput ( pData ),
205 pPattern ( nullptr ),
206 pCondSet ( nullptr ),
207 nAscentPixel(0),
208 eAttrOrient ( SvxCellOrientation::Standard ),
209 eAttrHorJust( SvxCellHorJustify::Standard ),
210 eAttrVerJust( SvxCellVerJustify::Bottom ),
211 eAttrHorJustMethod( SvxCellJustifyMethod::Auto ),
212 pMargin ( nullptr ),
213 nIndent ( 0 ),
214 bRotated ( false ),
215 nOriginalWidth( 0 ),
216 nMaxDigitWidth( 0 ),
217 nSignWidth( 0 ),
218 nDotWidth( 0 ),
219 nExpWidth( 0 ),
220 nValueFormat( 0 ),
221 bLineBreak ( false ),
222 bRepeat ( false ),
223 bShrink ( false ),
224 bPixelToLogic( bPTL ),
225 nRepeatPos( -1 ),
226 nRepeatChar( 0x0 )
227{
228 ScModule* pScMod = SC_MOD();
231
232 const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig();
235}
236
238{
239 // text remains valid, size is updated
240
241 OutputDevice* pDev = pOutput->mpDev;
242 OutputDevice* pRefDevice = pOutput->mpRefDevice;
243 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
244
245 // call GetFont with a modified fraction, use only the height
246
247 Fraction aFraction( nScale, 100 );
248 if ( !bPixelToLogic )
249 aFraction *= pOutput->aZoomY;
250 vcl::Font aTmpFont;
251 pPattern->GetFont( aTmpFont, SC_AUTOCOL_RAW, pFmtDevice, &aFraction, pCondSet, nScript );
252 tools::Long nNewHeight = aTmpFont.GetFontHeight();
253 if ( nNewHeight > 0 )
254 aFont.SetFontHeight( nNewHeight );
255
256 // set font and dependent variables as in SetPattern
257
258 pDev->SetFont( aFont );
259 if ( pFmtDevice != pDev )
260 pFmtDevice->SetFont( aFont );
261
262 aMetric = pFmtDevice->GetFontMetric();
263 if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
264 {
266 MapMode aOld = pDefaultDev->GetMapMode();
267 pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
268 aMetric = pDefaultDev->GetFontMetric( aFont );
269 pDefaultDev->SetMapMode( aOld );
270 }
271
273 if ( bPixelToLogic )
274 nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
275
276 SetAutoText( aString ); // same text again, to get text size
277}
278
279namespace {
280
281template<typename ItemType, typename EnumType>
282EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
283{
284 const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet));
285 return static_cast<EnumType>(rItem.GetValue());
286}
287
288bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
289{
290 return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet);
291}
292
293}
294
295static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB nTab )
296{
297 sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, nTab );
298 SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
299 return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT;
300}
301
303 const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
304 SvtScriptType nScript )
305{
306 nMaxDigitWidth = 0;
307 nSignWidth = 0;
308 nDotWidth = 0;
309 nExpWidth = 0;
310
311 pPattern = pNew;
312 pCondSet = pSet;
313
314 // evaluate pPattern
315
316 OutputDevice* pDev = pOutput->mpDev;
317 OutputDevice* pRefDevice = pOutput->mpRefDevice;
318 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
319
320 // font
321
322 ScAutoFontColorMode eColorMode;
324 {
327 else
329 }
330 else
331 eColorMode = SC_AUTOCOL_PRINT;
332
333 if ( bPixelToLogic )
334 pPattern->GetFont( aFont, eColorMode, pFmtDevice, nullptr, pCondSet, nScript,
336 else
337 pPattern->GetFont( aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript,
340
341 // orientation
342
344
345 // alignment
346
348
350 if ( eAttrVerJust == SvxCellVerJustify::Standard )
351 eAttrVerJust = SvxCellVerJustify::Bottom;
352
353 // justification method
354
355 eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet);
356
357 // line break
358
360
361 // handle "repeat" alignment
362
363 bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat );
364 if ( bRepeat )
365 {
366 // "repeat" disables rotation (before constructing the font)
367 eAttrOrient = SvxCellOrientation::Standard;
368
369 // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
370 if ( bLineBreak )
371 eAttrHorJust = SvxCellHorJustify::Standard;
372 }
373
374 sal_Int16 nRot;
375 switch (eAttrOrient)
376 {
377 case SvxCellOrientation::Standard:
378 nRot = 0;
379 bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 &&
380 !bRepeat;
381 break;
382 case SvxCellOrientation::Stacked:
383 nRot = 0;
384 bRotated = false;
385 break;
386 case SvxCellOrientation::TopBottom:
387 nRot = 2700;
388 bRotated = false;
389 break;
390 case SvxCellOrientation::BottomUp:
391 nRot = 900;
392 bRotated = false;
393 break;
394 default:
395 OSL_FAIL("Invalid SvxCellOrientation value");
396 nRot = 0;
397 bRotated = false;
398 break;
399 }
401
402 // syntax mode
403
405 pOutput->SetSyntaxColor(&aFont, rCell);
406
407 // There is no cell attribute for kerning, default is kerning OFF, all
408 // kerning is stored at an EditText object that is drawn using EditEngine.
409 aFont.SetKerning( FontKerning::NONE);
410
411 pDev->SetFont( aFont );
412 if ( pFmtDevice != pDev )
413 pFmtDevice->SetFont( aFont );
414
415 aMetric = pFmtDevice->GetFontMetric();
416
417 // if there is the leading 0 on a printer device, we have problems
418 // -> take metric from the screen (as for EditEngine!)
419 if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
420 {
422 MapMode aOld = pDefaultDev->GetMapMode();
423 pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
424 aMetric = pDefaultDev->GetFontMetric( aFont );
425 pDefaultDev->SetMapMode( aOld );
426 }
427
429 if ( bPixelToLogic )
430 nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
431
432 Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() );
433 pDev->SetTextLineColor( aULineColor );
434
435 Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() );
436 pDev->SetOverlineColor( aOLineColor );
437
438 // number format
439
441
442 // margins
444 if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right )
445 nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
446 else
447 nIndent = 0;
448
449 // "Shrink to fit"
450
452
453 // at least the text size needs to be retrieved again
456}
457
459{
460 nMaxDigitWidth = 0;
461 nSignWidth = 0;
462 nDotWidth = 0;
463 nExpWidth = 0;
464
465 // Is called, when the font variables do not change (!StringDiffer)
466
467 pPattern = pNew;
468 pCondSet = pSet;
469
470 // number format
471
472 sal_uLong nOld = nValueFormat;
474
475 if (nValueFormat != nOld)
476 maLastCell.clear(); // always reformat
477
478 // margins
479
481
482 if ( eAttrHorJust == SvxCellHorJustify::Left )
483 nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
484 else
485 nIndent = 0;
486
487 // "Shrink to fit"
488
490}
491
492static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell )
493{
494 return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE &&
495 rCell.getDouble() == rOldCell.getDouble();
496}
497
499{
500 bool bChanged = false;
501
502 if (!rCell.isEmpty())
503 {
504 if (!SameValue(rCell, maLastCell))
505 {
506 maLastCell = rCell; // store cell
507
508 const Color* pColor;
509 sal_uLong nFormat = nValueFormat;
511 nFormat, &pColor,
513 *pOutput->mpDoc,
516 true );
517 if ( nFormat )
518 {
519 nRepeatPos = aString.indexOf( 0x1B );
520 if ( nRepeatPos != -1 )
521 {
522 if (nRepeatPos + 1 == aString.getLength())
523 nRepeatPos = -1;
524 else
525 {
527 // delete placeholder and char to repeat
528 aString = aString.replaceAt( nRepeatPos, 2, u"" );
529 // Do not cache/reuse a repeat-filled string, column
530 // widths or fonts or sizes may differ.
532 }
533 }
534 }
535 else
536 {
537 nRepeatPos = -1;
538 nRepeatChar = 0x0;
539 }
540 if (aString.getLength() > DRAWTEXT_MAX)
541 aString = aString.copy(0, DRAWTEXT_MAX);
542
544 {
545 OutputDevice* pDev = pOutput->mpDev;
546 aFont.SetColor(*pColor);
547 pDev->SetFont( aFont ); // only for output
548 bChanged = true;
549 maLastCell.clear(); // next time return here again
550 }
551
552 TextChanged();
553 }
554 // otherwise keep string/size
555 }
556 else
557 {
558 aString.clear();
560 aTextSize = Size(0,0);
561 nOriginalWidth = 0;
562 }
563
564 return bChanged;
565}
566
568{
569 SetAutoText("###");
570}
571
573{
574 if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() )
575 return;
576
577 tools::Long nCharWidth = GetFmtTextWidth(OUString(nRepeatChar));
578
579 if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) )
580 return;
581
582 // Are there restrictions on the cell type we should filter out here ?
583 tools::Long nTextWidth = aTextSize.Width();
584 if ( bPixelToLogic )
585 {
586 nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
587 nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
588 }
589
590 tools::Long nSpaceToFill = nColWidth - nTextWidth;
591 if ( nSpaceToFill <= nCharWidth )
592 return;
593
594 sal_Int32 nCharsToInsert = nSpaceToFill / nCharWidth;
595 OUStringBuffer aFill(nCharsToInsert);
596 comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
597 aString = aString.replaceAt( nRepeatPos, 0, aFill );
598 TextChanged();
599}
600
602{
603 // #i113045# do the single-character width calculations in logic units
604 if (bPixelToLogic)
605 nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
606
607 CellType eType = rCell.getType();
609 // must be a value or formula cell.
610 return;
611
612 if (eType == CELLTYPE_FORMULA)
613 {
614 ScFormulaCell* pFCell = rCell.getFormula();
615 if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
616 {
617 SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
618 return;
619 }
620 // If it's formula, the result must be a value.
621 if (!pFCell->IsValue())
622 return;
623 }
624
626 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
627 {
628 // Not 'General' number format. Set hash text and bail out.
629 SetHashText();
630 return;
631 }
632
633 double fVal = rCell.getValue();
634
635 const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat);
636 if (!pNumFormat)
637 return;
638
639 tools::Long nMaxDigit = GetMaxDigitWidth();
640 if (!nMaxDigit)
641 return;
642
643 sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
644 {
645 OUString sTempOut(aString);
646 if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
647 {
648 aString = sTempOut;
649 // Failed to get output string. Bail out.
650 return;
651 }
652 aString = sTempOut;
653 }
654 sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
655 sal_Int32 nLen = aString.getLength();
656 sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0];
657 for( sal_Int32 i = 0; i < nLen; ++i )
658 {
659 sal_Unicode c = aString[i];
660 if (c == '-')
661 ++nSignCount;
662 else if (c == cDecSep)
663 ++nDecimalCount;
664 else if (c == 'E')
665 ++nExpCount;
666 }
667
668 // #i112250# A small value might be formatted as "0" when only counting the digits,
669 // but fit into the column when considering the smaller width of the decimal separator.
670 if (aString == "0" && fVal != 0.0)
671 nDecimalCount = 1;
672
673 if (nDecimalCount)
674 nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
675 if (nSignCount)
676 nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
677 if (nExpCount)
678 nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
679
680 if (nDecimalCount || nSignCount || nExpCount)
681 {
682 // Re-calculate.
683 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
684 OUString sTempOut(aString);
685 if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
686 {
687 aString = sTempOut;
688 // Failed to get output string. Bail out.
689 return;
690 }
691 aString = sTempOut;
692 }
693
694 tools::Long nActualTextWidth = GetFmtTextWidth(aString);
695 if (nActualTextWidth > nWidth)
696 {
697 // Even after the decimal adjustment the text doesn't fit. Give up.
698 SetHashText();
699 return;
700 }
701
702 TextChanged();
703 maLastCell.clear(); // #i113022# equal cell and format in another column may give different string
704}
705
706void ScDrawStringsVars::SetAutoText( const OUString& rAutoText )
707{
708 aString = rAutoText;
709
710 OutputDevice* pRefDevice = pOutput->mpRefDevice;
711 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
713 aTextSize.setHeight( pFmtDevice->GetTextHeight() );
714
715 if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
716 {
717 double fMul = pOutput->GetStretch();
718 aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
719 }
720
722 if ( GetOrient() != SvxCellOrientation::Standard )
723 {
724 tools::Long nTemp = aTextSize.Height();
726 aTextSize.setWidth( nTemp );
727 }
728
730 if ( bPixelToLogic )
731 aTextSize = pRefDevice->LogicToPixel( aTextSize );
732
733 maLastCell.clear(); // the same text may fit in the next cell
734}
735
737{
738 if (nMaxDigitWidth > 0)
739 return nMaxDigitWidth;
740
741 for (char i = 0; i < 10; ++i)
742 {
743 char cDigit = '0' + i;
744 // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
745 tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
746 nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
747 }
748 return nMaxDigitWidth;
749}
750
752{
753 if (nSignWidth > 0)
754 return nSignWidth;
755
756 nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-'));
757 return nSignWidth;
758}
759
761{
762 if (nDotWidth > 0)
763 return nDotWidth;
764
765 const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator;
766 nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
767 return nDotWidth;
768}
769
771{
772 if (nExpWidth > 0)
773 return nExpWidth;
774
775 nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E'));
776 return nExpWidth;
777}
778
780{
781 return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString ));
782}
783
785{
786 OutputDevice* pRefDevice = pOutput->mpRefDevice;
787 OutputDevice* pFmtDevice = pOutput->pFmtDevice;
789 aTextSize.setHeight( pFmtDevice->GetTextHeight() );
790
791 if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
792 {
793 double fMul = pOutput->GetStretch();
794 aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
795 }
796
798 if ( GetOrient() != SvxCellOrientation::Standard )
799 {
800 tools::Long nTemp = aTextSize.Height();
802 aTextSize.setWidth( nTemp );
803 }
804
806 if ( bPixelToLogic )
807 aTextSize = pRefDevice->LogicToPixel( aTextSize );
808}
809
811{
812 for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
813 {
814 switch(aString[nIdx])
815 {
816 case CHAR_NBSP:
817 case CHAR_SHY:
818 case CHAR_ZWSP:
819 case CHAR_LRM:
820 case CHAR_RLM:
821 case CHAR_NBHY:
822 case CHAR_WJ:
823 return true;
824 default:
825 break;
826 }
827 }
828
829 return false;
830}
831
833{
834 if ( mpRefDevice->IsMapModeEnabled() )
835 {
836 // If a non-trivial MapMode is set, its scale is now already
837 // taken into account in the OutputDevice's font handling
838 // (OutputDevice::ImplNewFont, see #95414#).
839 // The old handling below is only needed for pixel output.
840 return 1.0;
841 }
842
843 // calculation in double is faster than Fraction multiplication
844 // and doesn't overflow
845
846 if ( mpRefDevice == pFmtDevice )
847 {
848 MapMode aOld = mpRefDevice->GetMapMode();
849 return static_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
850 }
851 else
852 {
853 // when formatting for printer, device map mode has already been taken care of
854 return static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
855 }
856}
857
858// output strings
859
860static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, ScRefCellValue& rCell )
861{
862 vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
863
864 OUString aURL;
865 OUString aCellText;
866 if (rCell.getType() == CELLTYPE_FORMULA)
867 {
868 ScFormulaCell* pFCell = rCell.getFormula();
869 if ( pFCell->IsHyperLinkCell() )
870 pFCell->GetURLResult( aURL, aCellText );
871 }
872
873 if ( !aURL.isEmpty() && pPDFData )
874 {
876 aBookmark.nLinkId = pPDFData->CreateLink(rRect, aCellText);
877 aBookmark.aBookmark = aURL;
878 std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
879 rBookmarks.push_back( aBookmark );
880 }
881}
882
884{
885 switch (rCell.getType())
886 {
887 case CELLTYPE_VALUE:
888 pFont->SetColor(*mxValueColor);
889 break;
890 case CELLTYPE_STRING:
891 pFont->SetColor(*mxTextColor);
892 break;
893 case CELLTYPE_FORMULA:
894 pFont->SetColor(*mxFormulaColor);
895 break;
896 default:
897 {
898 // added to avoid warnings
899 }
900 }
901}
902
903static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
904{
905 ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
906 SfxItemSet aSet( rEngine.GetEmptyItemSet() );
907 aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
908 rEngine.QuickSetAttribs( aSet, aSel );
909 // function is called with update mode set to FALSE
910}
911
913{
914 Color aColor;
915 switch (rCell.getType())
916 {
917 case CELLTYPE_VALUE:
918 aColor = *mxValueColor;
919 break;
920 case CELLTYPE_STRING:
921 aColor = *mxTextColor;
922 break;
923 case CELLTYPE_FORMULA:
924 aColor = *mxFormulaColor;
925 break;
926 default:
927 {
928 // added to avoid warnings
929 }
930 }
931 lcl_SetEditColor( rEngine, aColor );
932}
933
935 SCCOL& rOverX, SCROW& rOverY,
936 bool bVisRowChanged )
937{
938 bool bDoMerge = false;
939 bool bIsLeft = ( nX == nVisX1 );
940 bool bIsTop = ( nY == nVisY1 ) || bVisRowChanged;
941
942 bool bHOver;
943 bool bVOver;
944 bool bHidden;
945
946 if (!mpDoc->ColHidden(nX, nTab) && nX >= nX1 && nX <= nX2
947 && !mpDoc->RowHidden(nY, nTab) && nY >= nY1 && nY <= nY2)
948 {
949 ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
950 bHOver = pInfo->bHOverlapped;
951 bVOver = pInfo->bVOverlapped;
952 }
953 else
954 {
955 ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG)->GetValue();
956 bHOver = bool(nOverlap2 & ScMF::Hor);
957 bVOver = bool(nOverlap2 & ScMF::Ver);
958 }
959
960 if ( bHOver && bVOver )
961 bDoMerge = bIsLeft && bIsTop;
962 else if ( bHOver )
963 bDoMerge = bIsLeft;
964 else if ( bVOver )
965 bDoMerge = bIsTop;
966
967 rOverX = nX;
968 rOverY = nY;
969
970 while (bHOver) // nY constant
971 {
972 --rOverX;
973 bHidden = mpDoc->ColHidden(rOverX, nTab);
974 if ( !bDoMerge && !bHidden )
975 return false;
976
977 if (rOverX >= nX1 && !bHidden)
978 {
979 bHOver = pRowInfo[nArrY].cellInfo(rOverX).bHOverlapped;
980 bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
981 }
982 else
983 {
984 ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, nTab, ATTR_MERGE_FLAG)->GetValue();
985 bHOver = bool(nOverlap & ScMF::Hor);
986 bVOver = bool(nOverlap & ScMF::Ver);
987 }
988 }
989
990 while (bVOver)
991 {
992 --rOverY;
993 bHidden = mpDoc->RowHidden(rOverY, nTab);
994 if ( !bDoMerge && !bHidden )
995 return false;
996
997 if (nArrY>0)
998 --nArrY; // local copy !
999
1000 if (rOverX >= nX1 && rOverY >= nY1 &&
1001 !mpDoc->ColHidden(rOverX, nTab) &&
1002 !mpDoc->RowHidden(rOverY, nTab) &&
1003 pRowInfo[nArrY].nRowNo == rOverY)
1004 {
1005 bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
1006 }
1007 else
1008 {
1009 ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, nTab, ATTR_MERGE_FLAG )->GetValue();
1010 bVOver = bool(nOverlap & ScMF::Ver);
1011 }
1012 }
1013
1014 return true;
1015}
1016
1017static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern )
1018{
1019 OSL_ENSURE( pNewPattern, "pNewPattern" );
1020
1021 if ( pNewPattern == rpOldPattern )
1022 return false;
1023 else if ( !rpOldPattern )
1024 return true;
1025 else if ( &pNewPattern->GetItem( ATTR_FONT ) != &rpOldPattern->GetItem( ATTR_FONT ) )
1026 return true;
1027 else if ( &pNewPattern->GetItem( ATTR_CJK_FONT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT ) )
1028 return true;
1029 else if ( &pNewPattern->GetItem( ATTR_CTL_FONT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT ) )
1030 return true;
1031 else if ( &pNewPattern->GetItem( ATTR_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) )
1032 return true;
1033 else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) )
1034 return true;
1035 else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) )
1036 return true;
1037 else if ( &pNewPattern->GetItem( ATTR_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) )
1038 return true;
1039 else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) )
1040 return true;
1041 else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) )
1042 return true;
1043 else if ( &pNewPattern->GetItem( ATTR_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_FONT_POSTURE ) )
1044 return true;
1045 else if ( &pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) )
1046 return true;
1047 else if ( &pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) )
1048 return true;
1049 else if ( &pNewPattern->GetItem( ATTR_FONT_UNDERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) )
1050 return true;
1051 else if ( &pNewPattern->GetItem( ATTR_FONT_OVERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) )
1052 return true;
1053 else if ( &pNewPattern->GetItem( ATTR_FONT_WORDLINE ) != &rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) )
1054 return true;
1055 else if ( &pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ) != &rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) )
1056 return true;
1057 else if ( &pNewPattern->GetItem( ATTR_FONT_CONTOUR ) != &rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) )
1058 return true;
1059 else if ( &pNewPattern->GetItem( ATTR_FONT_SHADOWED ) != &rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) )
1060 return true;
1061 else if ( &pNewPattern->GetItem( ATTR_FONT_COLOR ) != &rpOldPattern->GetItem( ATTR_FONT_COLOR ) )
1062 return true;
1063 else if ( &pNewPattern->GetItem( ATTR_HOR_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) )
1064 return true;
1065 else if ( &pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) )
1066 return true;
1067 else if ( &pNewPattern->GetItem( ATTR_VER_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) )
1068 return true;
1069 else if ( &pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) )
1070 return true;
1071 else if ( &pNewPattern->GetItem( ATTR_STACKED ) != &rpOldPattern->GetItem( ATTR_STACKED ) )
1072 return true;
1073 else if ( &pNewPattern->GetItem( ATTR_LINEBREAK ) != &rpOldPattern->GetItem( ATTR_LINEBREAK ) )
1074 return true;
1075 else if ( &pNewPattern->GetItem( ATTR_MARGIN ) != &rpOldPattern->GetItem( ATTR_MARGIN ) )
1076 return true;
1077 else if ( &pNewPattern->GetItem( ATTR_ROTATE_VALUE ) != &rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) )
1078 return true;
1079 else if ( &pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ) != &rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) )
1080 return true;
1081 else if ( &pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ) != &rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) )
1082 return true;
1083 else if ( &pNewPattern->GetItem( ATTR_FONT_RELIEF ) != &rpOldPattern->GetItem( ATTR_FONT_RELIEF ) )
1084 return true;
1085 else if ( &pNewPattern->GetItem( ATTR_BACKGROUND ) != &rpOldPattern->GetItem( ATTR_BACKGROUND ) )
1086 return true; // needed with automatic text color
1087 else
1088 {
1089 rpOldPattern = pNewPattern;
1090 return false;
1091 }
1092}
1093
1094static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc,
1095 const ScFormulaCell* pFCell )
1096{
1097 if ( !bProgress && pFCell->GetDirty() )
1098 {
1100 bProgress = true;
1101 }
1102}
1103
1104static bool IsAmbiguousScript( SvtScriptType nScript )
1105{
1106 return ( nScript != SvtScriptType::LATIN &&
1107 nScript != SvtScriptType::ASIAN &&
1108 nScript != SvtScriptType::COMPLEX );
1109}
1110
1111bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
1112{
1113 // pThisRowInfo may be NULL
1114
1115 bool bEmpty;
1116 if ( pThisRowInfo && nX <= nX2 )
1117 bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1118 else
1119 {
1120 ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1121 bEmpty = aCell.isEmpty();
1122 }
1123
1124 if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
1125 {
1126 // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
1127 // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
1128
1129 bool bIsPrint = ( eType == OUTTYPE_PRINTER );
1130
1131 if ( bIsPrint || bTabProtected )
1132 {
1133 const ScProtectionAttr* pAttr =
1134 mpDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
1135 if ( bIsPrint && pAttr->GetHidePrint() )
1136 bEmpty = true;
1137 else if ( bTabProtected )
1138 {
1139 if ( pAttr->GetHideCell() )
1140 bEmpty = true;
1141 else if ( mbShowFormulas && pAttr->GetHideFormula() )
1142 {
1144 bEmpty = true;
1145 }
1146 }
1147 }
1148 }
1149 return bEmpty;
1150}
1151
1153{
1154 rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP));
1155 if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow))
1156 rCell.clear();
1157}
1158
1160{
1161 // apply the same logic here as in DrawStrings/DrawEdit:
1162 // Stop at non-empty or merged or overlapped cell,
1163 // where a note is empty as well as a cell that's hidden by protection settings
1164
1165 ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
1166 if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY))
1167 return false;
1168
1169 const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, nTab );
1170 return !(pPattern->GetItem(ATTR_MERGE).IsMerged() ||
1171 pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped());
1172}
1173
1174// nX, nArrY: loop variables from DrawStrings / DrawEdit
1175// nPosX, nPosY: corresponding positions for nX, nArrY
1176// nCellX, nCellY: position of the cell that contains the text
1177// nNeeded: Text width, including margin
1178// rPattern: cell format at nCellX, nCellY
1179// nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings
1180// bCellIsValue: if set, don't extend into empty cells
1181// bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
1182// bOverwrite: if set, also extend into non-empty cells (for rotated text)
1183// rParam output: various area parameters.
1184
1186 SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
1187 const ScPatternAttr& rPattern,
1188 sal_uInt16 nHorJustify, bool bCellIsValue,
1189 bool bBreak, bool bOverwrite,
1190 OutputAreaParam& rParam )
1191{
1192 // rThisRowInfo may be for a different row than nCellY, is still used for clip marks
1193 RowInfo& rThisRowInfo = pRowInfo[nArrY];
1194
1195 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1196
1197 tools::Long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX
1198 SCCOL nCompCol = nX;
1199 while ( nCellX > nCompCol )
1200 {
1202 tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1203 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1204 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1205 nCellPosX += nColWidth * nLayoutSign;
1206 ++nCompCol;
1207 }
1208 while ( nCellX < nCompCol )
1209 {
1210 --nCompCol;
1211 tools::Long nColWidth = ( nCompCol <= nX2 ) ?
1212 pRowInfo[0].basicCellInfo(nCompCol).nWidth :
1213 static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
1214 nCellPosX -= nColWidth * nLayoutSign;
1215 }
1216
1217 tools::Long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY
1218 SCSIZE nCompArr = nArrY;
1219 SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
1220 while ( nCellY > nCompRow )
1221 {
1222 if ( nCompArr + 1 < nArrCount )
1223 {
1224 nCellPosY += pRowInfo[nCompArr].nHeight;
1225 ++nCompArr;
1226 nCompRow = pRowInfo[nCompArr].nRowNo;
1227 }
1228 else
1229 {
1230 sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, nTab );
1231 if ( nDocHeight )
1232 nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY );
1233 ++nCompRow;
1234 }
1235 }
1236 nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, mnPPTY );
1237
1238 const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE );
1239 bool bMerged = pMerge->IsMerged();
1240 tools::Long nMergeCols = pMerge->GetColMerge();
1241 if ( nMergeCols == 0 )
1242 nMergeCols = 1;
1243 tools::Long nMergeRows = pMerge->GetRowMerge();
1244 if ( nMergeRows == 0 )
1245 nMergeRows = 1;
1246
1247 tools::Long nMergeSizeX = 0;
1248 for ( tools::Long i=0; i<nMergeCols; i++ )
1249 {
1250 tools::Long nColWidth = ( nCellX+i <= nX2 ) ?
1251 pRowInfo[0].basicCellInfo(nCellX+i).nWidth :
1252 static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * mnPPTX );
1253 nMergeSizeX += nColWidth;
1254 }
1255 tools::Long nMergeSizeY = 0;
1256 short nDirect = 0;
1257 if ( rThisRowInfo.nRowNo == nCellY )
1258 {
1259 // take first row's height from row info
1260 nMergeSizeY += rThisRowInfo.nHeight;
1261 nDirect = 1; // skip in loop
1262 }
1263 // following rows always from document
1264 nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, mnPPTY);
1265
1266 --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines)
1267
1268 rParam.mnColWidth = nMergeSizeX; // store the actual column width.
1269 rParam.mnLeftClipLength = rParam.mnRightClipLength = 0;
1270
1271 // construct the rectangles using logical left/right values (justify is called at the end)
1272
1273 // rAlignRect is the single cell or merged area, used for alignment.
1274
1275 rParam.maAlignRect.SetLeft( nCellPosX );
1276 rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign );
1277 rParam.maAlignRect.SetTop( nCellPosY );
1278 rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 );
1279
1280 // rClipRect is all cells that are used for output.
1281 // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.
1282
1283 rParam.maClipRect = rParam.maAlignRect;
1284 if ( nNeeded > nMergeSizeX )
1285 {
1286 SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify);
1287
1288 tools::Long nMissing = nNeeded - nMergeSizeX;
1289 tools::Long nLeftMissing = 0;
1290 tools::Long nRightMissing = 0;
1291 switch ( eHorJust )
1292 {
1293 case SvxCellHorJustify::Left:
1294 nRightMissing = nMissing;
1295 break;
1296 case SvxCellHorJustify::Right:
1297 nLeftMissing = nMissing;
1298 break;
1299 case SvxCellHorJustify::Center:
1300 nLeftMissing = nMissing / 2;
1301 nRightMissing = nMissing - nLeftMissing;
1302 break;
1303 default:
1304 {
1305 // added to avoid warnings
1306 }
1307 }
1308
1309 // nLeftMissing, nRightMissing are logical, eHorJust values are visual
1310 if ( bLayoutRTL )
1311 ::std::swap( nLeftMissing, nRightMissing );
1312
1313 SCCOL nRightX = nCellX;
1314 SCCOL nLeftX = nCellX;
1315 if ( !bMerged && !bCellIsValue && !bBreak )
1316 {
1317 // look for empty cells into which the text can be extended
1318
1319 while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
1320 {
1321 ++nRightX;
1322 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, nTab ) * mnPPTX );
1323 nRightMissing -= nAdd;
1324 rParam.maClipRect.AdjustRight(nAdd * nLayoutSign );
1325
1326 if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
1327 rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true;
1328 }
1329
1330 while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
1331 {
1332 if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
1333 rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true;
1334
1335 --nLeftX;
1336 tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, nTab ) * mnPPTX );
1337 nLeftMissing -= nAdd;
1338 rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) );
1339 }
1340 }
1341
1342 // Set flag and reserve space for clipping mark triangle,
1343 // even if rThisRowInfo isn't for nCellY (merged cells).
1344 if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
1345 {
1346 rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right;
1347 bAnyClipped = true;
1348 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1349 rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
1350 }
1351 if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
1352 {
1353 rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left;
1354 bAnyClipped = true;
1355 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
1356 rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign );
1357 }
1358
1359 rParam.mbLeftClip = ( nLeftMissing > 0 );
1360 rParam.mbRightClip = ( nRightMissing > 0 );
1361 rParam.mnLeftClipLength = nLeftMissing;
1362 rParam.mnRightClipLength = nRightMissing;
1363 }
1364 else
1365 {
1366 rParam.mbLeftClip = rParam.mbRightClip = false;
1367
1368 // leave space for AutoFilter on screen
1369 // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)
1370
1371 if ( eType==OUTTYPE_WINDOW &&
1372 ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) &&
1373 ( !bBreak || mpRefDevice == pFmtDevice ) )
1374 {
1375 // filter drop-down width depends on row height
1376 double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
1377 fZoom = fZoom > 1.0 ? fZoom : 1.0;
1378 const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE;
1379 bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
1380 if ( bFit )
1381 {
1382 // content fits even in the remaining area without the filter button
1383 // -> align within that remaining area
1384
1385 rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) );
1386 rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) );
1387 }
1388 }
1389 }
1390
1391 // justify both rectangles for alignment calculation, use with DrawText etc.
1392
1393 rParam.maAlignRect.Normalize();
1394 rParam.maClipRect.Normalize();
1395}
1396
1397namespace {
1398
1399bool beginsWithRTLCharacter(const OUString& rStr)
1400{
1401 if (rStr.isEmpty())
1402 return false;
1403
1404 switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
1405 {
1406 case i18n::DirectionProperty_RIGHT_TO_LEFT:
1407 case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC:
1408 case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING:
1409 case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE:
1410 return true;
1411 default:
1412 ;
1413 }
1414
1415 return false;
1416}
1417
1418}
1419
1426 bool bCellIsValue, const OUString& rText,
1427 const ScPatternAttr& rPattern, const SfxItemSet* pCondSet,
1428 const ScDocument* pDoc, SCTAB nTab, const bool bNumberFormatIsText )
1429{
1430 SvxCellHorJustify eHorJustContext = eInHorJust;
1431 bool bUseWritingDirection = false;
1432 if (eInHorJust == SvxCellHorJustify::Standard)
1433 {
1434 // fdo#32530: Default alignment depends on value vs
1435 // string, and the direction of the 1st letter.
1436 if (beginsWithRTLCharacter( rText)) //If language is RTL
1437 {
1438 if (bCellIsValue)
1439 eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1440 else
1441 eHorJustContext = SvxCellHorJustify::Right;
1442 }
1443 else if (bCellIsValue) //If language is not RTL
1444 eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right;
1445 else
1446 bUseWritingDirection = true;
1447 }
1448
1449 if (bUseWritingDirection ||
1450 eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
1451 {
1452 SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet);
1453 if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
1454 eHorJustContext = SvxCellHorJustify::Left;
1455 else if (nDirection == SvxFrameDirection::Environment)
1456 {
1457 SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL");
1458 // fdo#73588: The content of the cell must also
1459 // begin with a RTL character to be right
1460 // aligned; otherwise, it should be left aligned.
1461 eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
1462 }
1463 else
1464 eHorJustContext = SvxCellHorJustify::Right;
1465 }
1466 return eHorJustContext;
1467}
1468
1469void ScOutputData::DrawStrings( bool bPixelToLogic )
1470{
1471 LayoutStrings(bPixelToLogic);
1472}
1473
1474tools::Rectangle ScOutputData::LayoutStrings(bool bPixelToLogic, bool bPaint, const ScAddress &rAddress)
1475{
1476 bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings();
1477 mpDoc->SetLayoutStrings(true);
1478 comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings] {
1479 mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings);
1480 });
1481
1482 OSL_ENSURE( mpDev == mpRefDevice ||
1483 mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(),
1484 "LayoutStrings: different MapUnits ?!?!" );
1485
1486 vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(mpDev->GetExtOutDevData() );
1487
1488 sc::IdleSwitch aIdleSwitch(*mpDoc, false);
1489 ScDrawStringsVars aVars( this, bPixelToLogic );
1490
1491 bool bProgress = false;
1492
1493 tools::Long nInitPosX = nScrX;
1494 if ( bLayoutRTL )
1495 nInitPosX += nMirrorW - 1; // pixels
1496 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1497
1498 SCCOL nLastContentCol = mpDoc->MaxCol();
1499 if ( nX2 < mpDoc->MaxCol() )
1500 nLastContentCol = sal::static_int_cast<SCCOL>(
1501 nLastContentCol - mpDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, mpDoc->MaxCol(), nY2, nTab, DIR_RIGHT ) );
1502 SCCOL nLoopStartX = nX1;
1503 if ( nX1 > 0 )
1504 --nLoopStartX; // start before nX1 for rest of long text to the left
1505
1506 // variables for GetOutputArea
1507 OutputAreaParam aAreaParam;
1508 bool bCellIsValue = false;
1509 tools::Long nNeededWidth = 0;
1510 const ScPatternAttr* pPattern = nullptr;
1511 const SfxItemSet* pCondSet = nullptr;
1512 const ScPatternAttr* pOldPattern = nullptr;
1513 const SfxItemSet* pOldCondSet = nullptr;
1514 SvtScriptType nOldScript = SvtScriptType::NONE;
1515
1516 // Try to limit interpreting to only visible cells. Calling e.g. IsValue()
1517 // on a formula cell that needs interpreting would call Interpret()
1518 // for the entire formula group, which could be large.
1520
1521 // alternative pattern instances in case we need to modify the pattern
1522 // before processing the cell value.
1523 std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
1524
1525 std::vector<sal_Int32> aDX;
1526 tools::Long nPosY = nScrY;
1527 for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
1528 {
1529 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1530 SCROW nY = pThisRowInfo->nRowNo;
1531 if ((bPaint && pThisRowInfo->bChanged) || (!bPaint && rAddress.Row() == nY))
1532 {
1533 tools::Long nPosX = nInitPosX;
1534 if ( nLoopStartX < nX1 )
1535 nPosX -= pRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign;
1536 for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
1537 {
1538 bool bMergeEmpty = false;
1539 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
1540 bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
1541
1542 SCCOL nCellX = nX; // position where the cell really starts
1543 SCROW nCellY = nY;
1544 bool bDoCell = false;
1545 bool bUseEditEngine = false;
1546
1547 // Part of a merged cell?
1548
1549 bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped);
1550 if ( bOverlapped )
1551 {
1552 bEmpty = true;
1553
1554 SCCOL nOverX; // start of the merged cells
1555 SCROW nOverY;
1556 bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
1557 if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
1558 {
1559 nCellX = nOverX;
1560 nCellY = nOverY;
1561 bDoCell = true;
1562 }
1563 else
1564 bMergeEmpty = true;
1565 }
1566
1567 // Rest of a long text further to the left?
1568
1569 if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
1570 {
1571 SCCOL nTempX=nX1;
1572 while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1573 --nTempX;
1574
1575 if ( nTempX < nX1 &&
1576 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1578 {
1579 nCellX = nTempX;
1580 bDoCell = true;
1581 }
1582 }
1583
1584 // Rest of a long text further to the right?
1585
1586 if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
1587 {
1588 // don't have to look further than nLastContentCol
1589
1590 SCCOL nTempX=nX;
1591 while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
1592 ++nTempX;
1593
1594 if ( nTempX > nX &&
1595 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
1597 {
1598 nCellX = nTempX;
1599 bDoCell = true;
1600 }
1601 }
1602
1603 // normal visible cell
1604
1605 if (!bEmpty)
1606 bDoCell = true;
1607
1608 // don't output the cell that's being edited
1609
1610 if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
1611 bDoCell = false;
1612
1613 // skip text in cell if data bar/icon set is set and only value selected
1614 if ( bDoCell )
1615 {
1616 if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
1617 bDoCell = false;
1618 if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
1619 bDoCell = false;
1620 }
1621
1622 // output the cell text
1623
1624 ScRefCellValue aCell;
1625 if (bDoCell)
1626 {
1627 if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
1628 aCell = pThisRowInfo->cellInfo(nCellX).maCell;
1629 else
1630 GetVisibleCell( nCellX, nCellY, nTab, aCell ); // get from document
1631 if (aCell.isEmpty())
1632 bDoCell = false;
1633 else if (aCell.getType() == CELLTYPE_EDIT)
1634 bUseEditEngine = true;
1635 }
1636
1637 // Check if this cell is mis-spelled.
1638 if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
1639 {
1640 if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
1641 bUseEditEngine = true;
1642 }
1643
1644 if (bDoCell && !bUseEditEngine)
1645 {
1646 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
1647 {
1648 ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
1649 pPattern = rCellInfo.pPatternAttr;
1650 pCondSet = rCellInfo.pConditionSet;
1651
1652 if ( !pPattern )
1653 {
1654 // #i68085# pattern from cell info for hidden columns is null,
1655 // test for null is quicker than using column flags
1656 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1657 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1658 }
1659 }
1660 else // get from document
1661 {
1662 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
1663 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
1664 }
1666 {
1667 aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1668 ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1669 if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
1670 {
1671 pAltPattern->SetStyleSheet(pPreviewStyle);
1672 }
1673 else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
1674 {
1675 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
1676 pAltPattern->GetItemSet().Put( *pItem );
1677 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
1678 pAltPattern->GetItemSet().Put( *pItem );
1679 if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
1680 pAltPattern->GetItemSet().Put( *pItem );
1681 }
1682 pPattern = pAltPattern;
1683 }
1684
1685 if (aCell.hasNumeric() &&
1686 pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
1687 {
1688 // Disable line break when the cell content is numeric.
1689 aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
1690 ScPatternAttr* pAltPattern = aAltPatterns.back().get();
1691 ScLineBreakCell aLineBreak(false);
1692 pAltPattern->GetItemSet().Put(aLineBreak);
1693 pPattern = pAltPattern;
1694 }
1695
1697 ScAddress(nCellX, nCellY, nTab),
1698 pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet));
1699
1700 if (nScript == SvtScriptType::NONE)
1702
1703 if ( pPattern != pOldPattern || pCondSet != pOldCondSet ||
1704 nScript != nOldScript || mbSyntaxMode )
1705 {
1706 if ( StringDiffer(pOldPattern,pPattern) ||
1707 pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode )
1708 {
1709 aVars.SetPattern(pPattern, pCondSet, aCell, nScript);
1710 }
1711 else
1712 aVars.SetPatternSimple( pPattern, pCondSet );
1713 pOldPattern = pPattern;
1714 pOldCondSet = pCondSet;
1715 nOldScript = nScript;
1716 }
1717
1718 // use edit engine for rotated, stacked or mixed-script text
1719 if ( aVars.GetOrient() == SvxCellOrientation::Stacked ||
1720 aVars.IsRotated() || IsAmbiguousScript(nScript) )
1721 bUseEditEngine = true;
1722 }
1723 if (bDoCell && !bUseEditEngine)
1724 {
1725 bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA);
1726 if ( bFormulaCell )
1727 lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula());
1728 if ( aVars.SetText(aCell) )
1729 pOldPattern = nullptr;
1730 bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult());
1731 }
1732 tools::Long nTotalMargin = 0;
1733 SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard;
1734 if (bDoCell && !bUseEditEngine)
1735 {
1736 CellType eCellType = aCell.getType();
1737 bCellIsValue = ( eCellType == CELLTYPE_VALUE );
1738 if ( eCellType == CELLTYPE_FORMULA )
1739 {
1740 ScFormulaCell* pFCell = aCell.getFormula();
1741 bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
1742 }
1743
1744 const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
1745 eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(),
1746 *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText );
1747
1748 bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block );
1749 // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats
1750 // Must be synchronized with ScColumn::GetNeededSize()
1751 SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1752 if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER))
1753 bBreak = false;
1754
1755 bool bRepeat = aVars.IsRepeat() && !bBreak;
1756 bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;
1757
1758 nTotalMargin =
1759 static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) +
1760 static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX);
1761
1762 nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;
1763
1764 // GetOutputArea gives justified rectangles
1765 GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
1766 *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
1767 bCellIsValue || bRepeat || bShrink, bBreak, false,
1768 aAreaParam );
1769
1770 aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin );
1771 if ( bShrink )
1772 {
1773 if ( aVars.GetOrient() != SvxCellOrientation::Standard )
1774 {
1775 // Only horizontal scaling is handled here.
1776 // DrawEdit is used to vertically scale 90 deg rotated text.
1777 bUseEditEngine = true;
1778 }
1779 else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal
1780 {
1781 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1782 tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin
1783
1784 if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats)
1785 {
1786 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
1787
1788 aVars.SetShrinkScale( nScale, nOldScript );
1789 tools::Long nNewSize = aVars.GetTextSize().Width();
1790
1791 sal_uInt16 nShrinkAgain = 0;
1792 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
1793 {
1794 // If the text is still too large, reduce the scale again by 10%, until it fits,
1795 // at most 7 times (it's less than 50% of the calculated scale then).
1796
1797 nScale = ( nScale * 9 ) / 10;
1798 aVars.SetShrinkScale( nScale, nOldScript );
1799 nNewSize = aVars.GetTextSize().Width();
1800 ++nShrinkAgain;
1801 }
1802 // If even at half the size the font still isn't rendered smaller,
1803 // fall back to normal clipping (showing ### for numbers).
1804 if ( nNewSize <= nAvailable )
1805 {
1806 // Reset relevant parameters.
1807 aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1808 aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1809 }
1810
1811 pOldPattern = nullptr;
1812 }
1813 }
1814 }
1815
1816 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
1817 {
1818 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
1819 tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin
1820 // When formatting for the printer, the text sizes don't always add up.
1821 // Round down (too few repetitions) rather than exceeding the cell size then:
1822 if ( pFmtDevice != mpRefDevice )
1823 ++nRepeatSize;
1824 if ( nRepeatSize > 0 )
1825 {
1826 tools::Long nRepeatCount = nAvailable / nRepeatSize;
1827 if ( nRepeatCount > 1 )
1828 {
1829 OUString aCellStr = aVars.GetString();
1830 OUStringBuffer aRepeated(aCellStr);
1831 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
1832 aRepeated.append(aCellStr);
1833 aVars.SetAutoText( aRepeated.makeStringAndClear() );
1834 }
1835 }
1836 }
1837
1838 // use edit engine if automatic line breaks are needed
1839 if ( bBreak )
1840 {
1841 if ( aVars.GetOrient() == SvxCellOrientation::Standard )
1842 bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
1843 else
1844 {
1845 tools::Long nHeight = aVars.GetTextSize().Height() +
1846 static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) +
1847 static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY);
1848 bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() );
1849 }
1850 }
1851 if (!bUseEditEngine)
1852 {
1853 bUseEditEngine =
1854 aVars.GetHorJust() == SvxCellHorJustify::Block &&
1855 aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute;
1856 }
1857 }
1858 if (bUseEditEngine)
1859 {
1860 // mark the cell in ScCellInfo to be drawn in DrawEdit:
1861 // Cells to the left are marked directly, cells to the
1862 // right are handled by the flag for nX2
1863 SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
1864 RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0];
1865 pMarkRowInfo->basicCellInfo(nMarkX).bEditEngine = true;
1866 bDoCell = false; // don't draw here
1867 }
1868 if ( bDoCell )
1869 {
1870 if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
1871 {
1872 if (mbShowFormulas)
1873 aVars.SetHashText();
1874 else
1875 // Adjust the decimals to fit the available column width.
1876 aVars.SetTextToWidthOrHash(aCell, aAreaParam.mnColWidth - nTotalMargin);
1877
1878 nNeededWidth = aVars.GetTextSize().Width() +
1879 static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) +
1880 static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX );
1881 if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
1882 {
1883 // Cell value is no longer clipped. Reset relevant parameters.
1884 aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
1885 aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
1886 }
1887
1888 // If the "###" replacement doesn't fit into the cells, no clip marks
1889 // are shown, as the "###" already denotes too little space.
1890 // The rectangles from the first GetOutputArea call remain valid.
1891 }
1892
1893 tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added
1894 tools::Long nJustPosY = aAreaParam.maAlignRect.Top();
1895 tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
1896 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight();
1897
1898 bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
1899 // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip
1900 bool bVClip = AdjustAreaParamClipRect(aAreaParam);
1901 bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
1902
1903 // check horizontal space
1904
1905 if ( !bOutside )
1906 {
1907 bool bRightAdjusted = false; // to correct text width calculation later
1908 switch (eOutHorJust)
1909 {
1910 case SvxCellHorJustify::Left:
1911 nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX );
1912 break;
1913 case SvxCellHorJustify::Right:
1914 nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
1915 static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX );
1916 bRightAdjusted = true;
1917 break;
1918 case SvxCellHorJustify::Center:
1919 nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
1920 static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) -
1921 static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2;
1922 break;
1923 default:
1924 {
1925 // added to avoid warnings
1926 }
1927 }
1928
1929 tools::Long nTestClipHeight = aVars.GetTextSize().Height();
1930 switch (aVars.GetVerJust())
1931 {
1932 case SvxCellVerJustify::Top:
1933 case SvxCellVerJustify::Block:
1934 {
1935 tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
1936 nJustPosY += nTop;
1937 nTestClipHeight += nTop;
1938 }
1939 break;
1940 case SvxCellVerJustify::Bottom:
1941 {
1942 tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
1943 nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
1944 nTestClipHeight += nBot;
1945 }
1946 break;
1947 case SvxCellVerJustify::Center:
1948 {
1949 tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
1950 tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
1951 nJustPosY += ( nOutHeight + nTop -
1952 aVars.GetTextSize().Height() - nBot ) / 2;
1953 nTestClipHeight += std::abs( nTop - nBot );
1954 }
1955 break;
1956 default:
1957 {
1958 // added to avoid warnings
1959 }
1960 }
1961
1962 if ( nTestClipHeight > nOutHeight )
1963 {
1964 // no vertical clipping when printing cells with optimal height,
1965 // except when font size is from conditional formatting.
1966 if ( eType != OUTTYPE_PRINTER ||
1967 ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) ||
1968 ( aVars.HasCondHeight() ) )
1969 bVClip = true;
1970 }
1971
1972 if ( bHClip || bVClip )
1973 {
1974 // only clip the affected dimension so that not all right-aligned
1975 // columns are cut off when performing a non-proportional resize
1976 if (!bHClip)
1977 {
1978 aAreaParam.maClipRect.SetLeft( nScrX );
1979 aAreaParam.maClipRect.SetRight( nScrX+nScrW );
1980 }
1981 if (!bVClip)
1982 {
1983 aAreaParam.maClipRect.SetTop( nScrY );
1984 aAreaParam.maClipRect.SetBottom( nScrY+nScrH );
1985 }
1986
1987 // aClipRect is not used after SetClipRegion/IntersectClipRegion,
1988 // so it can be modified here
1989 if (bPixelToLogic)
1990 aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect );
1991
1992 if (bMetaFile)
1993 {
1994 mpDev->Push();
1995 mpDev->IntersectClipRegion( aAreaParam.maClipRect );
1996 }
1997 else
1998 mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
1999 }
2000
2001 Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation
2002
2003 switch (aVars.GetOrient())
2004 {
2005 case SvxCellOrientation::Standard:
2006 nJustPosY += aVars.GetAscent();
2007 break;
2008 case SvxCellOrientation::TopBottom:
2009 nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
2010 break;
2011 case SvxCellOrientation::BottomUp:
2012 nJustPosY += aVars.GetTextSize().Height();
2013 nJustPosX += aVars.GetAscent();
2014 break;
2015 default:
2016 {
2017 // added to avoid warnings
2018 }
2019 }
2020
2021 // When clipping, the visible part is now completely defined by the alignment,
2022 // there's no more special handling to show the right part of RTL text.
2023
2024 Point aDrawTextPos( nJustPosX, nJustPosY );
2025 if ( bPixelToLogic )
2026 {
2027 // undo text width adjustment in pixels
2028 if (bRightAdjusted)
2029 aDrawTextPos.AdjustX(aVars.GetTextSize().Width() );
2030
2031 aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos );
2032
2033 // redo text width adjustment in logic units
2034 if (bRightAdjusted)
2035 aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) );
2036 }
2037
2038 // in Metafiles always use DrawTextArray to ensure that positions are
2039 // recorded (for non-proportional resize):
2040
2041 const OUString& aString = aVars.GetString();
2042 if (!aString.isEmpty())
2043 {
2044 // If the string is clipped, make it shorter for
2045 // better performance since drawing by HarfBuzz is
2046 // quite expensive especially for long string.
2047
2048 OUString aShort = aString;
2049
2050 // But never fiddle with numeric values.
2051 // (Which was the cause of tdf#86024).
2052 // The General automatic format output takes
2053 // care of this, or fixed width numbers either fit
2054 // or display as ###.
2055 if (!bCellIsValue)
2056 {
2057 double fVisibleRatio = 1.0;
2058 double fTextWidth = aVars.GetTextSize().Width();
2059 sal_Int32 nTextLen = aString.getLength();
2060 if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0)
2061 {
2062 fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth;
2063 if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2064 {
2065 // Only show the left-end segment.
2066 sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2067 aShort = aShort.copy(0, nShortLen);
2068 }
2069 }
2070 else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0)
2071 {
2072 fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth;
2073 if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
2074 {
2075 // Only show the right-end segment.
2076 sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
2077 aShort = aShort.copy(nTextLen-nShortLen);
2078
2079 // Adjust the text position after shortening of the string.
2080 double fShortWidth = aVars.GetFmtTextWidth(aShort);
2081 double fOffset = fTextWidth - fShortWidth;
2082 aDrawTextPos.Move(fOffset, 0);
2083 }
2084 }
2085 }
2086
2087 // if we are not painting, it means we are interested in
2088 // the area of the text that covers the specified cell
2089 if (!bPaint && rAddress.Col() == nX)
2090 {
2091 tools::Rectangle aRect;
2092 mpDev->GetTextBoundRect(aRect, aShort);
2093 aRect += aDrawTextPos;
2094 return aRect;
2095 }
2096
2097 if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY)
2098 {
2099 size_t nLen = aShort.getLength();
2100 if (aDX.size() < nLen)
2101 aDX.resize(nLen, 0);
2102
2103 pFmtDevice->GetTextArray(aShort, &aDX);
2104
2105 if ( !mpRefDevice->GetConnectMetaFile() ||
2106 mpRefDevice->GetOutDevType() == OUTDEV_PRINTER )
2107 {
2108 double fMul = GetStretch();
2109 for (size_t i = 0; i < nLen; ++i)
2110 aDX[i] = static_cast<sal_Int32>(aDX[i] / fMul + 0.5);
2111 }
2112
2113 if (bPaint)
2114 mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
2115 }
2116 else
2117 {
2118 if (bPaint)
2119 mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr,
2120 aVars.GetLayoutGlyphs(aShort));
2121 }
2122 }
2123
2124 if ( bHClip || bVClip )
2125 {
2126 if (bMetaFile)
2127 mpDev->Pop();
2128 else
2129 mpDev->SetClipRegion();
2130 }
2131
2132 // PDF: whole-cell hyperlink from formula?
2133 bool bHasURL = pPDFData && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell();
2134 if (bPaint && bHasURL)
2135 {
2136 tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
2137 lcl_DoHyperlinkResult(mpDev, aURLRect, aCell);
2138 }
2139 }
2140 }
2141 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2142 }
2143 }
2144 nPosY += pRowInfo[nArrY].nHeight;
2145 }
2146 if ( bProgress )
2148
2149 return tools::Rectangle();
2150}
2151
2152std::unique_ptr<ScFieldEditEngine> ScOutputData::CreateOutputEditEngine()
2153{
2154 std::unique_ptr<ScFieldEditEngine> pEngine(new ScFieldEditEngine(mpDoc, mpDoc->GetEnginePool()));
2155 pEngine->SetUpdateLayout( false );
2156 // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
2157 pEngine->SetRefDevice( pFmtDevice );
2158 EEControlBits nCtrl = pEngine->GetControlWord();
2159 if ( bShowSpellErrors )
2160 nCtrl |= EEControlBits::ONLINESPELLING;
2161 if ( eType == OUTTYPE_PRINTER )
2162 nCtrl &= ~EEControlBits::MARKFIELDS;
2163 else
2164 nCtrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
2166 nCtrl &= ~EEControlBits::FORMAT100; // use the actual MapMode
2167 pEngine->SetControlWord( nCtrl );
2168 mpDoc->ApplyAsianEditSettings( *pEngine );
2169 pEngine->EnableAutoColor( mbUseStyleColor );
2170 pEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( nTab ) );
2171 return pEngine;
2172}
2173
2174static void lcl_ClearEdit( EditEngine& rEngine ) // text and attributes
2175{
2176 rEngine.SetUpdateLayout( false );
2177
2178 rEngine.SetText(OUString());
2179 // do not keep any para-attributes
2180 const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
2181 if (rPara.Count())
2182 rEngine.SetParaAttribs( 0,
2183 SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
2184 rEngine.EnableSkipOutsideFormat(false);
2185}
2186
2187static bool lcl_SafeIsValue( ScRefCellValue& rCell )
2188{
2189 switch (rCell.getType())
2190 {
2191 case CELLTYPE_VALUE:
2192 return true;
2193 case CELLTYPE_FORMULA:
2194 {
2195 ScFormulaCell* pFCell = rCell.getFormula();
2196 if (pFCell->IsRunning() || pFCell->IsValue())
2197 return true;
2198 }
2199 break;
2200 default:
2201 {
2202 // added to avoid warnings
2203 }
2204 }
2205 return false;
2206}
2207
2208static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent )
2209{
2210 bool bUpdateMode = rEngine.SetUpdateLayout( false );
2211
2212 sal_Int32 nParCount = rEngine.GetParagraphCount();
2213 for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
2214 {
2215 std::vector<sal_Int32> aPortions;
2216 rEngine.GetPortions( nPar, aPortions );
2217
2218 sal_Int32 nStart = 0;
2219 for ( const sal_Int32 nEnd : aPortions )
2220 {
2221 ESelection aSel( nPar, nStart, nPar, nEnd );
2222 SfxItemSet aAttribs = rEngine.GetAttribs( aSel );
2223
2224 tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight();
2225 tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight();
2226 tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight();
2227
2228 nWestern = ( nWestern * nPercent ) / 100;
2229 nCJK = ( nCJK * nPercent ) / 100;
2230 nCTL = ( nCTL * nPercent ) / 100;
2231
2232 aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
2233 aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
2234 aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );
2235
2236 rEngine.QuickSetAttribs( aAttribs, aSel );
2237
2238 nStart = nEnd;
2239 }
2240 }
2241
2242 if ( bUpdateMode )
2243 rEngine.SetUpdateLayout( true );
2244}
2245
2246static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate )
2247{
2248 if ( bSwap )
2249 bWidth = !bWidth;
2250
2251 if ( nAttrRotate )
2252 {
2253 tools::Long nRealWidth = static_cast<tools::Long>(rEngine.CalcTextWidth());
2254 tools::Long nRealHeight = rEngine.GetTextHeight();
2255
2256 // assuming standard mode, otherwise width isn't used
2257
2258 double nRealOrient = toRadians(nAttrRotate); // 1/100th degrees
2259 double nAbsCos = fabs( cos( nRealOrient ) );
2260 double nAbsSin = fabs( sin( nRealOrient ) );
2261 if ( bWidth )
2262 return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
2263 else
2264 return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
2265 }
2266 else if ( bWidth )
2267 return static_cast<tools::Long>(rEngine.CalcTextWidth());
2268 else
2269 return rEngine.GetTextHeight();
2270}
2271
2273 tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
2274 bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
2275 tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
2276{
2277 if ( !bWidth )
2278 {
2279 // vertical
2280
2281 tools::Long nScaleSize = bPixelToLogic ?
2282 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2283
2284 // Don't scale if it fits already.
2285 // Allowing to extend into the margin, to avoid scaling at optimal height.
2286 if ( nScaleSize <= rAlignRect.GetHeight() )
2287 return;
2288
2289 bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp );
2290 tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
2291 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2292
2293 lcl_ScaleFonts( rEngine, nScale );
2294 rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2295 tools::Long nNewSize = bPixelToLogic ?
2296 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2297
2298 sal_uInt16 nShrinkAgain = 0;
2299 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2300 {
2301 // further reduce, like in DrawStrings
2302 lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
2303 rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
2304 nNewSize = bPixelToLogic ?
2305 mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
2306 ++nShrinkAgain;
2307 }
2308
2309 // sizes for further processing (alignment etc):
2310 rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate );
2311 tools::Long nPixelWidth = bPixelToLogic ?
2312 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2313 rNeededPixel = nPixelWidth + nLeftM + nRightM;
2314 }
2315 else if ( rLeftClip || rRightClip )
2316 {
2317 // horizontal
2318
2319 tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
2320 tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin
2321
2322 if ( nScaleSize <= nAvailable )
2323 return;
2324
2325 tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
2326
2327 lcl_ScaleFonts( rEngine, nScale );
2328 rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2329 tools::Long nNewSize = bPixelToLogic ?
2330 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2331
2332 sal_uInt16 nShrinkAgain = 0;
2333 while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
2334 {
2335 // further reduce, like in DrawStrings
2336 lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
2337 rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
2338 nNewSize = bPixelToLogic ?
2339 mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
2340 ++nShrinkAgain;
2341 }
2342 if ( nNewSize <= nAvailable )
2343 rLeftClip = rRightClip = false;
2344
2345 // sizes for further processing (alignment etc):
2346 rNeededPixel = nNewSize + nLeftM + nRightM;
2347 rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate );
2348 }
2349}
2350
2351ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue) :
2352 meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ),
2353 meHorJustContext( meHorJustAttr ),
2354 meHorJustResult( meHorJustAttr ),
2355 meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ),
2356 meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ),
2357 meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ),
2358 meOrient( pPattern->GetCellOrientation(pCondSet) ),
2359 mnArrY(0),
2360 mnX(0), mnCellX(0), mnCellY(0),
2361 mnPosX(0), mnPosY(0), mnInitPosX(0),
2362 mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ),
2363 mbCellIsValue(bCellIsValue),
2364 mbAsianVertical(false),
2365 mbPixelToLogic(false),
2366 mbHyphenatorSet(false),
2367 mpEngine(nullptr),
2368 mpPattern(pPattern),
2369 mpCondSet(pCondSet),
2370 mpPreviewFontSet(nullptr),
2371 mpOldPattern(nullptr),
2372 mpOldCondSet(nullptr),
2373 mpOldPreviewFontSet(nullptr),
2374 mpThisRowInfo(nullptr),
2375 mpMisspellRanges(nullptr)
2376{}
2377
2379 const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields)
2380{
2381 if (maCell.getType() == CELLTYPE_EDIT)
2382 {
2383 const EditTextObject* pData = maCell.getEditText();
2384 if (pData)
2385 {
2386 mpEngine->SetTextCurrentDefaults(*pData);
2387
2388 if ( mbBreak && !mbAsianVertical && pData->HasField() )
2389 {
2390 // Fields aren't wrapped, so clipping is enabled to prevent
2391 // a field from being drawn beyond the cell size
2392
2393 rWrapFields = true;
2394 }
2395 }
2396 else
2397 {
2398 OSL_FAIL("pData == 0");
2399 return false;
2400 }
2401 }
2402 else
2403 {
2404 sal_uInt32 nFormat = mpPattern->GetNumberFormat(
2405 pDoc->GetFormatTable(), mpCondSet );
2406 const Color* pColor;
2407 OUString aString = ScCellFormat::GetString( maCell,
2408 nFormat, &pColor,
2409 *pDoc->GetFormatTable(),
2410 *pDoc,
2411 bShowNullValues,
2412 bShowFormulas);
2413
2414 mpEngine->SetTextCurrentDefaults(aString);
2415 if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
2416 lcl_SetEditColor( *mpEngine, *pColor );
2417 }
2418
2419 if (mpMisspellRanges)
2420 mpEngine->SetAllMisspellRanges(*mpMisspellRanges);
2421
2422 return true;
2423}
2424
2426{
2427 // syntax highlighting mode is ignored here
2428 // StringDiffer doesn't look at hyphenate, language items
2429
2430 if (mpPattern == mpOldPattern && mpCondSet == mpOldCondSet && mpPreviewFontSet == mpOldPreviewFontSet )
2431 return;
2432
2433 Color nConfBackColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2434 bool bCellContrast = bUseStyleColor &&
2436
2437 auto pSet = std::make_unique<SfxItemSet>( mpEngine->GetEmptyItemSet() );
2438 mpPattern->FillEditItemSet( pSet.get(), mpCondSet );
2439 if ( mpPreviewFontSet )
2440 {
2441 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) )
2442 {
2443 // tdf#125054 adapt WhichID
2444 pSet->Put(*pItem, EE_CHAR_FONTINFO);
2445 }
2446 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
2447 {
2448 // tdf#125054 adapt WhichID
2449 pSet->Put(*pItem, EE_CHAR_FONTINFO_CJK);
2450 }
2451 if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
2452 {
2453 // tdf#125054 adapt WhichID
2454 pSet->Put(*pItem, EE_CHAR_FONTINFO_CTL);
2455 }
2456 }
2457 bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
2458 mpEngine->SetDefaults( std::move(pSet) );
2459 mpOldPattern = mpPattern;
2460 mpOldCondSet = mpCondSet;
2461 mpOldPreviewFontSet = mpPreviewFontSet;
2462
2463 EEControlBits nControl = mpEngine->GetControlWord();
2464 if (meOrient == SvxCellOrientation::Stacked)
2465 nControl |= EEControlBits::ONECHARPERLINE;
2466 else
2467 nControl &= ~EEControlBits::ONECHARPERLINE;
2468 mpEngine->SetControlWord( nControl );
2469
2470 if ( !mbHyphenatorSet && bParaHyphenate )
2471 {
2472 // set hyphenator the first time it is needed
2473 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
2474 mpEngine->SetHyphenator( xXHyphenator );
2475 mbHyphenatorSet = true;
2476 }
2477
2478 Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor();
2479 if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
2480 aBackCol = nConfBackColor;
2481 mpEngine->SetBackgroundColor( aBackCol );
2482}
2483
2484void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const
2485{
2486 const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet);
2487
2488 sal_uInt16 nIndent = 0;
2489 if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right)
2490 nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet);
2491
2492 rLeftM = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX));
2493 rTopM = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY));
2494 rRightM = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX));
2495 rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY));
2496 if(meHorJustAttr == SvxCellHorJustify::Right)
2497 {
2498 rLeftM = static_cast<tools::Long>((rMargin.GetLeftMargin() * nPPTX));
2499 rRightM = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX));
2500 }
2501}
2502
2504 Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const
2505{
2506 tools::Long nTopM, nLeftM, nBottomM, nRightM;
2507 calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY);
2508
2509 if (isVerticallyOriented())
2510 {
2511 rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM );
2512 rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM );
2513 }
2514 else
2515 {
2516 rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM );
2517 rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2518 }
2519
2520 if (mbAsianVertical)
2521 {
2522 rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
2523 // Subtract some extra value from the height or else the text would go
2524 // outside the cell area. The value of 5 is arbitrary, and is based
2525 // entirely on heuristics.
2526 rPaperSize.AdjustHeight( -5 );
2527 }
2528}
2529
2531{
2532 tools::Long nEngineWidth = 0;
2533 if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical)
2534 nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth());
2535
2536 tools::Long nEngineHeight = pEngine->GetTextHeight();
2537
2538 if (isVerticallyOriented())
2539 std::swap( nEngineWidth, nEngineHeight );
2540
2541 if (meOrient == SvxCellOrientation::Stacked)
2542 nEngineWidth = nEngineWidth * 11 / 10;
2543
2544 rWidth = nEngineWidth;
2545 rHeight = nEngineHeight;
2546}
2547
2549{
2550 return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical);
2551}
2552
2554{
2555 if (maCell.getType() != CELLTYPE_FORMULA)
2556 return false;
2557
2558 return maCell.getFormula()->IsHyperLinkCell();
2559}
2560
2562{
2563 return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp);
2564}
2565
2567 Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice)
2568{
2569 OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!");
2570
2571 if (mbPixelToLogic)
2572 rLogicStart = pRefDevice->PixelToLogic(rLogicStart);
2573
2574 if (!mbBreak)
2575 return;
2576
2577 // vertical adjustment is within the EditEngine
2578 if (mbPixelToLogic)
2579 rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
2580 else
2581 rLogicStart.AdjustY(nTopM );
2582
2583 switch (meHorJustResult)
2584 {
2585 case SvxCellHorJustify::Center:
2586 rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 );
2587 break;
2588 case SvxCellHorJustify::Right:
2589 rLogicStart.AdjustX(nCellWidth - nEngineWidth );
2590 break;
2591 default:
2592 ; // do nothing
2593 }
2594}
2595
2597{
2598 if (isVerticallyOriented() || mbAsianVertical)
2599 {
2600 SvxAdjust eSvxAdjust = SvxAdjust::Left;
2601 switch (meVerJust)
2602 {
2603 case SvxCellVerJustify::Top:
2604 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2605 SvxAdjust::Left : SvxAdjust::Right;
2606 break;
2607 case SvxCellVerJustify::Center:
2608 eSvxAdjust = SvxAdjust::Center;
2609 break;
2610 case SvxCellVerJustify::Bottom:
2611 case SvxCellVerJustify::Standard:
2612 eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
2613 SvxAdjust::Right : SvxAdjust::Left;
2614 break;
2615 case SvxCellVerJustify::Block:
2616 eSvxAdjust = SvxAdjust::Block;
2617 break;
2618 }
2619
2620 mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2621 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2622
2623 if (meHorJustResult == SvxCellHorJustify::Block)
2624 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2625 }
2626 else
2627 {
2628 // horizontal alignment now may depend on cell content
2629 // (for values with number formats with mixed script types)
2630 // -> always set adjustment
2631
2632 SvxAdjust eSvxAdjust = SvxAdjust::Left;
2633 if (meOrient == SvxCellOrientation::Stacked)
2634 eSvxAdjust = SvxAdjust::Center;
2635 else if (mbBreak)
2636 {
2637 if (meOrient == SvxCellOrientation::Standard)
2638 switch (meHorJustResult)
2639 {
2640 case SvxCellHorJustify::Repeat: // repeat is not yet implemented
2641 case SvxCellHorJustify::Standard:
2642 SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()");
2643 [[fallthrough]];
2644 case SvxCellHorJustify::Left:
2645 eSvxAdjust = SvxAdjust::Left;
2646 break;
2647 case SvxCellHorJustify::Center:
2648 eSvxAdjust = SvxAdjust::Center;
2649 break;
2650 case SvxCellHorJustify::Right:
2651 eSvxAdjust = SvxAdjust::Right;
2652 break;
2653 case SvxCellHorJustify::Block:
2654 eSvxAdjust = SvxAdjust::Block;
2655 break;
2656 }
2657 else
2658 switch (meVerJust)
2659 {
2660 case SvxCellVerJustify::Top:
2661 eSvxAdjust = SvxAdjust::Right;
2662 break;
2663 case SvxCellVerJustify::Center:
2664 eSvxAdjust = SvxAdjust::Center;
2665 break;
2666 case SvxCellVerJustify::Bottom:
2667 case SvxCellVerJustify::Standard:
2668 eSvxAdjust = SvxAdjust::Left;
2669 break;
2670 case SvxCellVerJustify::Block:
2671 eSvxAdjust = SvxAdjust::Block;
2672 break;
2673 }
2674 }
2675
2676 mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
2677
2678 if (mbAsianVertical)
2679 {
2680 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
2681 if (meHorJustResult == SvxCellHorJustify::Block)
2682 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2683 }
2684 else
2685 {
2686 mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) );
2687 if (meVerJust == SvxCellVerJustify::Block)
2688 mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
2689 }
2690 }
2691
2692 mpEngine->SetVertical(mbAsianVertical);
2693 if (maCell.getType() == CELLTYPE_EDIT)
2694 {
2695 // We need to synchronize the vertical mode in the EditTextObject
2696 // instance too. No idea why we keep this state in two separate
2697 // instances.
2698 const EditTextObject* pData = maCell.getEditText();
2699 if (pData)
2700 const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical);
2701 }
2702}
2703
2705{
2706 if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center)
2707 {
2708 SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ?
2709 SvxAdjust::Center : SvxAdjust::Right;
2710
2711 const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false);
2712 pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) );
2713 pEngine->SetUpdateLayout(bPrevUpdateLayout);
2714 return true;
2715 }
2716 return false;
2717}
2718
2720{
2721 // PDF: whole-cell hyperlink from formula?
2722 vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
2723 bool bHasURL = pPDFData && isHyperlinkCell();
2724 if (!bHasURL)
2725 return;
2726
2727 tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth());
2728 tools::Long nURLHeight = mpEngine->GetTextHeight();
2729 if (mbBreak)
2730 {
2731 Size aPaper = mpEngine->GetPaperSize();
2732 if ( mbAsianVertical )
2733 nURLHeight = aPaper.Height();
2734 else
2735 nURLWidth = aPaper.Width();
2736 }
2737 if (isVerticallyOriented())
2738 std::swap( nURLWidth, nURLHeight );
2739 else if (mbAsianVertical)
2740 aURLStart.AdjustX( -nURLWidth );
2741
2742 tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
2743 lcl_DoHyperlinkResult(pDev, aURLRect, maCell);
2744}
2745
2746// Returns true if the rect is clipped vertically
2748{
2749 if( rAreaParam.maClipRect.Left() < nScrX )
2750 {
2751 rAreaParam.maClipRect.SetLeft( nScrX );
2752 rAreaParam.mbLeftClip = true;
2753 }
2754 if( rAreaParam.maClipRect.Right() > nScrX + nScrW )
2755 {
2756 rAreaParam.maClipRect.SetRight( nScrX + nScrW );
2757 rAreaParam.mbRightClip = true;
2758 }
2759
2760 bool bVClip = false;
2761
2762 if( rAreaParam.maClipRect.Top() < nScrY )
2763 {
2764 rAreaParam.maClipRect.SetTop( nScrY );
2765 bVClip = true;
2766 }
2767 if( rAreaParam.maClipRect.Bottom() > nScrY + nScrH )
2768 {
2769 rAreaParam.maClipRect.SetBottom( nScrY + nScrH );
2770 bVClip = true;
2771 }
2772 return bVClip;
2773}
2774
2775// Doesn't handle clip marks - should be handled in advance using GetOutputArea
2777{
2778public:
2779 ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip,
2780 const VclPtr<OutputDevice>& pDev, bool bMetaFile )
2781 :mbMetaFile(bMetaFile)
2782 {
2783 if (!(bClip || bSimClip))
2784 return;
2785
2786 maRect = rRect;
2787 if (bClip) // for bSimClip only initialize aClipRect
2788 {
2789 mpDev.reset(pDev);
2790 if (mbMetaFile)
2791 {
2792 mpDev->Push();
2793 mpDev->IntersectClipRegion(maRect);
2794 }
2795 else
2796 mpDev->SetClipRegion(vcl::Region(maRect));
2797 }
2798 }
2799
2800 ~ClearableClipRegion() COVERITY_NOEXCEPT_FALSE
2801 {
2802 // Pop() or SetClipRegion() must only be called in case bClip was true
2803 // in the ctor, and only then mpDev is set.
2804 if (mpDev)
2805 {
2806 if (mbMetaFile)
2807 mpDev->Pop();
2808 else
2809 mpDev->SetClipRegion();
2810 }
2811 }
2812
2813 const tools::Rectangle& getRect() const { return maRect; }
2814
2815private:
2819};
2820
2821// Returns needed width in current units; sets rNeededPixel to needed width in pixels
2823 tools::Long& rNeededPixel, tools::Long nAddWidthPixels )
2824{
2825 rParam.mpEngine->SetTextCurrentDefaults( rSetString );
2826 tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() );
2827 if ( rParam.mbPixelToLogic )
2828 rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width();
2829 else
2830 rNeededPixel = nEngineWidth;
2831
2832 rNeededPixel += nAddWidthPixels;
2833
2834 return nEngineWidth;
2835}
2836
2838{
2839 OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
2840 OSL_ASSERT(!rParam.mbAsianVertical);
2841
2842 Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
2843
2844 bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
2845 bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
2846 Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
2847
2848 if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat )
2849 {
2850 // ignore orientation/rotation if "repeat" is active
2851 rParam.meOrient = SvxCellOrientation::Standard;
2852 nAttrRotate = 0_deg100;
2853
2854 // #i31843# "repeat" with "line breaks" is treated as default alignment
2855 // (but rotation is still disabled).
2856 // Default again leads to context dependent alignment instead of
2857 // SvxCellHorJustify::Standard.
2858 if ( rParam.mbBreak )
2859 rParam.meHorJustResult = rParam.meHorJustContext;
2860 }
2861
2862 if (nAttrRotate)
2863 {
2866 return; // rotated is outputted separately
2867 }
2868
2869 SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
2870
2873 tools::Long nTopM, nLeftM, nBottomM, nRightM;
2874 rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
2875
2876 SCCOL nXForPos = rParam.mnX;
2877 if ( nXForPos < nX1 )
2878 {
2879 nXForPos = nX1;
2880 rParam.mnPosX = rParam.mnInitPosX;
2881 }
2882 SCSIZE nArrYForPos = rParam.mnArrY;
2883 if ( nArrYForPos < 1 )
2884 {
2885 nArrYForPos = 1;
2886 rParam.mnPosY = nScrY;
2887 }
2888
2889 OutputAreaParam aAreaParam;
2890
2891 // Initial page size - large for normal text, cell size for automatic line breaks
2892
2893 Size aPaperSize( 1000000, 1000000 );
2894 if (rParam.mbBreak)
2895 {
2896 // call GetOutputArea with nNeeded=0, to get only the cell width
2897
2899 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
2900 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
2901 rParam.mbCellIsValue, true, false, aAreaParam );
2902
2904 rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
2905 }
2906 if (rParam.mbPixelToLogic)
2907 {
2908 Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
2909 if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
2910 {
2911 // #i85342# screen display and formatting for printer,
2912 // use same GetEditArea call as in ScViewData::SetEditEngine
2913
2914 Fraction aFract(1,1);
2915 tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
2916 HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
2917 aLogicSize.setWidth( aUtilRect.GetWidth() );
2918 }
2919 rParam.mpEngine->SetPaperSize(aLogicSize);
2920 }
2921 else
2922 rParam.mpEngine->SetPaperSize(aPaperSize);
2923
2924 // Fill the EditEngine (cell attributes and text)
2925
2926 // default alignment for asian vertical mode is top-right
2927 if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard )
2928 rParam.meVerJust = SvxCellVerJustify::Top;
2929
2931 rParam.setAlignmentToEngine();
2932 // Don't format unnecessary parts if the text will be drawn from top (Standard will
2933 // act that way if text doesn't fit, see below).
2934 rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top
2935 || rParam.meVerJust==SvxCellVerJustify::Standard);
2936
2937 // Read content from cell
2938
2939 bool bWrapFields = false;
2941 // Failed to read cell content. Bail out.
2942 return;
2943
2944 if ( mbSyntaxMode )
2945 SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell);
2946 else if ( mbUseStyleColor && mbForceAutoColor )
2947 lcl_SetEditColor( *rParam.mpEngine, COL_AUTO );
2948
2949 rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
2950
2951 // Get final output area using the calculated width
2952
2953 tools::Long nEngineWidth, nEngineHeight;
2954 rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
2955
2956 tools::Long nNeededPixel = nEngineWidth;
2957 if (rParam.mbPixelToLogic)
2958 nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
2959 nNeededPixel += nLeftM + nRightM;
2960
2961 if (!rParam.mbBreak || bShrink)
2962 {
2963 // for break, the first GetOutputArea call is sufficient
2964 GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
2965 *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
2966 rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
2967
2968 if ( bShrink )
2969 {
2970 ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
2971 nLeftM, nTopM, nRightM, nBottomM, true,
2972 rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
2973 nEngineWidth, nEngineHeight, nNeededPixel,
2974 aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
2975 }
2976 if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
2977 {
2978 // First check if twice the space for the formatted text is available
2979 // (otherwise just keep it unchanged).
2980
2981 tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
2982 tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
2983 if ( nAvailable >= 2 * nFormatted )
2984 {
2985 // "repeat" is handled with unformatted text (for performance reasons)
2986 OUString aCellStr = rParam.mpEngine->GetText();
2987
2988 tools::Long nRepeatSize = 0;
2989 SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
2990 if ( pFmtDevice != mpRefDevice )
2991 ++nRepeatSize;
2992 if ( nRepeatSize > 0 )
2993 {
2994 tools::Long nRepeatCount = nAvailable / nRepeatSize;
2995 if ( nRepeatCount > 1 )
2996 {
2997 OUStringBuffer aRepeated(aCellStr);
2998 for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
2999 aRepeated.append(aCellStr);
3000
3001 SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
3002 nNeededPixel, (nLeftM + nRightM ) );
3003
3004 nEngineHeight = rParam.mpEngine->GetTextHeight();
3005 }
3006 }
3007 }
3008 }
3009
3010
3011 if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
3012 {
3013 SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
3014
3015 // No clip marks if "###" doesn't fit (same as in DrawStrings)
3016 }
3017
3018 if (eOutHorJust != SvxCellHorJustify::Left)
3019 {
3020 aPaperSize.setWidth( nNeededPixel + 1 );
3021 if (rParam.mbPixelToLogic)
3022 rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
3023 else
3024 rParam.mpEngine->SetPaperSize(aPaperSize);
3025 }
3026 }
3027
3028 tools::Long nStartX = aAreaParam.maAlignRect.Left();
3029 tools::Long nStartY = aAreaParam.maAlignRect.Top();
3031 tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
3032 tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
3033
3034 if (rParam.mbBreak)
3035 {
3036 // text with automatic breaks is aligned only within the
3037 // edit engine's paper size, the output of the whole area
3038 // is always left-aligned
3039
3040 nStartX += nLeftM;
3041 }
3042 else
3043 {
3044 if ( eOutHorJust == SvxCellHorJustify::Right )
3045 nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
3046 else if ( eOutHorJust == SvxCellHorJustify::Center )
3047 nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
3048 else
3049 nStartX += nLeftM;
3050 }
3051
3052 bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
3053 if (bOutside)
3054 return;
3055
3056 // Also take fields in a cell with automatic breaks into account: clip to cell width
3057 bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
3058 bool bSimClip = false;
3059
3060 Size aCellSize; // output area, excluding margins, in logical units
3061 if (rParam.mbPixelToLogic)
3062 aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
3063 else
3064 aCellSize = Size( nOutWidth, nOutHeight );
3065
3066 if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
3067 {
3068 const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
3069 bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
3070
3071 // Don't clip for text height when printing rows with optimal height,
3072 // except when font size is from conditional formatting.
3074 if ( eType != OUTTYPE_PRINTER ||
3075 ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
3076 ( rParam.mpCondSet && SfxItemState::SET ==
3078 bClip = true;
3079 else
3080 bSimClip = true;
3081
3082 // Show clip marks if height is at least 5pt too small and
3083 // there are several lines of text.
3084 // Not for asian vertical text, because that would interfere
3085 // with the default right position of the text.
3086 // Only with automatic line breaks, to avoid having to find
3087 // the cells with the horizontal end of the text again.
3088 if ( nEngineHeight - aCellSize.Height() > 100 &&
3089 rParam.mbBreak && bMarkClipped &&
3090 ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
3091 {
3092 ScCellInfo* pClipMarkCell = nullptr;
3093 if ( bMerged )
3094 {
3095 // anywhere in the merged area...
3096 SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
3097 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3098 }
3099 else
3100 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3101
3102 pClipMarkCell->nClipMark |= ScClipMark::Right;
3103 bAnyClipped = true;
3104
3105 tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
3106 if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
3107 aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
3108
3109 // Standard is normally treated as Bottom, but if text height is clipped, then
3110 // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat().
3111 if (rParam.meVerJust==SvxCellVerJustify::Standard)
3112 rParam.meVerJust=SvxCellVerJustify::Top;
3113 }
3114 }
3115
3116 Point aURLStart;
3117
3118 { // Clip marks are already handled in GetOutputArea
3119 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
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
3189void 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 return;
3201
3202 ScCellInfo* pClipMarkCell = nullptr;
3203 if (bMerged)
3204 {
3205 // anywhere in the merged area...
3206 SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
3207 pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
3208 }
3209 else
3210 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
3211
3212 bAnyClipped = true;
3213 bVertical = true;
3214 const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
3215 if (bTop)
3216 {
3217 pClipMarkCell->nClipMark |= ScClipMark::Top;
3218 if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3219 aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
3220 }
3221 else
3222 {
3223 pClipMarkCell->nClipMark |= ScClipMark::Bottom;
3224 if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
3225 aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
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->SetUpdateLayout( 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->SetUpdateLayout( 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->SetUpdateLayout( 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();
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 ScCellInfo* 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].cellInfo(nClipX);
3944 }
3945 else
3946 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
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
3960 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
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->SetUpdateLayout( 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();
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 ScCellInfo* 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].cellInfo(nClipX);
4231 }
4232 else
4233 pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
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
4247 ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
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
4290void 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 if (pThisRowInfo->basicCellInfo(nX).bEditEngine)
4328 {
4329 SCROW nY = pThisRowInfo->nRowNo;
4330
4331 SCCOL nCellX = nX; // position where the cell really starts
4332 SCROW nCellY = nY;
4333 bool bDoCell = false;
4334
4335 tools::Long nPosY = nRowPosY;
4336 if ( nArrY == 0 )
4337 {
4338 nPosY = nScrY;
4339 nY = pRowInfo[1].nRowNo;
4340 SCCOL nOverX; // start of the merged cells
4341 SCROW nOverY;
4342 if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, true ))
4343 {
4344 nCellX = nOverX;
4345 nCellY = nOverY;
4346 bDoCell = true;
4347 }
4348 }
4349 else if ( nX == nX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() )
4350 {
4351 // Rest of a long text further to the right?
4352
4353 SCCOL nTempX=nX;
4354 while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
4355 ++nTempX;
4356
4357 if ( nTempX > nX &&
4358 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
4360 {
4361 nCellX = nTempX;
4362 bDoCell = true;
4363 }
4364 }
4365 else
4366 {
4367 bDoCell = true;
4368 }
4369
4370 if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
4371 bDoCell = false;
4372
4373 const ScPatternAttr* pPattern = nullptr;
4374 const SfxItemSet* pCondSet = nullptr;
4375 if (bDoCell)
4376 {
4377 if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
4378 !mpDoc->ColHidden(nCellX, nTab) )
4379 {
4380 ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
4381 pPattern = rCellInfo.pPatternAttr;
4382 pCondSet = rCellInfo.pConditionSet;
4383 aCell = rCellInfo.maCell;
4384 }
4385 else // get from document
4386 {
4387 pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
4388 pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
4389 GetVisibleCell( nCellX, nCellY, nTab, aCell );
4390 }
4391 if (aCell.isEmpty())
4392 bDoCell = false;
4393 }
4394 if (bDoCell)
4395 {
4396 if ( mpDoc->GetPreviewCellStyle() )
4397 {
4398 if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
4399 {
4400 pPreviewPattr.reset( new ScPatternAttr(*pPattern) );
4401 pPreviewPattr->SetStyleSheet(pPreviewStyle);
4402 pPattern = pPreviewPattr.get();
4403 }
4404 }
4405 SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab );
4406 if (!pEngine)
4407 pEngine = CreateOutputEditEngine();
4408 else
4409 lcl_ClearEdit( *pEngine ); // also calls SetUpdateMode(sal_False)
4410
4411 // fdo#32530: Check if the first character is RTL.
4412 OUString aStr = mpDoc->GetString(nCellX, nCellY, nTab);
4413
4414 DrawEditParam aParam(pPattern, pCondSet, lcl_SafeIsValue(aCell));
4415 const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
4417 aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText);
4418 aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ?
4419 SvxCellHorJustify::Block : aParam.meHorJustContext;
4420 aParam.mbPixelToLogic = bPixelToLogic;
4421 aParam.mbHyphenatorSet = bHyphenatorSet;
4422 aParam.mpEngine = pEngine.get();
4423 aParam.maCell = aCell;
4424 aParam.mnArrY = nArrY;
4425 aParam.mnX = nX;
4426 aParam.mnCellX = nCellX;
4427 aParam.mnCellY = nCellY;
4428 aParam.mnPosX = nPosX;
4429 aParam.mnPosY = nPosY;
4430 aParam.mnInitPosX = nInitPosX;
4431 aParam.mpPreviewFontSet = pPreviewFontSet;
4432 aParam.mpOldPattern = pOldPattern;
4433 aParam.mpOldCondSet = pOldCondSet;
4434 aParam.mpOldPreviewFontSet = pOldPreviewFontSet;
4435 aParam.mpThisRowInfo = pThisRowInfo;
4436 if (mpSpellCheckCxt)
4437 aParam.mpMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY);
4438
4439 if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat)
4440 {
4441 // ignore orientation/rotation if "repeat" is active
4442 aParam.meOrient = SvxCellOrientation::Standard;
4443 }
4444 switch (aParam.meOrient)
4445 {
4446 case SvxCellOrientation::BottomUp:
4447 DrawEditBottomTop(aParam);
4448 break;
4449 case SvxCellOrientation::TopBottom:
4450 DrawEditTopBottom(aParam);
4451 break;
4452 case SvxCellOrientation::Stacked:
4453 // this can be vertically stacked or asian vertical.
4454 DrawEditStacked(aParam);
4455 break;
4456 default:
4457 DrawEditStandard(aParam);
4458 }
4459
4460 // Retrieve parameters for next iteration.
4461 pOldPattern = aParam.mpOldPattern;
4462 pOldCondSet = aParam.mpOldCondSet;
4463 pOldPreviewFontSet = aParam.mpOldPreviewFontSet;
4464 bHyphenatorSet = aParam.mbHyphenatorSet;
4465 }
4466 }
4467 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
4468 }
4469 }
4470 nRowPosY += pRowInfo[nArrY].nHeight;
4471 }
4472
4473 pEngine.reset();
4474
4476 {
4477 DrawRotated(bPixelToLogic);
4478 }
4479}
4480
4481void ScOutputData::DrawRotated(bool bPixelToLogic)
4482{
4484 SCCOL nRotMax = nX2;
4485 for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
4486 if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
4487 nRotMax = pRowInfo[nRotY].nRotMaxCol;
4488
4489 ScModule* pScMod = SC_MOD();
4490 Color nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
4491 bool bCellContrast = mbUseStyleColor &&
4493
4494 std::unique_ptr<ScFieldEditEngine> pEngine;
4495 bool bHyphenatorSet = false;
4496 const ScPatternAttr* pPattern;
4497 const SfxItemSet* pCondSet;
4498 const ScPatternAttr* pOldPattern = nullptr;
4499 const SfxItemSet* pOldCondSet = nullptr;
4500 ScRefCellValue aCell;
4501
4502 tools::Long nInitPosX = nScrX;
4503 if ( bLayoutRTL )
4504 {
4505 nInitPosX += nMirrorW - 1;
4506 }
4507 tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
4508
4509 tools::Long nRowPosY = nScrY;
4510 for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 for the rest of the merged
4511 {
4512 RowInfo* pThisRowInfo = &pRowInfo[nArrY];
4513 tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight);
4514 if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
4515
4516 if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
4517 {
4518 tools::Long nPosX = 0;
4519 for (SCCOL nX=0; nX<=nRotMax; nX++)
4520 {
4521 if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
4522
4523 const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
4524 if ( pInfo->