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