Coverage Report

Created: 2026-04-09 11:41

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