Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/styles/CommonStylePreviewRenderer.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
 */
10
11
#include <CommonStylePreviewRenderer.hxx>
12
13
#include <sfx2/objsh.hxx>
14
#include <svl/style.hxx>
15
#include <svl/itemset.hxx>
16
#include <svl/itempool.hxx>
17
#include <vcl/metric.hxx>
18
#include <vcl/outdev.hxx>
19
20
#include <com/sun/star/drawing/FillStyle.hpp>
21
#include <svx/xdef.hxx>
22
#include <svx/xfillit0.hxx>
23
#include <svx/xflclit.hxx>
24
#include <editeng/brushitem.hxx>
25
#include <editeng/fontitem.hxx>
26
#include <editeng/fhgtitem.hxx>
27
#include <editeng/charreliefitem.hxx>
28
#include <editeng/contouritem.hxx>
29
#include <editeng/colritem.hxx>
30
#include <editeng/crossedoutitem.hxx>
31
#include <editeng/editeng.hxx>
32
#include <editeng/emphasismarkitem.hxx>
33
#include <editeng/postitem.hxx>
34
#include <editeng/shdditem.hxx>
35
#include <editeng/udlnitem.hxx>
36
#include <editeng/wghtitem.hxx>
37
#include <editeng/svxfont.hxx>
38
#include <editeng/cmapitem.hxx>
39
40
#include <editeng/editids.hrc>
41
42
using namespace css;
43
44
namespace svx
45
{
46
47
CommonStylePreviewRenderer::CommonStylePreviewRenderer(
48
                                const SfxObjectShell& rShell, OutputDevice& rOutputDev,
49
                                SfxStyleSheetBase* pStyle, tools::Long nMaxHeight)
50
0
    : StylePreviewRenderer(rShell, rOutputDev, pStyle, nMaxHeight)
51
0
    , maFontColor(COL_AUTO)
52
0
    , maHighlightColor(COL_AUTO)
53
0
    , maBackgroundColor(COL_AUTO)
54
0
    , mnHeight(0)
55
0
    , mnBaseLine(0)
56
0
    , maStyleName(mpStyle->GetName())
57
0
{
58
0
}
59
60
CommonStylePreviewRenderer::~CommonStylePreviewRenderer()
61
0
{}
62
63
static bool GetWhich(const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich)
64
0
{
65
0
    rWhich = rSet.GetPool()->GetWhichIDFromSlotID(nSlot);
66
0
    return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT;
67
0
}
68
69
static bool SetFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
70
0
{
71
0
    sal_uInt16 nWhich;
72
0
    if (GetWhich(rSet, nSlot, nWhich))
73
0
    {
74
0
        const auto& rFontItem = static_cast<const SvxFontItem&>(rSet.Get(nWhich));
75
0
        rFont.SetFamily(rFontItem.GetFamily());
76
0
        rFont.SetFamilyName(rFontItem.GetFamilyName());
77
0
        rFont.SetPitch(rFontItem.GetPitch());
78
0
        rFont.SetCharSet(rFontItem.GetCharSet());
79
0
        rFont.SetStyleName(rFontItem.GetStyleName());
80
0
        rFont.SetAlignment(ALIGN_BASELINE);
81
0
        return true;
82
0
    }
83
0
    return false;
84
0
}
85
86
bool CommonStylePreviewRenderer::SetFontSize(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
87
0
{
88
0
    sal_uInt16 nWhich;
89
0
    if (GetWhich(rSet, nSlot, nWhich))
90
0
    {
91
0
        const auto& rFontHeightItem = static_cast<const SvxFontHeightItem&>(rSet.Get(nWhich));
92
0
        Size aFontSize(0, rFontHeightItem.GetHeight());
93
0
        aFontSize = mrOutputDev.LogicToPixel(aFontSize, MapMode(mrShell.GetMapUnit()));
94
0
        rFont.SetFontSize(aFontSize);
95
0
        mrOutputDev.SetFont(rFont);
96
0
        FontMetric aMetric(mrOutputDev.GetFontMetric());
97
0
        return true;
98
0
    }
99
0
    return false;
100
0
}
101
102
bool CommonStylePreviewRenderer::recalculate()
103
0
{
104
0
    m_oFont.reset();
105
0
    m_oCJKFont.reset();
106
0
    m_oCTLFont.reset();
107
108
0
    std::optional<SfxItemSet> pItemSet(mpStyle->GetItemSetForPreview());
109
110
0
    if (!pItemSet) return false;
111
112
0
    SvxFont aFont;
113
0
    SvxFont aCJKFont;
114
0
    SvxFont aCTLFont;
115
116
0
    const SfxPoolItem* pItem;
117
118
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT)) != nullptr)
119
0
        aFont.SetWeight(static_cast<const SvxWeightItem*>(pItem)->GetWeight());
120
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_WEIGHT)) != nullptr)
121
0
        aCJKFont.SetWeight(static_cast<const SvxWeightItem*>(pItem)->GetWeight());
122
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_WEIGHT)) != nullptr)
123
0
        aCTLFont.SetWeight(static_cast<const SvxWeightItem*>(pItem)->GetWeight());
124
125
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE)) != nullptr)
126
0
        aFont.SetItalic(static_cast<const SvxPostureItem*>(pItem)->GetPosture());
127
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_POSTURE)) != nullptr)
128
0
        aCJKFont.SetItalic(static_cast<const SvxPostureItem*>(pItem)->GetPosture());
129
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_POSTURE)) != nullptr)
130
0
        aCTLFont.SetItalic(static_cast<const SvxPostureItem*>(pItem)->GetPosture());
131
132
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR)) != nullptr)
133
0
    {
134
0
        auto aVal = static_cast<const SvxContourItem*>(pItem)->GetValue();
135
0
        aFont.SetOutline(aVal);
136
0
        aCJKFont.SetOutline(aVal);
137
0
        aCTLFont.SetOutline(aVal);
138
0
    }
139
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED)) != nullptr)
140
0
    {
141
0
        auto aVal = static_cast<const SvxShadowedItem*>(pItem)->GetValue();
142
0
        aFont.SetShadow(aVal);
143
0
        aCJKFont.SetShadow(aVal);
144
0
        aCTLFont.SetShadow(aVal);
145
0
    }
146
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF)) != nullptr)
147
0
    {
148
0
        auto aVal = static_cast<const SvxCharReliefItem*>(pItem)->GetValue();
149
0
        aFont.SetRelief(aVal);
150
0
        aCJKFont.SetRelief(aVal);
151
0
        aCTLFont.SetRelief(aVal);
152
0
    }
153
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE)) != nullptr)
154
0
    {
155
0
        auto aVal = static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle();
156
0
        aFont.SetUnderline(aVal);
157
0
        aCJKFont.SetUnderline(aVal);
158
0
        aCTLFont.SetUnderline(aVal);
159
0
    }
160
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE)) != nullptr)
161
0
    {
162
0
        auto aVal = static_cast<const SvxOverlineItem*>(pItem)->GetValue();
163
0
        aFont.SetOverline(aVal);
164
0
        aCJKFont.SetOverline(aVal);
165
0
        aCTLFont.SetOverline(aVal);
166
0
    }
167
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT)) != nullptr)
168
0
    {
169
0
        auto aVal = static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout();
170
0
        aFont.SetStrikeout(aVal);
171
0
        aCJKFont.SetStrikeout(aVal);
172
0
        aCTLFont.SetStrikeout(aVal);
173
0
    }
174
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP)) != nullptr)
175
0
    {
176
0
        auto aVal = static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap();
177
0
        aFont.SetCaseMap(aVal);
178
0
        aCJKFont.SetCaseMap(aVal);
179
0
        aCTLFont.SetCaseMap(aVal);
180
0
    }
181
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK)) != nullptr)
182
0
    {
183
0
        auto aVal = static_cast<const SvxEmphasisMarkItem*>(pItem)->GetEmphasisMark();
184
0
        aFont.SetEmphasisMark(aVal);
185
0
        aCJKFont.SetEmphasisMark(aVal);
186
0
        aCTLFont.SetEmphasisMark(aVal);
187
0
    }
188
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR)) != nullptr)
189
0
    {
190
0
        maFontColor = static_cast<const SvxColorItem*>(pItem)->GetValue();
191
0
    }
192
0
    if ((pItem = pItemSet->GetItem(SID_ATTR_BRUSH_CHAR)) != nullptr)
193
0
    {
194
0
        maHighlightColor = static_cast<const SvxBrushItem*>(pItem)->GetColor();
195
0
    }
196
197
0
    if (mpStyle->GetFamily() == SfxStyleFamily::Para)
198
0
    {
199
0
        if ((pItem = pItemSet->GetItem(XATTR_FILLSTYLE)) != nullptr)
200
0
        {
201
0
            css::drawing::FillStyle aFillStyle = static_cast<const XFillStyleItem*>(pItem)->GetValue();
202
0
            if (aFillStyle == drawing::FillStyle_SOLID)
203
0
            {
204
0
                if ((pItem = pItemSet->GetItem(XATTR_FILLCOLOR)) != nullptr)
205
0
                {
206
0
                    maBackgroundColor = static_cast<const XFillColorItem*>(pItem)->GetColorValue();
207
0
                }
208
0
            }
209
0
        }
210
0
    }
211
212
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_FONT, aFont) &&
213
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_FONTHEIGHT, aFont))
214
0
        m_oFont = aFont;
215
216
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_CJK_FONT, aCJKFont) &&
217
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, aCJKFont))
218
0
        m_oCJKFont = aCJKFont;
219
220
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_CTL_FONT, aCTLFont) &&
221
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, aCTLFont))
222
0
        m_oCTLFont = aCTLFont;
223
224
0
    CheckScript();
225
0
    CalcRenderSize();
226
0
    return true;
227
0
}
228
229
void CommonStylePreviewRenderer::CalcRenderSize()
230
0
{
231
0
    const OUString& rText = maStyleName;
232
233
0
    mnBaseLine = 0;
234
0
    mnHeight = 0;
235
0
    SvtScriptType aScript;
236
0
    sal_uInt16 nIdx = 0;
237
0
    sal_Int32 nStart = 0;
238
0
    sal_Int32 nEnd;
239
0
    size_t nCnt = maScriptChanges.size();
240
241
0
    if (nCnt)
242
0
    {
243
0
        nEnd = maScriptChanges[nIdx].changePos;
244
0
        aScript = maScriptChanges[nIdx].scriptType;
245
0
    }
246
0
    else
247
0
    {
248
0
        nEnd = rText.getLength();
249
0
        aScript = SvtScriptType::LATIN;
250
0
    }
251
252
0
    do
253
0
    {
254
0
        auto oFont = (aScript == SvtScriptType::ASIAN) ?
255
0
                         m_oCJKFont :
256
0
                         ((aScript == SvtScriptType::COMPLEX) ?
257
0
                             m_oCTLFont :
258
0
                             m_oFont);
259
260
0
        mrOutputDev.Push(vcl::PushFlags::FONT);
261
262
0
        tools::Long nWidth;
263
0
        if (oFont)
264
0
        {
265
0
            mrOutputDev.SetFont(*oFont);
266
0
            nWidth = oFont->GetTextSize(mrOutputDev, rText, nStart, nEnd - nStart).Width();
267
0
        }
268
0
        else
269
0
            nWidth = mrOutputDev.GetTextWidth(rText, nStart, nEnd - nStart);
270
271
0
        tools::Rectangle aRect;
272
0
        mrOutputDev.GetTextBoundRect(aRect, rText, nStart, nStart, nEnd - nStart);
273
274
0
        mrOutputDev.Pop();
275
276
0
        mnBaseLine = std::max(mnBaseLine, -aRect.Top());
277
0
        mnHeight = std::max(mnHeight, aRect.GetHeight());
278
0
        if (nIdx >= maScriptChanges.size())
279
0
            break;
280
281
0
        maScriptChanges[nIdx++].textWidth = nWidth;
282
283
0
        if (nEnd < rText.getLength() && nIdx < nCnt)
284
0
        {
285
0
            nStart = nEnd;
286
0
            nEnd = maScriptChanges[nIdx].changePos;
287
0
            aScript = maScriptChanges[nIdx].scriptType;
288
0
        }
289
0
        else
290
0
            break;
291
0
    }
292
0
    while(true);
293
294
0
    double fRatio = 1;
295
0
    if (mnHeight > mnMaxHeight && mnHeight != 0)
296
0
        fRatio = double(mnMaxHeight) / mnHeight;
297
298
0
    mnHeight *= fRatio;
299
0
    mnBaseLine *= fRatio;
300
0
    if (fRatio != 1)
301
0
    {
302
0
        Size aFontSize;
303
0
        if (m_oFont)
304
0
        {
305
0
            aFontSize = m_oFont->GetFontSize();
306
0
            m_oFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
307
0
        }
308
0
        if (m_oCJKFont)
309
0
        {
310
0
            aFontSize = m_oCJKFont->GetFontSize();
311
0
            m_oCJKFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
312
0
        }
313
0
        if (m_oCTLFont)
314
0
        {
315
0
            aFontSize = m_oCTLFont->GetFontSize();
316
0
            m_oCTLFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
317
0
        }
318
319
0
        for (auto& aChange : maScriptChanges)
320
0
            aChange.textWidth *= fRatio;
321
0
    }
322
0
}
323
324
bool CommonStylePreviewRenderer::render(const tools::Rectangle& aRectangle, RenderAlign eRenderAlign)
325
0
{
326
0
    const OUString& rText = maStyleName;
327
328
    // setup the device & draw
329
0
    auto popIt = mrOutputDev.ScopedPush(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTFILLCOLOR);
330
331
0
    if (maBackgroundColor != COL_AUTO)
332
0
    {
333
0
        mrOutputDev.SetFillColor(maBackgroundColor);
334
0
        mrOutputDev.DrawRect(aRectangle);
335
0
    }
336
337
0
    Point aFontDrawPosition = aRectangle.TopLeft();
338
0
    aFontDrawPosition.AdjustY(mnBaseLine);
339
0
    if (eRenderAlign == RenderAlign::CENTER)
340
0
    {
341
0
        if (aRectangle.GetHeight() > mnHeight)
342
0
            aFontDrawPosition.AdjustY((aRectangle.GetHeight() - mnHeight) / 2 );
343
0
    }
344
345
0
    SvtScriptType aScript;
346
0
    sal_uInt16 nIdx = 0;
347
0
    sal_Int32 nStart = 0;
348
0
    sal_Int32 nEnd;
349
0
    size_t nCnt = maScriptChanges.size();
350
0
    if (nCnt)
351
0
    {
352
0
        nEnd = maScriptChanges[nIdx].changePos;
353
0
        aScript = maScriptChanges[nIdx].scriptType;
354
0
    }
355
0
    else
356
0
    {
357
0
        nEnd = rText.getLength();
358
0
        aScript = SvtScriptType::LATIN;
359
0
    }
360
361
0
    do
362
0
    {
363
0
        auto oFont = (aScript == SvtScriptType::ASIAN)
364
0
                         ? m_oCJKFont
365
0
                         : ((aScript == SvtScriptType::COMPLEX)
366
0
                             ? m_oCTLFont
367
0
                             : m_oFont);
368
369
0
        mrOutputDev.Push(vcl::PushFlags::FONT);
370
371
0
        if (oFont)
372
0
            mrOutputDev.SetFont(*oFont);
373
374
0
        if (maFontColor != COL_AUTO)
375
0
            mrOutputDev.SetTextColor(maFontColor);
376
0
        else
377
0
        {
378
0
            if (maBackgroundColor != COL_AUTO)
379
0
               mrOutputDev.SetTextColor(maBackgroundColor.IsDark() ? COL_WHITE : COL_BLACK);
380
0
            else if (maHighlightColor != COL_AUTO)
381
0
               mrOutputDev.SetTextColor(maHighlightColor.IsDark() ? COL_WHITE : COL_BLACK);
382
0
        }
383
384
0
        if (maHighlightColor != COL_AUTO)
385
0
            mrOutputDev.SetTextFillColor(maHighlightColor);
386
387
0
        if (oFont)
388
0
            oFont->QuickDrawText(&mrOutputDev, aFontDrawPosition, rText, nStart, nEnd - nStart, {});
389
0
        else
390
0
            mrOutputDev.DrawText(aFontDrawPosition, rText, nStart, nEnd - nStart);
391
392
0
        mrOutputDev.Pop();
393
394
0
        aFontDrawPosition.AdjustX(maScriptChanges[nIdx++].textWidth);
395
0
        if (nEnd < rText.getLength() && nIdx < nCnt)
396
0
        {
397
0
            nStart = nEnd;
398
0
            nEnd = maScriptChanges[nIdx].changePos;
399
0
            aScript = maScriptChanges[nIdx].scriptType;
400
0
        }
401
0
        else
402
0
            break;
403
0
    }
404
0
    while(true);
405
406
0
    return true;
407
0
}
408
409
void CommonStylePreviewRenderer::CheckScript()
410
0
{
411
0
    assert(!maStyleName.isEmpty()); // must have a preview text here!
412
0
    if (maStyleName == maScriptText)
413
0
        return; // already initialized
414
415
0
    maScriptText = maStyleName;
416
0
    maScriptChanges.clear();
417
418
0
    auto aEditEngine = EditEngine(nullptr);
419
0
    aEditEngine.SetText(maScriptText);
420
421
0
    auto aScript = aEditEngine.GetScriptType({ 0, 0, 0, 0 });
422
0
    for (sal_Int32 i = 1; i <= maScriptText.getLength(); i++)
423
0
    {
424
0
        auto aNextScript = aEditEngine.GetScriptType({ 0, i, 0, i });
425
0
        if (aNextScript != aScript)
426
0
            maScriptChanges.emplace_back(aScript, i - 1);
427
0
        if (i == maScriptText.getLength())
428
0
            maScriptChanges.emplace_back(aScript, i);
429
0
        aScript = aNextScript;
430
0
    }
431
0
}
432
433
} // end svx namespace
434
435
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */