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