Coverage Report

Created: 2026-06-30 11:14

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
#include <vcl/svapp.hxx>
43
44
using namespace css;
45
46
namespace svx
47
{
48
49
CommonStylePreviewRenderer::CommonStylePreviewRenderer(
50
                                const SfxObjectShell& rShell, OutputDevice& rOutputDev,
51
                                SfxStyleSheetBase* pStyle, tools::Long nMaxHeight)
52
0
    : StylePreviewRenderer(rShell, rOutputDev, pStyle, nMaxHeight)
53
0
    , maFontColor(COL_AUTO)
54
0
    , maHighlightColor(COL_AUTO)
55
0
    , maBackgroundColor(COL_AUTO)
56
0
    , mnHeight(0)
57
0
    , mnBaseLine(0)
58
0
    , maStyleName(mpStyle->GetName())
59
0
{
60
0
}
61
62
CommonStylePreviewRenderer::~CommonStylePreviewRenderer()
63
0
{}
64
65
static bool GetWhich(const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich)
66
0
{
67
0
    rWhich = rSet.GetPool()->GetWhichIDFromSlotID(nSlot);
68
0
    return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT;
69
0
}
70
71
static bool SetFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
72
0
{
73
0
    sal_uInt16 nWhich;
74
0
    if (GetWhich(rSet, nSlot, nWhich))
75
0
    {
76
0
        const auto& rFontItem = static_cast<const SvxFontItem&>(rSet.Get(nWhich));
77
0
        rFont.SetFamily(rFontItem.GetFamily());
78
0
        rFont.SetFamilyName(rFontItem.GetFamilyName());
79
0
        rFont.SetPitch(rFontItem.GetPitch());
80
0
        rFont.SetCharSet(rFontItem.GetCharSet());
81
0
        rFont.SetStyleName(rFontItem.GetStyleName());
82
0
        rFont.SetAlignment(ALIGN_BASELINE);
83
0
        return true;
84
0
    }
85
0
    return false;
86
0
}
87
88
bool CommonStylePreviewRenderer::SetFontSize(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
89
0
{
90
0
    sal_uInt16 nWhich;
91
0
    if (GetWhich(rSet, nSlot, nWhich))
92
0
    {
93
0
        const auto& rFontHeightItem = static_cast<const SvxFontHeightItem&>(rSet.Get(nWhich));
94
0
        Size aFontSize(0, rFontHeightItem.GetHeight());
95
0
        aFontSize = mrOutputDev.LogicToPixel(aFontSize, MapMode(mrShell.GetMapUnit()));
96
0
        rFont.SetFontSize(aFontSize);
97
0
        mrOutputDev.SetFont(rFont);
98
0
        FontMetric aMetric(mrOutputDev.GetFontMetric());
99
0
        return true;
100
0
    }
101
0
    return false;
102
0
}
103
104
bool CommonStylePreviewRenderer::recalculate()
105
0
{
106
0
    m_oFont.reset();
107
0
    m_oCJKFont.reset();
108
0
    m_oCTLFont.reset();
109
110
0
    std::optional<SfxItemSet> pItemSet(mpStyle->GetItemSetForPreview());
111
112
0
    if (!pItemSet) return false;
113
114
0
    SvxFont aFont;
115
0
    SvxFont aCJKFont;
116
0
    SvxFont aCTLFont;
117
118
0
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT))
119
0
        aFont.SetWeight(pItem->GetWeight());
120
0
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_WEIGHT))
121
0
        aCJKFont.SetWeight(pItem->GetWeight());
122
0
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_WEIGHT))
123
0
        aCTLFont.SetWeight(pItem->GetWeight());
124
125
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE))
126
0
        aFont.SetItalic(pItem->GetPosture());
127
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_POSTURE))
128
0
        aCJKFont.SetItalic(pItem->GetPosture());
129
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_POSTURE))
130
0
        aCTLFont.SetItalic(pItem->GetPosture());
131
132
0
    if (const SvxContourItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR))
133
0
    {
134
0
        auto aVal = pItem->GetValue();
135
0
        aFont.SetOutline(aVal);
136
0
        aCJKFont.SetOutline(aVal);
137
0
        aCTLFont.SetOutline(aVal);
138
0
    }
139
0
    if (const SvxShadowedItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED))
140
0
    {
141
0
        auto aVal = pItem->GetValue();
142
0
        aFont.SetShadow(aVal);
143
0
        aCJKFont.SetShadow(aVal);
144
0
        aCTLFont.SetShadow(aVal);
145
0
    }
146
0
    if (const SvxCharReliefItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF))
147
0
    {
148
0
        auto aVal = pItem->GetValue();
149
0
        aFont.SetRelief(aVal);
150
0
        aCJKFont.SetRelief(aVal);
151
0
        aCTLFont.SetRelief(aVal);
152
0
    }
153
0
    if (const SvxUnderlineItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE))
154
0
    {
155
0
        auto aVal = pItem->GetLineStyle();
156
0
        aFont.SetUnderline(aVal);
157
0
        aCJKFont.SetUnderline(aVal);
158
0
        aCTLFont.SetUnderline(aVal);
159
0
    }
160
0
    if (const SvxOverlineItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE))
161
0
    {
162
0
        auto aVal = pItem->GetValue();
163
0
        aFont.SetOverline(aVal);
164
0
        aCJKFont.SetOverline(aVal);
165
0
        aCTLFont.SetOverline(aVal);
166
0
    }
167
0
    if (const SvxCrossedOutItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT))
168
0
    {
169
0
        auto aVal = pItem->GetStrikeout();
170
0
        aFont.SetStrikeout(aVal);
171
0
        aCJKFont.SetStrikeout(aVal);
172
0
        aCTLFont.SetStrikeout(aVal);
173
0
    }
174
0
    if (const SvxCaseMapItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP))
175
0
    {
176
0
        auto aVal = pItem->GetCaseMap();
177
0
        aFont.SetCaseMap(aVal);
178
0
        aCJKFont.SetCaseMap(aVal);
179
0
        aCTLFont.SetCaseMap(aVal);
180
0
    }
181
0
    if (const SvxEmphasisMarkItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK))
182
0
    {
183
0
        auto aVal = pItem->GetEmphasisMark();
184
0
        aFont.SetEmphasisMark(aVal);
185
0
        aCJKFont.SetEmphasisMark(aVal);
186
0
        aCTLFont.SetEmphasisMark(aVal);
187
0
    }
188
0
    if (const SvxColorItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR))
189
0
    {
190
0
        maFontColor = pItem->GetValue();
191
0
    }
192
0
    if (const SvxBrushItem* pItem = pItemSet->GetItem(SID_ATTR_BRUSH_CHAR))
193
0
    {
194
0
        maHighlightColor = pItem->GetColor();
195
0
    }
196
197
0
    if (mpStyle->GetFamily() == SfxStyleFamily::Para)
198
0
    {
199
0
        if (const XFillStyleItem* pItem = pItemSet->GetItem(XATTR_FILLSTYLE))
200
0
        {
201
0
            css::drawing::FillStyle aFillStyle = pItem->GetValue();
202
0
            if (aFillStyle == drawing::FillStyle_SOLID)
203
0
            {
204
0
                if (const XFillColorItem* pFillColorItem = pItemSet->GetItem(XATTR_FILLCOLOR))
205
0
                {
206
0
                    maBackgroundColor = pFillColorItem->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 (mpStyle->IsHidden())
375
0
            mrOutputDev.SetTextColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
376
0
        else if (maFontColor != COL_AUTO)
377
0
            mrOutputDev.SetTextColor(maFontColor);
378
0
        else
379
0
        {
380
0
            if (maBackgroundColor != COL_AUTO)
381
0
               mrOutputDev.SetTextColor(maBackgroundColor.IsDark() ? COL_WHITE : COL_BLACK);
382
0
            else if (maHighlightColor != COL_AUTO)
383
0
               mrOutputDev.SetTextColor(maHighlightColor.IsDark() ? COL_WHITE : COL_BLACK);
384
0
        }
385
386
0
        if (maHighlightColor != COL_AUTO)
387
0
            mrOutputDev.SetTextFillColor(maHighlightColor);
388
389
0
        if (oFont)
390
0
            oFont->QuickDrawText(&mrOutputDev, aFontDrawPosition, rText, nStart, nEnd - nStart, {});
391
0
        else
392
0
            mrOutputDev.DrawText(aFontDrawPosition, rText, nStart, nEnd - nStart);
393
394
0
        mrOutputDev.Pop();
395
396
0
        aFontDrawPosition.AdjustX(maScriptChanges[nIdx++].textWidth);
397
0
        if (nEnd < rText.getLength() && nIdx < nCnt)
398
0
        {
399
0
            nStart = nEnd;
400
0
            nEnd = maScriptChanges[nIdx].changePos;
401
0
            aScript = maScriptChanges[nIdx].scriptType;
402
0
        }
403
0
        else
404
0
            break;
405
0
    }
406
0
    while(true);
407
408
0
    return true;
409
0
}
410
411
void CommonStylePreviewRenderer::CheckScript()
412
0
{
413
0
    assert(!maStyleName.isEmpty()); // must have a preview text here!
414
0
    if (maStyleName == maScriptText)
415
0
        return; // already initialized
416
417
0
    maScriptText = maStyleName;
418
0
    maScriptChanges.clear();
419
420
0
    auto aEditEngine = EditEngine(nullptr);
421
0
    aEditEngine.SetText(maScriptText);
422
423
0
    auto aScript = aEditEngine.GetScriptType({ 0, 0, 0, 0 });
424
0
    for (sal_Int32 i = 1; i <= maScriptText.getLength(); i++)
425
0
    {
426
0
        auto aNextScript = aEditEngine.GetScriptType({ 0, i, 0, i });
427
0
        if (aNextScript != aScript)
428
0
            maScriptChanges.emplace_back(aScript, i - 1);
429
0
        if (i == maScriptText.getLength())
430
0
            maScriptChanges.emplace_back(aScript, i);
431
0
        aScript = aNextScript;
432
0
    }
433
0
}
434
435
} // end svx namespace
436
437
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */