Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.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 <sdr/primitive2d/sdrmeasureprimitive2d.hxx>
21
#include <sdr/primitive2d/sdrdecompositiontools.hxx>
22
#include <basegfx/matrix/b2dhommatrix.hxx>
23
#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
24
#include <sdr/attribute/sdrtextattribute.hxx>
25
#include <basegfx/polygon/b2dpolypolygontools.hxx>
26
#include <rtl/ref.hxx>
27
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
28
#include <basegfx/matrix/b2dhommatrixtools.hxx>
29
#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
30
#include <osl/diagnose.h>
31
32
33
using namespace com::sun::star;
34
35
36
namespace drawinglayer::primitive2d
37
{
38
        Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
39
            const attribute::SdrLineAttribute& rLineAttribute,
40
            const basegfx::B2DHomMatrix& rObjectMatrix,
41
            const basegfx::B2DPoint& rStart,
42
            const basegfx::B2DPoint& rEnd,
43
            bool bLeftActive,
44
            bool bRightActive) const
45
0
        {
46
0
            const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
47
0
            basegfx::B2DPolygon aPolygon;
48
49
0
            aPolygon.append(rStart);
50
0
            aPolygon.append(rEnd);
51
0
            aPolygon.transform(rObjectMatrix);
52
53
0
            if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
54
0
            {
55
0
                return createPolygonLinePrimitive(
56
0
                    aPolygon,
57
0
                    rLineAttribute,
58
0
                    attribute::SdrLineStartEndAttribute());
59
0
            }
60
61
0
            if(bLeftActive && bRightActive)
62
0
            {
63
0
                return createPolygonLinePrimitive(
64
0
                    aPolygon,
65
0
                    rLineAttribute,
66
0
                    rLineStartEnd);
67
0
            }
68
69
0
            const basegfx::B2DPolyPolygon aEmpty;
70
0
            const attribute::SdrLineStartEndAttribute aLineStartEnd(
71
0
                bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
72
0
                bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
73
0
                bLeftActive && rLineStartEnd.isStartActive(), bRightActive && rLineStartEnd.isEndActive(),
74
0
                bLeftActive && rLineStartEnd.isStartCentered(), bRightActive && rLineStartEnd.isEndCentered());
75
76
0
            return createPolygonLinePrimitive(
77
0
                aPolygon,
78
0
                rLineAttribute,
79
0
                aLineStartEnd);
80
0
        }
81
82
        Primitive2DReference SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& aViewInformation) const
83
0
        {
84
0
            Primitive2DContainer aRetval;
85
0
            rtl::Reference<SdrBlockTextPrimitive2D> xBlockText;
86
0
            basegfx::B2DRange aTextRange;
87
0
            const basegfx::B2DVector aLine(getEnd() - getStart());
88
0
            const double fDistance(aLine.getLength());
89
0
            const double fAngle(atan2(aLine.getY(), aLine.getX()));
90
0
            bool bAutoUpsideDown(false);
91
0
            const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
92
0
            const basegfx::B2DHomMatrix aObjectMatrix(
93
0
                basegfx::utils::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));
94
95
            // prepare text, but do not add yet; it needs to be aligned to
96
            // the line geometry
97
0
            if(!rTextAttribute.isDefault())
98
0
            {
99
0
                basegfx::B2DHomMatrix aTextMatrix;
100
0
                double fTestAngle(fAngle);
101
102
0
                if(getTextRotation())
103
0
                {
104
0
                    aTextMatrix.rotate(-M_PI_2);
105
0
                    fTestAngle -= (M_PI_2);
106
107
0
                    if(getTextAutoAngle() && fTestAngle < -M_PI)
108
0
                    {
109
0
                        fTestAngle += 2 * M_PI;
110
0
                    }
111
0
                }
112
113
0
                if(getTextAutoAngle())
114
0
                {
115
0
                    if(fTestAngle > (M_PI / 4.0) || fTestAngle < (-M_PI * (3.0 / 4.0)))
116
0
                    {
117
0
                        bAutoUpsideDown = true;
118
0
                    }
119
0
                }
120
121
                // create primitive and get text range
122
0
                xBlockText = new SdrBlockTextPrimitive2D(
123
0
                    &rTextAttribute.getSdrText(),
124
0
                    rTextAttribute.getOutlinerParaObject(),
125
0
                    aTextMatrix,
126
0
                    SDRTEXTHORZADJUST_CENTER,
127
0
                    SDRTEXTVERTADJUST_CENTER,
128
0
                    rTextAttribute.isScroll(),
129
0
                    false,
130
0
                    false,
131
0
                    false);
132
133
0
                aTextRange = xBlockText->getB2DRange(aViewInformation);
134
0
            }
135
136
            // prepare line attribute and result
137
0
            double fTextX;
138
0
            double fTextY;
139
0
            {
140
0
                const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
141
0
                bool bArrowsOutside(false);
142
0
                bool bMainLineSplitted(false);
143
0
                const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
144
0
                double fStartArrowW(0.0);
145
0
                double fStartArrowH(0.0);
146
0
                double fEndArrowW(0.0);
147
0
                double fEndArrowH(0.0);
148
149
0
                if(!rLineStartEnd.isDefault())
150
0
                {
151
0
                    if(rLineStartEnd.isStartActive())
152
0
                    {
153
0
                        const basegfx::B2DRange aArrowRange(rLineStartEnd.getStartPolyPolygon().getB2DRange());
154
0
                        fStartArrowW = rLineStartEnd.getStartWidth();
155
0
                        fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
156
157
0
                        if(rLineStartEnd.isStartCentered())
158
0
                        {
159
0
                            fStartArrowH *= 0.5;
160
0
                        }
161
0
                    }
162
163
0
                    if(rLineStartEnd.isEndActive())
164
0
                    {
165
0
                        const basegfx::B2DRange aArrowRange(rLineStartEnd.getEndPolyPolygon().getB2DRange());
166
0
                        fEndArrowW = rLineStartEnd.getEndWidth();
167
0
                        fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
168
169
0
                        if(rLineStartEnd.isEndCentered())
170
0
                        {
171
0
                            fEndArrowH *= 0.5;
172
0
                        }
173
0
                    }
174
0
                }
175
176
0
                const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
177
0
                const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
178
0
                const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
179
180
0
                if(fSpaceNeededByArrows > fDistance)
181
0
                {
182
0
                    bArrowsOutside = true;
183
0
                }
184
185
0
                MeasureTextPosition eHorizontal(getHorizontal());
186
0
                MeasureTextPosition eVertical(getVertical());
187
188
0
                if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
189
0
                {
190
0
                    eVertical = MEASURETEXTPOSITION_NEGATIVE;
191
0
                }
192
193
0
                if(MEASURETEXTPOSITION_CENTERED == eVertical)
194
0
                {
195
0
                    bMainLineSplitted = true;
196
0
                }
197
198
0
                if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
199
0
                {
200
0
                    if(aTextRange.getWidth() > fDistance)
201
0
                    {
202
0
                        eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
203
0
                    }
204
0
                    else
205
0
                    {
206
0
                        eHorizontal = MEASURETEXTPOSITION_CENTERED;
207
0
                    }
208
209
0
                    if(bMainLineSplitted)
210
0
                    {
211
0
                        if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
212
0
                        {
213
0
                            bArrowsOutside = true;
214
0
                        }
215
0
                    }
216
0
                    else
217
0
                    {
218
0
                        const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
219
220
0
                        if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
221
0
                        {
222
0
                            bArrowsOutside = true;
223
0
                        }
224
0
                    }
225
0
                }
226
227
0
                if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
228
0
                {
229
0
                    bArrowsOutside = true;
230
0
                }
231
232
                // switch text above/below?
233
0
                if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
234
0
                {
235
0
                    if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
236
0
                    {
237
0
                        eVertical = MEASURETEXTPOSITION_POSITIVE;
238
0
                    }
239
0
                    else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
240
0
                    {
241
0
                        eVertical = MEASURETEXTPOSITION_NEGATIVE;
242
0
                    }
243
0
                }
244
245
0
                const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
246
0
                const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
247
0
                const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
248
249
                // main line
250
0
                if(bArrowsOutside)
251
0
                {
252
0
                    double fLenLeft(fArrowsOutsideLen);
253
0
                    double fLenRight(fArrowsOutsideLen);
254
255
0
                    if(!bMainLineSplitted)
256
0
                    {
257
0
                        if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
258
0
                        {
259
0
                            fLenLeft = fStartArrowH + aTextRange.getWidth();
260
0
                        }
261
0
                        else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
262
0
                        {
263
0
                            fLenRight = fEndArrowH + aTextRange.getWidth();
264
0
                        }
265
0
                    }
266
267
0
                    const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
268
0
                    const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
269
270
0
                    aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
271
0
                    aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
272
273
0
                    if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
274
0
                    {
275
0
                        aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
276
0
                    }
277
0
                }
278
0
                else
279
0
                {
280
0
                    if(bMainLineSplitted)
281
0
                    {
282
0
                        const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
283
0
                        const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
284
0
                        const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
285
286
0
                        aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
287
0
                        aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
288
0
                    }
289
0
                    else
290
0
                    {
291
0
                        aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
292
0
                    }
293
0
                }
294
295
                // left/right help line value preparation
296
0
                const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
297
0
                const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
298
0
                const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
299
300
                // left help line
301
0
                const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
302
0
                const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
303
304
0
                aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
305
306
                // right help line
307
0
                const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
308
0
                const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
309
310
0
                aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
311
312
                // text horizontal position
313
0
                if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
314
0
                {
315
                    // left
316
0
                    const double fSmall(fArrowsOutsideLen * 0.18);
317
0
                    fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
318
319
0
                    if(bMainLineSplitted)
320
0
                    {
321
0
                        fTextX -= (fArrowsOutsideLen - fStartArrowH);
322
0
                    }
323
324
0
                    if(!rTextAttribute.isDefault())
325
0
                    {
326
0
                        fTextX -= rTextAttribute.getTextRightDistance();
327
0
                    }
328
0
                }
329
0
                else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
330
0
                {
331
                    // right
332
0
                    const double fSmall(fArrowsOutsideLen * 0.18);
333
0
                    fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
334
335
0
                    if(bMainLineSplitted)
336
0
                    {
337
0
                        fTextX += (fArrowsOutsideLen - fEndArrowH);
338
0
                    }
339
340
0
                    if(!rTextAttribute.isDefault())
341
0
                    {
342
0
                        fTextX += rTextAttribute.getTextLeftDistance();
343
0
                    }
344
0
                }
345
0
                else // MEASURETEXTPOSITION_CENTERED
346
0
                {
347
                    // centered
348
0
                    fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
349
350
0
                    if(!rTextAttribute.isDefault())
351
0
                    {
352
0
                        fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
353
0
                    }
354
0
                }
355
356
                // text vertical position
357
0
                if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
358
0
                {
359
                    // top
360
0
                    const double fSmall(fArrowsOutsideLen * 0.10);
361
0
                    fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
362
363
0
                    if(!rTextAttribute.isDefault())
364
0
                    {
365
0
                        fTextY -= rTextAttribute.getTextLowerDistance();
366
0
                    }
367
0
                }
368
0
                else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
369
0
                {
370
                    // bottom
371
0
                    const double fSmall(fArrowsOutsideLen * 0.10);
372
0
                    fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
373
374
0
                    if(!rTextAttribute.isDefault())
375
0
                    {
376
0
                        fTextY += rTextAttribute.getTextUpperDistance();
377
0
                    }
378
0
                }
379
0
                else // MEASURETEXTPOSITION_CENTERED
380
0
                {
381
                    // centered
382
0
                    fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
383
384
0
                    if(!rTextAttribute.isDefault())
385
0
                    {
386
0
                        fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
387
0
                    }
388
0
                }
389
0
            }
390
391
0
            if(getSdrLSTAttribute().getLine().isDefault())
392
0
            {
393
                // embed line geometry to invisible (100% transparent) line group for HitTest
394
0
                aRetval = Primitive2DContainer {
395
0
                    new HiddenGeometryPrimitive2D(std::move(aRetval))
396
0
                };
397
0
            }
398
399
0
            if(xBlockText.is())
400
0
            {
401
                // create transformation to text primitive end position
402
0
                basegfx::B2DHomMatrix aChange;
403
404
                // handle auto text rotation
405
0
                if(bAutoUpsideDown)
406
0
                {
407
0
                    aChange.rotate(M_PI);
408
0
                }
409
410
                // move from aTextRange.TopLeft to fTextX, fTextY
411
0
                aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
412
413
                // apply object matrix
414
0
                aChange *= aObjectMatrix;
415
416
                // apply to existing text primitive
417
0
                rtl::Reference<SdrTextPrimitive2D> pNewBlockText = xBlockText->createTransformedClone(aChange);
418
0
                OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
419
0
                xBlockText.clear();
420
421
                // add to local primitives
422
0
                aRetval.push_back(pNewBlockText);
423
0
            }
424
425
            // add shadow
426
0
            if(!getSdrLSTAttribute().getShadow().isDefault())
427
0
            {
428
0
                aRetval = createEmbeddedShadowPrimitive(
429
0
                    std::move(aRetval),
430
0
                    getSdrLSTAttribute().getShadow());
431
0
            }
432
433
0
            return new GroupPrimitive2D(std::move(aRetval));
434
0
        }
435
436
        SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
437
            const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute,
438
            const basegfx::B2DPoint& rStart,
439
            const basegfx::B2DPoint& rEnd,
440
            MeasureTextPosition eHorizontal,
441
            MeasureTextPosition eVertical,
442
            double fDistance,
443
            double fUpper,
444
            double fLower,
445
            double fLeftDelta,
446
            double fRightDelta,
447
            bool bBelow,
448
            bool bTextRotation,
449
            bool bTextAutoAngle)
450
0
        :   maSdrLSTAttribute(rSdrLSTAttribute),
451
0
            maStart(rStart),
452
0
            maEnd(rEnd),
453
0
            meHorizontal(eHorizontal),
454
0
            meVertical(eVertical),
455
0
            mfDistance(fDistance),
456
0
            mfUpper(fUpper),
457
0
            mfLower(fLower),
458
0
            mfLeftDelta(fLeftDelta),
459
0
            mfRightDelta(fRightDelta),
460
0
            mbBelow(bBelow),
461
0
            mbTextRotation(bTextRotation),
462
0
            mbTextAutoAngle(bTextAutoAngle)
463
0
        {
464
0
        }
465
466
        bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
467
0
        {
468
0
            if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
469
0
            {
470
0
                const SdrMeasurePrimitive2D& rCompare = static_cast<const SdrMeasurePrimitive2D&>(rPrimitive);
471
472
0
                return (getStart() == rCompare.getStart()
473
0
                    && getEnd() == rCompare.getEnd()
474
0
                    && getHorizontal() == rCompare.getHorizontal()
475
0
                    && getVertical() == rCompare.getVertical()
476
0
                    && getDistance() == rCompare.getDistance()
477
0
                    && getUpper() == rCompare.getUpper()
478
0
                    && getLower() == rCompare.getLower()
479
0
                    && getLeftDelta() == rCompare.getLeftDelta()
480
0
                    && getRightDelta() == rCompare.getRightDelta()
481
0
                    && getBelow() == rCompare.getBelow()
482
0
                    && getTextRotation() == rCompare.getTextRotation()
483
0
                    && getTextAutoAngle() == rCompare.getTextAutoAngle()
484
0
                    && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
485
0
            }
486
487
0
            return false;
488
0
        }
489
490
        // provide unique ID
491
        sal_uInt32 SdrMeasurePrimitive2D::getPrimitive2DID() const
492
0
        {
493
0
            return PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D;
494
0
        }
495
496
} // end of namespace
497
498
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */