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