Coverage Report

Created: 2025-12-08 09:28

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