Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/outdev/textline.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 <sal/types.h>
21
#include <basegfx/matrix/b2dhommatrixtools.hxx>
22
#include <basegfx/polygon/WaveLine.hxx>
23
#include <tools/helpers.hxx>
24
#include <o3tl/hash_combine.hxx>
25
#include <o3tl/lru_map.hxx>
26
#include <comphelper/configuration.hxx>
27
#include <tools/lazydelete.hxx>
28
#include <vcl/dropcache.hxx>
29
#include <vcl/metaact.hxx>
30
#include <vcl/settings.hxx>
31
#include <vcl/virdev.hxx>
32
#include <vcl/skia/SkiaHelper.hxx>
33
34
#include <drawmode.hxx>
35
#include <salgdi.hxx>
36
#include <impglyphitem.hxx>
37
38
#include <cassert>
39
40
247k
#define UNDERLINE_LAST      LINESTYLE_BOLDWAVE
41
148k
#define STRIKEOUT_LAST      STRIKEOUT_X
42
43
namespace {
44
    struct WavyLineCache final : public CacheOwner
45
    {
46
        WavyLineCache()
47
#if defined __cpp_lib_memory_resource
48
0
            : m_aItems(10, &GetMemoryResource())
49
#else
50
            : m_aItems(10)
51
#endif
52
0
        {
53
0
        }
54
55
        bool find( Color aLineColor, size_t nLineWidth, size_t nWaveHeight, size_t nWordWidth, Bitmap& rOutput )
56
0
        {
57
0
            Key aKey = { nWaveHeight, sal_uInt32(aLineColor) };
58
0
            auto item = m_aItems.find( aKey );
59
0
            if ( item == m_aItems.end() )
60
0
                return false;
61
            // needs update
62
0
            if ( item->second.m_aLineWidth != nLineWidth || item->second.m_aWordWidth < nWordWidth )
63
0
            {
64
0
                return false;
65
0
            }
66
0
            rOutput = item->second.m_Bitmap;
67
0
            return true;
68
0
        }
69
70
        void insert( const Bitmap& aBitmap, const Color& aLineColor, const size_t nLineWidth, const size_t nWaveHeight, const size_t nWordWidth, Bitmap& rOutput )
71
0
        {
72
0
            Key aKey = { nWaveHeight, sal_uInt32(aLineColor) };
73
0
            m_aItems.insert( std::pair< Key, WavyLineCacheItem>( aKey, { nLineWidth, nWordWidth, aBitmap } ) );
74
0
            rOutput = aBitmap;
75
0
        }
76
77
0
        virtual OUString getCacheName() const override { return "WavyLineCache"; }
78
79
        virtual bool dropCaches() override
80
0
        {
81
0
            m_aItems.clear();
82
0
            return true;
83
0
        }
84
85
        virtual void dumpState(rtl::OStringBuffer& rState) override
86
0
        {
87
0
            rState.append("\nWavyLineCache:\t");
88
0
            rState.append(static_cast<sal_Int32>(m_aItems.size()));
89
0
        }
90
91
        private:
92
        struct WavyLineCacheItem
93
        {
94
            size_t m_aLineWidth;
95
            size_t m_aWordWidth;
96
            Bitmap m_Bitmap;
97
        };
98
99
        struct Key
100
        {
101
            size_t m_aFirst;
102
            size_t m_aSecond;
103
            bool operator ==( const Key& rOther ) const
104
0
            {
105
0
                return ( m_aFirst == rOther.m_aFirst && m_aSecond == rOther.m_aSecond );
106
0
            }
107
        };
108
109
        struct Hash
110
        {
111
            size_t operator() ( const Key& rKey ) const
112
0
            {
113
0
                size_t aSeed = 0;
114
0
                o3tl::hash_combine(aSeed, rKey.m_aFirst);
115
0
                o3tl::hash_combine(aSeed, rKey.m_aSecond);
116
0
                return aSeed;
117
0
            }
118
        };
119
120
        o3tl::lru_map< Key, WavyLineCacheItem, Hash > m_aItems;
121
    };
122
}
123
124
void OutputDevice::ImplInitTextLineSize()
125
0
{
126
0
    mpFontInstance->mxFontMetric->ImplInitTextLineSize( this );
127
0
}
128
129
void OutputDevice::ImplInitAboveTextLineSize()
130
0
{
131
0
    mpFontInstance->mxFontMetric->ImplInitAboveTextLineSize( this );
132
0
}
133
134
void OutputDevice::ImplDrawWavePixel( tools::Long nOriginX, tools::Long nOriginY,
135
                                      tools::Long nCurX, tools::Long nCurY,
136
                                      tools::Long nWidth,
137
                                      Degree10 nOrientation,
138
                                      SalGraphics* pGraphics,
139
                                      const OutputDevice& rOutDev,
140
                                      tools::Long nPixWidth, tools::Long nPixHeight )
141
1.24M
{
142
1.24M
    if (nOrientation)
143
779k
    {
144
779k
        Point aPoint( nOriginX, nOriginY );
145
779k
        aPoint.RotateAround( nCurX, nCurY, nOrientation );
146
779k
    }
147
148
1.24M
    if (shouldDrawWavePixelAsRect(nWidth))
149
175k
    {
150
175k
        pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, rOutDev );
151
175k
    }
152
1.07M
    else
153
1.07M
    {
154
1.07M
        pGraphics->DrawPixel( nCurX, nCurY, rOutDev );
155
1.07M
    }
156
1.24M
}
157
158
bool OutputDevice::shouldDrawWavePixelAsRect(tools::Long nLineWidth) const
159
1.24M
{
160
1.24M
    if (nLineWidth > 1)
161
175k
        return true;
162
163
1.07M
    return false;
164
1.24M
}
165
166
void OutputDevice::SetWaveLineColors(Color const& rColor, tools::Long nLineWidth)
167
802
{
168
    // On printers that output pixel via DrawRect()
169
802
    if (nLineWidth > 1)
170
118
    {
171
118
        if (mbLineColor || mbInitLineColor)
172
118
        {
173
118
            mpGraphics->SetLineColor();
174
118
            mbInitLineColor = true;
175
118
        }
176
177
118
        mpGraphics->SetFillColor( rColor );
178
118
        mbInitFillColor = true;
179
118
    }
180
684
    else
181
684
    {
182
684
        mpGraphics->SetLineColor( rColor );
183
684
        mbInitLineColor = true;
184
684
    }
185
802
}
186
187
Size OutputDevice::GetWaveLineSize(tools::Long nLineWidth) const
188
802
{
189
802
    if (nLineWidth > 1)
190
118
        return Size(nLineWidth, ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY);
191
192
684
    return Size(1, 1);
193
802
}
194
195
void OutputDevice::ImplDrawWaveLine( tools::Long nBaseX, tools::Long nBaseY,
196
                                     tools::Long nDistX, tools::Long nDistY,
197
                                     tools::Long nWidth, tools::Long nHeight,
198
                                     tools::Long nLineWidth, Degree10 nOrientation,
199
                                     const Color& rColor )
200
802
{
201
802
    if ( !nHeight )
202
0
        return;
203
204
802
    tools::Long nStartX = nBaseX + nDistX;
205
802
    tools::Long nStartY = nBaseY + nDistY;
206
207
    // If the height is 1 pixel, it's enough output a line
208
802
    if ( (nLineWidth == 1) && (nHeight == 1) )
209
0
    {
210
0
        mpGraphics->SetLineColor( rColor );
211
0
        mbInitLineColor = true;
212
213
0
        tools::Long nEndX = nStartX+nWidth;
214
0
        tools::Long nEndY = nStartY;
215
0
        if ( nOrientation )
216
0
        {
217
0
            Point aOriginPt( nBaseX, nBaseY );
218
0
            aOriginPt.RotateAround( nStartX, nStartY, nOrientation );
219
0
            aOriginPt.RotateAround( nEndX, nEndY, nOrientation );
220
0
        }
221
0
        mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, *this );
222
0
    }
223
802
    else
224
802
    {
225
802
        tools::Long    nCurX = nStartX;
226
802
        tools::Long    nCurY = nStartY;
227
802
        tools::Long    nDiffX = 2;
228
802
        tools::Long    nDiffY = nHeight-1;
229
802
        tools::Long    nCount = nWidth;
230
802
        tools::Long    nOffY = -1;
231
232
802
        SetWaveLineColors(rColor, nLineWidth);
233
802
        Size aSize(GetWaveLineSize(nLineWidth));
234
235
802
        tools::Long nPixWidth = aSize.Width();
236
802
        tools::Long nPixHeight = aSize.Height();
237
238
802
        if ( !nDiffY )
239
70
        {
240
88.0k
            while ( nWidth )
241
87.9k
            {
242
87.9k
                ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
243
87.9k
                                   mpGraphics, *this,
244
87.9k
                                   nPixWidth, nPixHeight );
245
87.9k
                nCurX++;
246
87.9k
                nWidth--;
247
87.9k
            }
248
70
        }
249
732
        else
250
732
        {
251
732
            nCurY += nDiffY;
252
146k
            for (tools::Long nFreq = nCount / (nDiffX+nDiffY); nFreq > 0; --nFreq)
253
145k
            {
254
942k
                for( tools::Long i = nDiffY; i; --i )
255
796k
                {
256
796k
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
257
796k
                                       mpGraphics, *this,
258
796k
                                       nPixWidth, nPixHeight );
259
796k
                    nCurX++;
260
796k
                    nCurY += nOffY;
261
796k
                }
262
435k
                for( tools::Long i = nDiffX; i; --i )
263
290k
                {
264
290k
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
265
290k
                                       mpGraphics, *this,
266
290k
                                       nPixWidth, nPixHeight );
267
290k
                    nCurX++;
268
290k
                }
269
145k
                nOffY = -nOffY;
270
145k
            }
271
732
            tools::Long nFreq = nCount % (nDiffX+nDiffY);
272
732
            if (nFreq > 0)
273
528
            {
274
71.8k
                for( tools::Long i = nDiffY; i && nFreq; --i, --nFreq )
275
71.2k
                {
276
71.2k
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
277
71.2k
                                       mpGraphics, *this,
278
71.2k
                                       nPixWidth, nPixHeight );
279
71.2k
                    nCurX++;
280
71.2k
                    nCurY += nOffY;
281
282
71.2k
                }
283
639
                for( tools::Long i = nDiffX; i && nFreq; --i, --nFreq )
284
111
                {
285
111
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
286
111
                                       mpGraphics, *this,
287
111
                                       nPixWidth, nPixHeight );
288
111
                    nCurX++;
289
111
                }
290
528
            }
291
732
        }
292
802
    }
293
802
}
294
295
void OutputDevice::ImplDrawWaveTextLine( tools::Long nBaseX, tools::Long nBaseY,
296
                                         tools::Long nDistX, tools::Long nDistY,
297
                                         tools::Long nWidth, tools::Long nLayoutWidth,
298
                                         FontLineStyle eTextLine,
299
                                         Color aColor,
300
                                         bool bIsAbove )
301
56.1k
{
302
56.1k
    static bool bFuzzing = comphelper::IsFuzzing();
303
56.1k
    if (bFuzzing && nLayoutWidth > 10000)
304
55.4k
    {
305
55.4k
        SAL_WARN("vcl.gdi", "drawLine, skipping suspicious WaveTextLine of length: "
306
55.4k
                                << nLayoutWidth << " for fuzzing performance");
307
55.4k
        return;
308
55.4k
    }
309
310
647
    LogicalFontInstance* pFontInstance = mpFontInstance.get();
311
647
    tools::Long            nLineHeight;
312
647
    tools::Long            nLinePos;
313
314
647
    if ( bIsAbove )
315
451
    {
316
451
        nLineHeight = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize();
317
451
        nLinePos = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset();
318
451
    }
319
196
    else
320
196
    {
321
196
        nLineHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize();
322
196
        nLinePos = pFontInstance->mxFontMetric->GetWavelineUnderlineOffset();
323
196
    }
324
647
    if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) )
325
274
        nLineHeight = 3;
326
327
647
    tools::Long nLineWidth = mnDPIX / 300;
328
647
    if ( !nLineWidth )
329
647
        nLineWidth = 1;
330
331
647
    if ( eTextLine == LINESTYLE_BOLDWAVE )
332
118
        nLineWidth *= 2;
333
334
647
    nLinePos += nDistY - (nLineHeight / 2);
335
336
647
    tools::Long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY;
337
647
    if ( eTextLine == LINESTYLE_DOUBLEWAVE )
338
155
    {
339
155
        tools::Long nOrgLineHeight = nLineHeight;
340
155
        nLineHeight /= 3;
341
155
        if ( nLineHeight < 2 )
342
27
        {
343
27
            if ( nOrgLineHeight > 1 )
344
27
                nLineHeight = 2;
345
0
            else
346
0
                nLineHeight = 1;
347
27
        }
348
349
155
        tools::Long nLineDY = nOrgLineHeight-(nLineHeight*2);
350
155
        if ( nLineDY < nLineWidthHeight )
351
27
            nLineDY = nLineWidthHeight;
352
353
155
        tools::Long nLineDY2 = nLineDY/2;
354
155
        if ( !nLineDY2 )
355
27
            nLineDY2 = 1;
356
357
155
        nLinePos -= nLineWidthHeight-nLineDY2;
358
155
        ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
359
155
                          nLineWidth, mpFontInstance->mnOrientation, aColor );
360
155
        nLinePos += nLineWidthHeight+nLineDY;
361
155
        ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
362
155
                          nLineWidth, mpFontInstance->mnOrientation, aColor );
363
155
    }
364
492
    else
365
492
    {
366
492
        nLinePos -= nLineWidthHeight/2;
367
492
        ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
368
492
                          nLineWidth, mpFontInstance->mnOrientation, aColor );
369
492
    }
370
647
}
371
372
void OutputDevice::ImplDrawStraightTextLine( tools::Long nBaseX, tools::Long nBaseY,
373
                                             tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
374
                                             FontLineStyle eTextLine,
375
                                             Color aColor,
376
                                             bool bIsAbove )
377
270k
{
378
270k
    static bool bFuzzing = comphelper::IsFuzzing();
379
270k
    if (bFuzzing && nWidth > 25000)
380
23.1k
    {
381
23.1k
        SAL_WARN("vcl.gdi", "drawLine, skipping suspicious TextLine of length: "
382
23.1k
                                << nWidth << " for fuzzing performance");
383
23.1k
        return;
384
23.1k
    }
385
386
247k
    LogicalFontInstance*  pFontInstance = mpFontInstance.get();
387
247k
    tools::Long            nLineHeight = 0;
388
247k
    tools::Long            nLinePos  = 0;
389
247k
    tools::Long            nLinePos2 = 0;
390
391
247k
    const tools::Long nY = nDistY;
392
393
247k
    if ( eTextLine > UNDERLINE_LAST )
394
140k
        eTextLine = LINESTYLE_SINGLE;
395
396
247k
    switch ( eTextLine )
397
247k
    {
398
142k
    case LINESTYLE_SINGLE:
399
150k
    case LINESTYLE_DOTTED:
400
150k
    case LINESTYLE_DASH:
401
152k
    case LINESTYLE_LONGDASH:
402
152k
    case LINESTYLE_DASHDOT:
403
155k
    case LINESTYLE_DASHDOTDOT:
404
155k
        if ( bIsAbove )
405
62.3k
        {
406
62.3k
            nLineHeight = pFontInstance->mxFontMetric->GetAboveUnderlineSize();
407
62.3k
            nLinePos    = nY + pFontInstance->mxFontMetric->GetAboveUnderlineOffset();
408
62.3k
        }
409
93.3k
        else
410
93.3k
        {
411
93.3k
            nLineHeight = pFontInstance->mxFontMetric->GetUnderlineSize();
412
93.3k
            nLinePos    = nY + pFontInstance->mxFontMetric->GetUnderlineOffset();
413
93.3k
        }
414
155k
        break;
415
129
    case LINESTYLE_BOLD:
416
2.12k
    case LINESTYLE_BOLDDOTTED:
417
2.31k
    case LINESTYLE_BOLDDASH:
418
3.51k
    case LINESTYLE_BOLDLONGDASH:
419
9.29k
    case LINESTYLE_BOLDDASHDOT:
420
13.3k
    case LINESTYLE_BOLDDASHDOTDOT:
421
13.3k
        if ( bIsAbove )
422
7.93k
        {
423
7.93k
            nLineHeight = pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize();
424
7.93k
            nLinePos    = nY + pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset();
425
7.93k
        }
426
5.38k
        else
427
5.38k
        {
428
5.38k
            nLineHeight = pFontInstance->mxFontMetric->GetBoldUnderlineSize();
429
5.38k
            nLinePos    = nY + pFontInstance->mxFontMetric->GetBoldUnderlineOffset();
430
5.38k
        }
431
13.3k
        break;
432
1.01k
    case LINESTYLE_DOUBLE:
433
1.01k
        if ( bIsAbove )
434
129
        {
435
129
            nLineHeight = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize();
436
129
            nLinePos    = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1();
437
129
            nLinePos2   = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2();
438
129
        }
439
889
        else
440
889
        {
441
889
            nLineHeight = pFontInstance->mxFontMetric->GetDoubleUnderlineSize();
442
889
            nLinePos    = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1();
443
889
            nLinePos2   = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2();
444
889
        }
445
1.01k
        break;
446
77.4k
    default:
447
77.4k
        break;
448
247k
    }
449
450
247k
    if ( !nLineHeight )
451
77.4k
        return;
452
453
170k
    if ( mbLineColor || mbInitLineColor )
454
168k
    {
455
168k
        mpGraphics->SetLineColor();
456
168k
        mbInitLineColor = true;
457
168k
    }
458
170k
    mpGraphics->SetFillColor( aColor );
459
170k
    mbInitFillColor = true;
460
461
170k
    tools::Long nLeft = nDistX;
462
463
170k
    switch ( eTextLine )
464
170k
    {
465
142k
    case LINESTYLE_SINGLE:
466
142k
    case LINESTYLE_BOLD:
467
142k
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
468
142k
        break;
469
1.01k
    case LINESTYLE_DOUBLE:
470
1.01k
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos,  nWidth, nLineHeight );
471
1.01k
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
472
1.01k
        break;
473
8.26k
    case LINESTYLE_DOTTED:
474
10.2k
    case LINESTYLE_BOLDDOTTED:
475
10.2k
        {
476
10.2k
            tools::Long nDotWidth = nLineHeight*mnDPIY;
477
10.2k
            nDotWidth += mnDPIY/2;
478
10.2k
            nDotWidth /= mnDPIY;
479
480
10.2k
            tools::Long nTempWidth = nDotWidth;
481
10.2k
            tools::Long nEnd = nLeft+nWidth;
482
1.26M
            while ( nLeft < nEnd )
483
1.25M
            {
484
1.25M
                if ( nLeft+nTempWidth > nEnd )
485
3.40k
                    nTempWidth = nEnd-nLeft;
486
487
1.25M
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
488
1.25M
                nLeft += nDotWidth*2;
489
1.25M
            }
490
10.2k
        }
491
10.2k
        break;
492
335
    case LINESTYLE_DASH:
493
1.79k
    case LINESTYLE_LONGDASH:
494
1.97k
    case LINESTYLE_BOLDDASH:
495
3.17k
    case LINESTYLE_BOLDLONGDASH:
496
3.17k
        {
497
3.17k
            tools::Long nDotWidth = nLineHeight*mnDPIY;
498
3.17k
            nDotWidth += mnDPIY/2;
499
3.17k
            nDotWidth /= mnDPIY;
500
501
3.17k
            tools::Long nMinDashWidth;
502
3.17k
            tools::Long nMinSpaceWidth;
503
3.17k
            tools::Long nSpaceWidth;
504
3.17k
            tools::Long nDashWidth;
505
3.17k
            if ( (eTextLine == LINESTYLE_LONGDASH) ||
506
1.71k
                 (eTextLine == LINESTYLE_BOLDLONGDASH) )
507
2.65k
            {
508
2.65k
                nMinDashWidth = nDotWidth*6;
509
2.65k
                nMinSpaceWidth = nDotWidth*2;
510
2.65k
                nDashWidth = 200;
511
2.65k
                nSpaceWidth = 100;
512
2.65k
            }
513
521
            else
514
521
            {
515
521
                nMinDashWidth = nDotWidth*4;
516
521
                nMinSpaceWidth = (nDotWidth*150)/100;
517
521
                nDashWidth = 100;
518
521
                nSpaceWidth = 50;
519
521
            }
520
3.17k
            nDashWidth = o3tl::convert(nDashWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
521
3.17k
            nSpaceWidth = o3tl::convert(nSpaceWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
522
            // DashWidth will be increased if the line is getting too thick
523
            // in proportion to the line's length
524
3.17k
            if ( nDashWidth < nMinDashWidth )
525
1.73k
                nDashWidth = nMinDashWidth;
526
3.17k
            if ( nSpaceWidth < nMinSpaceWidth )
527
1.72k
                nSpaceWidth = nMinSpaceWidth;
528
529
3.17k
            tools::Long nTempWidth = nDashWidth;
530
3.17k
            tools::Long nEnd = nLeft+nWidth;
531
161k
            while ( nLeft < nEnd )
532
158k
            {
533
158k
                if ( nLeft+nTempWidth > nEnd )
534
1.86k
                    nTempWidth = nEnd-nLeft;
535
158k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
536
158k
                nLeft += nDashWidth+nSpaceWidth;
537
158k
            }
538
3.17k
        }
539
3.17k
        break;
540
31
    case LINESTYLE_DASHDOT:
541
5.81k
    case LINESTYLE_BOLDDASHDOT:
542
5.81k
        {
543
5.81k
            tools::Long nDotWidth = nLineHeight*mnDPIY;
544
5.81k
            nDotWidth += mnDPIY/2;
545
5.81k
            nDotWidth /= mnDPIY;
546
547
5.81k
            tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
548
5.81k
            tools::Long nMinDashWidth = nDotWidth*4;
549
            // DashWidth will be increased if the line is getting too thick
550
            // in proportion to the line's length
551
5.81k
            if ( nDashWidth < nMinDashWidth )
552
5.78k
                nDashWidth = nMinDashWidth;
553
554
5.81k
            tools::Long nTempDotWidth = nDotWidth;
555
5.81k
            tools::Long nTempDashWidth = nDashWidth;
556
5.81k
            tools::Long nEnd = nLeft+nWidth;
557
151k
            while ( nLeft < nEnd )
558
147k
            {
559
147k
                if ( nLeft+nTempDotWidth > nEnd )
560
1.10k
                    nTempDotWidth = nEnd-nLeft;
561
562
147k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
563
147k
                nLeft += nDotWidth*2;
564
147k
                if ( nLeft > nEnd )
565
1.84k
                    break;
566
567
145k
                if ( nLeft+nTempDashWidth > nEnd )
568
2.25k
                    nTempDashWidth = nEnd-nLeft;
569
570
145k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
571
145k
                nLeft += nDashWidth+nDotWidth;
572
145k
            }
573
5.81k
        }
574
5.81k
        break;
575
3.59k
    case LINESTYLE_DASHDOTDOT:
576
7.61k
    case LINESTYLE_BOLDDASHDOTDOT:
577
7.61k
        {
578
7.61k
            tools::Long nDotWidth = nLineHeight*mnDPIY;
579
7.61k
            nDotWidth += mnDPIY/2;
580
7.61k
            nDotWidth /= mnDPIY;
581
582
7.61k
            tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
583
7.61k
            tools::Long nMinDashWidth = nDotWidth*4;
584
            // DashWidth will be increased if the line is getting too thick
585
            // in proportion to the line's length
586
7.61k
            if ( nDashWidth < nMinDashWidth )
587
7.59k
                nDashWidth = nMinDashWidth;
588
589
7.61k
            tools::Long nTempDotWidth = nDotWidth;
590
7.61k
            tools::Long nTempDashWidth = nDashWidth;
591
7.61k
            tools::Long nEnd = nLeft+nWidth;
592
80.6k
            while ( nLeft < nEnd )
593
78.0k
            {
594
78.0k
                if ( nLeft+nTempDotWidth > nEnd )
595
2.90k
                    nTempDotWidth = nEnd-nLeft;
596
597
78.0k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
598
78.0k
                nLeft += nDotWidth*2;
599
78.0k
                if ( nLeft > nEnd )
600
4.08k
                    break;
601
602
74.0k
                if ( nLeft+nTempDotWidth > nEnd )
603
626
                    nTempDotWidth = nEnd-nLeft;
604
605
74.0k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
606
74.0k
                nLeft += nDotWidth*2;
607
74.0k
                if ( nLeft > nEnd )
608
989
                    break;
609
610
73.0k
                if ( nLeft+nTempDashWidth > nEnd )
611
2.09k
                    nTempDashWidth = nEnd-nLeft;
612
613
73.0k
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
614
73.0k
                nLeft += nDashWidth+nDotWidth;
615
73.0k
            }
616
7.61k
        }
617
7.61k
        break;
618
0
    default:
619
0
        break;
620
170k
    }
621
170k
}
622
623
void OutputDevice::ImplDrawStrikeoutLine( tools::Long nBaseX, tools::Long nBaseY,
624
                                          tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
625
                                          FontStrikeout eStrikeout,
626
                                          Color aColor )
627
148k
{
628
148k
    LogicalFontInstance*  pFontInstance = mpFontInstance.get();
629
148k
    tools::Long            nLineHeight = 0;
630
148k
    tools::Long            nLinePos  = 0;
631
148k
    tools::Long            nLinePos2 = 0;
632
633
148k
    tools::Long nY = nDistY;
634
635
148k
    if ( eStrikeout > STRIKEOUT_LAST )
636
133k
        eStrikeout = STRIKEOUT_SINGLE;
637
638
148k
    switch ( eStrikeout )
639
148k
    {
640
134k
    case STRIKEOUT_SINGLE:
641
134k
        nLineHeight = pFontInstance->mxFontMetric->GetStrikeoutSize();
642
134k
        nLinePos    = nY + pFontInstance->mxFontMetric->GetStrikeoutOffset();
643
134k
        break;
644
5
    case STRIKEOUT_BOLD:
645
5
        nLineHeight = pFontInstance->mxFontMetric->GetBoldStrikeoutSize();
646
5
        nLinePos    = nY + pFontInstance->mxFontMetric->GetBoldStrikeoutOffset();
647
5
        break;
648
480
    case STRIKEOUT_DOUBLE:
649
480
        nLineHeight = pFontInstance->mxFontMetric->GetDoubleStrikeoutSize();
650
480
        nLinePos    = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1();
651
480
        nLinePos2   = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2();
652
480
        break;
653
13.7k
    default:
654
13.7k
        break;
655
148k
    }
656
657
148k
    if ( !nLineHeight )
658
13.7k
        return;
659
660
134k
    if ( mbLineColor || mbInitLineColor )
661
134k
    {
662
134k
        mpGraphics->SetLineColor();
663
134k
        mbInitLineColor = true;
664
134k
    }
665
134k
    mpGraphics->SetFillColor( aColor );
666
134k
    mbInitFillColor = true;
667
668
134k
    const tools::Long& nLeft = nDistX;
669
670
134k
    switch ( eStrikeout )
671
134k
    {
672
134k
    case STRIKEOUT_SINGLE:
673
134k
    case STRIKEOUT_BOLD:
674
134k
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
675
134k
        break;
676
480
    case STRIKEOUT_DOUBLE:
677
480
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
678
480
        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
679
480
        break;
680
0
    default:
681
0
        break;
682
134k
    }
683
134k
}
684
685
void OutputDevice::ImplDrawStrikeoutChar( tools::Long nBaseX, tools::Long nBaseY,
686
                                          tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
687
                                          FontStrikeout eStrikeout,
688
                                          Color aColor )
689
14.5k
{
690
    // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
691
    // to tweak this
692
14.5k
    if (!nWidth)
693
1.59k
        return;
694
695
    // prepare string for strikeout measurement
696
12.9k
    const char cStrikeoutChar =  eStrikeout == STRIKEOUT_SLASH ? '/' : 'X';
697
12.9k
    static const int nTestStrLen = 4;
698
12.9k
    static const int nMaxStrikeStrLen = 2048;
699
12.9k
    sal_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind...
700
701
64.9k
    for( int i = 0; i < nTestStrLen; ++i)
702
51.9k
        aChars[i] = cStrikeoutChar;
703
704
12.9k
    const OUString aStrikeoutTest(aChars, nTestStrLen);
705
706
    // calculate approximation of strikeout atom size
707
12.9k
    tools::Long nStrikeoutWidth = 0;
708
12.9k
    std::unique_ptr<SalLayout> pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
709
12.9k
    if( pLayout )
710
12.9k
    {
711
12.9k
        nStrikeoutWidth = pLayout->GetTextWidth() / nTestStrLen;
712
12.9k
    }
713
12.9k
    if( nStrikeoutWidth <= 0 ) // sanity check
714
689
        return;
715
716
12.2k
    int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth;
717
12.2k
    if( nStrikeStrLen > nMaxStrikeStrLen )
718
442
        nStrikeStrLen = nMaxStrikeStrLen;
719
11.8k
    else if (nStrikeStrLen < 0)
720
17
        nStrikeStrLen = 0;
721
722
    // build the strikeout string
723
1.71M
    for( int i = nTestStrLen; i < nStrikeStrLen; ++i)
724
1.70M
        aChars[i] = cStrikeoutChar;
725
726
12.2k
    const OUString aStrikeoutText(aChars, nStrikeStrLen);
727
728
12.2k
    if( mpFontInstance->mnOrientation )
729
10.1k
    {
730
10.1k
        Point aOriginPt(0, 0);
731
10.1k
        aOriginPt.RotateAround( nDistX, nDistY, mpFontInstance->mnOrientation );
732
10.1k
    }
733
734
12.2k
    nBaseX += nDistX;
735
12.2k
    nBaseY += nDistY;
736
737
    // strikeout text has to be left aligned
738
12.2k
    vcl::text::ComplexTextLayoutFlags nOrigTLM = mnTextLayoutMode;
739
12.2k
    mnTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiStrong;
740
12.2k
    pLayout = ImplLayout( aStrikeoutText, 0, aStrikeoutText.getLength() );
741
12.2k
    mnTextLayoutMode = nOrigTLM;
742
743
12.2k
    if( !pLayout )
744
0
        return;
745
746
    // draw the strikeout text
747
12.2k
    const Color aOldColor = GetTextColor();
748
12.2k
    SetTextColor( aColor );
749
12.2k
    ImplInitTextColor();
750
751
12.2k
    pLayout->DrawBase() = basegfx::B2DPoint(nBaseX + mnTextOffX, nBaseY + mnTextOffY);
752
753
12.2k
    tools::Rectangle aPixelRect;
754
12.2k
    aPixelRect.SetLeft( nBaseX+mnTextOffX );
755
12.2k
    aPixelRect.SetRight( aPixelRect.Left()+nWidth );
756
12.2k
    aPixelRect.SetBottom( nBaseY+mpFontInstance->mxFontMetric->GetDescent() );
757
12.2k
    aPixelRect.SetTop( nBaseY-mpFontInstance->mxFontMetric->GetAscent() );
758
759
12.2k
    if (mpFontInstance->mnOrientation)
760
10.1k
    {
761
10.1k
        tools::Polygon aPoly( aPixelRect );
762
10.1k
        aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontInstance->mnOrientation);
763
10.1k
        aPixelRect = aPoly.GetBoundRect();
764
10.1k
    }
765
766
12.2k
    Push( vcl::PushFlags::CLIPREGION );
767
12.2k
    IntersectClipRegion( PixelToLogic(aPixelRect) );
768
12.2k
    if( mbInitClipRegion )
769
12.2k
        InitClipRegion();
770
771
12.2k
    pLayout->DrawText( *mpGraphics );
772
773
12.2k
    Pop();
774
775
12.2k
    SetTextColor( aOldColor );
776
12.2k
    ImplInitTextColor();
777
12.2k
}
778
779
void OutputDevice::ImplDrawTextLine( tools::Long nX, tools::Long nY,
780
                                     tools::Long nDistX, double nWidth,
781
                                     double nLayoutWidth,
782
                                     FontStrikeout eStrikeout,
783
                                     FontLineStyle eUnderline,
784
                                     FontLineStyle eOverline,
785
                                     bool bUnderlineAbove )
786
167k
{
787
167k
    if ( !nWidth )
788
3.67k
        return;
789
790
163k
    Color aStrikeoutColor = GetTextColor();
791
163k
    Color aUnderlineColor = GetTextLineColor();
792
163k
    Color aOverlineColor  = GetOverlineColor();
793
163k
    bool bStrikeoutDone = false;
794
163k
    bool bUnderlineDone = false;
795
163k
    bool bOverlineDone  = false;
796
797
163k
    if ( IsRTLEnabled() )
798
0
    {
799
0
        tools::Long nXAdd = nWidth - nDistX;
800
0
        if( mpFontInstance->mnOrientation )
801
0
            nXAdd = basegfx::fround<tools::Long>( nXAdd * cos( toRadians(mpFontInstance->mnOrientation) ) );
802
803
0
        nX += nXAdd - 1;
804
0
    }
805
806
163k
    if ( !IsTextLineColor() )
807
138k
        aUnderlineColor = GetTextColor();
808
809
163k
    if ( !IsOverlineColor() )
810
137k
        aOverlineColor = GetTextColor();
811
812
163k
    if ( (eUnderline == LINESTYLE_SMALLWAVE) ||
813
162k
         (eUnderline == LINESTYLE_WAVE) ||
814
162k
         (eUnderline == LINESTYLE_DOUBLEWAVE) ||
815
162k
         (eUnderline == LINESTYLE_BOLDWAVE) )
816
510
    {
817
510
        ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, nLayoutWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
818
510
        bUnderlineDone = true;
819
510
    }
820
163k
    if ( (eOverline == LINESTYLE_SMALLWAVE) ||
821
109k
         (eOverline == LINESTYLE_WAVE) ||
822
109k
         (eOverline == LINESTYLE_DOUBLEWAVE) ||
823
107k
         (eOverline == LINESTYLE_BOLDWAVE) )
824
55.6k
    {
825
55.6k
        ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, nLayoutWidth, eOverline, aOverlineColor, true );
826
55.6k
        bOverlineDone = true;
827
55.6k
    }
828
829
163k
    if ( (eStrikeout == STRIKEOUT_SLASH) ||
830
153k
         (eStrikeout == STRIKEOUT_X) )
831
14.5k
    {
832
14.5k
        ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
833
14.5k
        bStrikeoutDone = true;
834
14.5k
    }
835
836
163k
    if ( !bUnderlineDone )
837
162k
        ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
838
839
163k
    if ( !bOverlineDone )
840
107k
        ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true );
841
842
163k
    if ( !bStrikeoutDone )
843
148k
        ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
844
163k
}
845
846
void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStrikeout,
847
                                      FontLineStyle eUnderline, FontLineStyle eOverline,
848
                                      bool bWordLine, bool bUnderlineAbove )
849
15.5k
{
850
15.5k
    double nLayoutWidth = rSalLayout.GetTextWidth();
851
15.5k
    if( bWordLine )
852
9.93k
    {
853
        // draw everything relative to the layout base point
854
9.93k
        const basegfx::B2DPoint aStartPt = rSalLayout.DrawBase();
855
856
        // calculate distance of each word from the base point
857
9.93k
        basegfx::B2DPoint aPos;
858
9.93k
        double nDist = 0;
859
9.93k
        double nWidth = 0;
860
9.93k
        const GlyphItem* pGlyph;
861
9.93k
        int nStart = 0;
862
9.56M
        while (rSalLayout.GetNextGlyph(&pGlyph, aPos, nStart))
863
9.55M
        {
864
            // calculate the boundaries of each word
865
9.55M
            if (!pGlyph->IsSpacing())
866
9.23M
            {
867
9.23M
                if( !nWidth )
868
194k
                {
869
                    // get the distance to the base point (as projected to baseline)
870
194k
                    nDist = aPos.getX() - aStartPt.getX();
871
194k
                    if( mpFontInstance->mnOrientation )
872
162k
                    {
873
162k
                        const double nDY = aPos.getY() - aStartPt.getY();
874
162k
                        const double fRad = toRadians(mpFontInstance->mnOrientation);
875
162k
                        nDist = basegfx::fround<tools::Long>(nDist * cos(fRad) - nDY * sin(fRad));
876
162k
                    }
877
194k
                }
878
879
                // update the length of the textline
880
9.23M
                nWidth += pGlyph->newWidth();
881
9.23M
            }
882
320k
            else if( nWidth > 0 )
883
154k
            {
884
                // draw the textline for each word
885
154k
                ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth,
886
154k
                                  eStrikeout, eUnderline, eOverline, bUnderlineAbove );
887
154k
                nWidth = 0;
888
154k
            }
889
9.55M
        }
890
891
        // draw textline for the last word
892
9.93k
        if( nWidth > 0 )
893
6.27k
        {
894
6.27k
            ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth,
895
6.27k
                              eStrikeout, eUnderline, eOverline, bUnderlineAbove );
896
6.27k
        }
897
9.93k
    }
898
5.62k
    else
899
5.62k
    {
900
5.62k
        basegfx::B2DPoint aStartPt = rSalLayout.GetDrawPosition();
901
5.62k
        ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), 0,
902
5.62k
                          nLayoutWidth, nLayoutWidth,
903
5.62k
                          eStrikeout, eUnderline, eOverline, bUnderlineAbove );
904
5.62k
    }
905
15.5k
}
906
907
void OutputDevice::ImplDrawMnemonicLine( tools::Long nX, tools::Long nY, tools::Long nWidth )
908
342
{
909
342
    tools::Long nBaseX = nX;
910
342
    if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() )
911
0
    {
912
        // revert the hack that will be done later in ImplDrawTextLine
913
0
        nX = nBaseX - nWidth - (nX - nBaseX - 1);
914
0
    }
915
916
342
    ImplDrawTextLine( nX, nY, 0, nWidth, nWidth, STRIKEOUT_NONE, LINESTYLE_SINGLE, LINESTYLE_NONE, false );
917
342
}
918
919
void OutputDevice::SetTextLineColor()
920
572k
{
921
572k
    if ( mpMetaFile )
922
0
        mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) );
923
924
572k
    maTextLineColor = COL_TRANSPARENT;
925
572k
}
926
927
void OutputDevice::SetTextLineColor( const Color& rColor )
928
14.6k
{
929
14.6k
    Color aColor(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));
930
931
14.6k
    if ( mpMetaFile )
932
4
        mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) );
933
934
14.6k
    maTextLineColor = aColor;
935
14.6k
}
936
937
void OutputDevice::SetOverlineColor()
938
573k
{
939
573k
    if ( mpMetaFile )
940
0
        mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), false ) );
941
942
573k
    maOverlineColor = COL_TRANSPARENT;
943
573k
}
944
945
void OutputDevice::SetOverlineColor( const Color& rColor )
946
15.7k
{
947
15.7k
    Color aColor(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));
948
949
15.7k
    if ( mpMetaFile )
950
4
        mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) );
951
952
15.7k
    maOverlineColor = aColor;
953
15.7k
}
954
955
void OutputDevice::DrawTextLine( const Point& rPos, tools::Long nWidth,
956
                                 FontStrikeout eStrikeout,
957
                                 FontLineStyle eUnderline,
958
                                 FontLineStyle eOverline )
959
1.69k
{
960
1.69k
    assert(!is_double_buffered_window());
961
962
1.69k
    if ( mpMetaFile )
963
0
        mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
964
965
1.69k
    if ( ((eUnderline == LINESTYLE_NONE) || (eUnderline == LINESTYLE_DONTKNOW)) &&
966
1.43k
         ((eOverline  == LINESTYLE_NONE) || (eOverline  == LINESTYLE_DONTKNOW)) &&
967
1.26k
         ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
968
1.16k
    {
969
1.16k
        return;
970
1.16k
    }
971
527
    if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
972
0
        return;
973
974
527
    if( mbInitClipRegion )
975
75
        InitClipRegion();
976
977
527
    if( mbOutputClipped )
978
48
        return;
979
980
    // initialize font if needed to get text offsets
981
    // TODO: only needed for mnTextOff!=(0,0)
982
479
    if (!InitFont())
983
0
        return;
984
985
479
    Point aPos = ImplLogicToDevicePixel( rPos );
986
479
    double fWidth = ImplLogicWidthToDeviceSubPixel(nWidth);
987
479
    aPos += Point( mnTextOffX, mnTextOffY );
988
479
    ImplDrawTextLine( aPos.X(), aPos.X(), 0, fWidth, fWidth, eStrikeout, eUnderline, eOverline, /*bUnderlineAbove*/false );
989
479
}
990
991
void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, tools::Long nLineWidth, tools::Long nWaveHeight)
992
0
{
993
0
    assert(!is_double_buffered_window());
994
995
0
    if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
996
0
        return;
997
998
    // we need a graphics
999
0
    if( !mpGraphics && !AcquireGraphics() )
1000
0
        return;
1001
0
    assert(mpGraphics);
1002
1003
0
    if ( mbInitClipRegion )
1004
0
        InitClipRegion();
1005
1006
0
    if ( mbOutputClipped )
1007
0
        return;
1008
1009
0
    if (!InitFont())
1010
0
        return;
1011
1012
0
    Point aStartPt = ImplLogicToDevicePixel(rStartPos);
1013
0
    Point aEndPt = ImplLogicToDevicePixel(rEndPos);
1014
1015
0
    tools::Long nStartX = aStartPt.X();
1016
0
    tools::Long nStartY = aStartPt.Y();
1017
0
    tools::Long nEndX = aEndPt.X();
1018
0
    tools::Long nEndY = aEndPt.Y();
1019
0
    double fOrientation = 0.0;
1020
1021
    // handle rotation
1022
0
    if (nStartY != nEndY || nStartX > nEndX)
1023
0
    {
1024
0
        fOrientation = basegfx::rad2deg(std::atan2(nStartY - nEndY, nEndX - nStartX));
1025
        // un-rotate the end point
1026
0
        aStartPt.RotateAround(nEndX, nEndY, Degree10(static_cast<sal_Int16>(-fOrientation * 10.0)));
1027
0
    }
1028
1029
    // Handle HiDPI
1030
0
    float fScaleFactor = GetDPIScaleFactor();
1031
0
    if (fScaleFactor > 1.0f)
1032
0
    {
1033
0
        nWaveHeight *= fScaleFactor;
1034
1035
0
        nStartY += fScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation.
1036
1037
        // odd heights look better than even
1038
0
        if (nWaveHeight % 2 == 0)
1039
0
        {
1040
0
            nWaveHeight--;
1041
0
        }
1042
0
    }
1043
1044
    // #109280# make sure the waveline does not exceed the descent to avoid paint problems
1045
0
    LogicalFontInstance* pFontInstance = mpFontInstance.get();
1046
0
    if (nWaveHeight > pFontInstance->mxFontMetric->GetWavelineUnderlineSize()
1047
    // tdf#153223 polyline with lineheight >0 not drawn when skia is off
1048
#ifdef MACOSX
1049
        || !SkiaHelper::isVCLSkiaEnabled()
1050
#endif
1051
0
       )
1052
0
    {
1053
0
        nWaveHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize();
1054
        // tdf#124848 hairline
1055
0
        nLineWidth = 0;
1056
0
    }
1057
1058
0
    if ( fOrientation == 0.0 )
1059
0
    {
1060
0
        static tools::DeleteOnDeinit< WavyLineCache > snLineCache {};
1061
0
        if ( !snLineCache.get() )
1062
0
            return;
1063
0
        WavyLineCache& rLineCache = *snLineCache.get();
1064
0
        Bitmap aWavylinebmp;
1065
0
        if ( !rLineCache.find( GetLineColor(), nLineWidth, nWaveHeight, nEndX - nStartX, aWavylinebmp ) )
1066
0
        {
1067
0
            size_t nWordLength = nEndX - nStartX;
1068
            // start with something big to avoid updating it frequently
1069
0
            nWordLength = nWordLength < 1024 ? 1024 : nWordLength;
1070
0
            ScopedVclPtrInstance< VirtualDevice > pVirtDev( *this, DeviceFormat::WITH_ALPHA );
1071
0
            pVirtDev->SetOutputSizePixel( Size( nWordLength, nWaveHeight * 2 ), false );
1072
0
            pVirtDev->SetLineColor( GetLineColor() );
1073
0
            pVirtDev->SetBackground( Wallpaper( COL_TRANSPARENT ) );
1074
0
            pVirtDev->Erase();
1075
0
            pVirtDev->SetAntialiasing( AntialiasingFlags::Enable );
1076
0
            pVirtDev->ImplDrawWaveLineBezier( 0, 0, nWordLength, 0, nWaveHeight, fOrientation, nLineWidth );
1077
0
            Bitmap aBitmap(pVirtDev->GetBitmap(Point(0, 0), pVirtDev->GetOutputSize()));
1078
1079
0
            rLineCache.insert( aBitmap, GetLineColor(), nLineWidth, nWaveHeight, nWordLength, aWavylinebmp );
1080
0
        }
1081
0
        if ( aWavylinebmp.ImplGetSalBitmap() != nullptr )
1082
0
        {
1083
0
            Size _size( nEndX - nStartX, aWavylinebmp.GetSizePixel().Height() );
1084
0
            DrawBitmap(Point( rStartPos.X(), rStartPos.Y() ), PixelToLogic( _size ), Point(), _size, aWavylinebmp);
1085
0
        }
1086
0
        return;
1087
0
    }
1088
1089
0
    ImplDrawWaveLineBezier( nStartX, nStartY, nEndX, nEndY, nWaveHeight, fOrientation, nLineWidth );
1090
0
}
1091
1092
void OutputDevice::ImplDrawWaveLineBezier(tools::Long nStartX, tools::Long nStartY, tools::Long nEndX, tools::Long nEndY, tools::Long nWaveHeight, double fOrientation, tools::Long nLineWidth)
1093
0
{
1094
    // we need a graphics
1095
0
    if( !mpGraphics && !AcquireGraphics() )
1096
0
        return;
1097
0
    assert(mpGraphics);
1098
1099
0
    if ( mbInitClipRegion )
1100
0
        InitClipRegion();
1101
1102
0
    if ( mbOutputClipped )
1103
0
        return;
1104
1105
0
    if (!InitFont())
1106
0
        return;
1107
1108
0
    const basegfx::B2DRectangle aWaveLineRectangle(nStartX, nStartY, nEndX, nEndY + nWaveHeight);
1109
0
    const basegfx::B2DPolygon aWaveLinePolygon = basegfx::createWaveLinePolygon(aWaveLineRectangle);
1110
0
    const basegfx::B2DHomMatrix aRotationMatrix = basegfx::utils::createRotateAroundPoint(nStartX, nStartY, basegfx::deg2rad(-fOrientation));
1111
0
    const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
1112
1113
0
    mpGraphics->SetLineColor(GetLineColor());
1114
0
    mpGraphics->DrawPolyLine(
1115
0
            aRotationMatrix,
1116
0
            aWaveLinePolygon,
1117
0
            0.0,
1118
0
            nLineWidth,
1119
0
            nullptr, // MM01
1120
0
            basegfx::B2DLineJoin::NONE,
1121
0
            css::drawing::LineCap_BUTT,
1122
0
            basegfx::deg2rad(15.0),
1123
0
            bPixelSnapHairline,
1124
0
            *this);
1125
0
}
1126
1127
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */