Coverage Report

Created: 2026-02-14 09:37

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
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT))
117
0
        aFont.SetWeight(pItem->GetWeight());
118
0
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_WEIGHT))
119
0
        aCJKFont.SetWeight(pItem->GetWeight());
120
0
    if (const SvxWeightItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_WEIGHT))
121
0
        aCTLFont.SetWeight(pItem->GetWeight());
122
123
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE))
124
0
        aFont.SetItalic(pItem->GetPosture());
125
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_POSTURE))
126
0
        aCJKFont.SetItalic(pItem->GetPosture());
127
0
    if (const SvxPostureItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_POSTURE))
128
0
        aCTLFont.SetItalic(pItem->GetPosture());
129
130
0
    if (const SvxContourItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR))
131
0
    {
132
0
        auto aVal = pItem->GetValue();
133
0
        aFont.SetOutline(aVal);
134
0
        aCJKFont.SetOutline(aVal);
135
0
        aCTLFont.SetOutline(aVal);
136
0
    }
137
0
    if (const SvxShadowedItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED))
138
0
    {
139
0
        auto aVal = pItem->GetValue();
140
0
        aFont.SetShadow(aVal);
141
0
        aCJKFont.SetShadow(aVal);
142
0
        aCTLFont.SetShadow(aVal);
143
0
    }
144
0
    if (const SvxCharReliefItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF))
145
0
    {
146
0
        auto aVal = pItem->GetValue();
147
0
        aFont.SetRelief(aVal);
148
0
        aCJKFont.SetRelief(aVal);
149
0
        aCTLFont.SetRelief(aVal);
150
0
    }
151
0
    if (const SvxUnderlineItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE))
152
0
    {
153
0
        auto aVal = pItem->GetLineStyle();
154
0
        aFont.SetUnderline(aVal);
155
0
        aCJKFont.SetUnderline(aVal);
156
0
        aCTLFont.SetUnderline(aVal);
157
0
    }
158
0
    if (const SvxOverlineItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE))
159
0
    {
160
0
        auto aVal = pItem->GetValue();
161
0
        aFont.SetOverline(aVal);
162
0
        aCJKFont.SetOverline(aVal);
163
0
        aCTLFont.SetOverline(aVal);
164
0
    }
165
0
    if (const SvxCrossedOutItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT))
166
0
    {
167
0
        auto aVal = pItem->GetStrikeout();
168
0
        aFont.SetStrikeout(aVal);
169
0
        aCJKFont.SetStrikeout(aVal);
170
0
        aCTLFont.SetStrikeout(aVal);
171
0
    }
172
0
    if (const SvxCaseMapItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP))
173
0
    {
174
0
        auto aVal = pItem->GetCaseMap();
175
0
        aFont.SetCaseMap(aVal);
176
0
        aCJKFont.SetCaseMap(aVal);
177
0
        aCTLFont.SetCaseMap(aVal);
178
0
    }
179
0
    if (const SvxEmphasisMarkItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK))
180
0
    {
181
0
        auto aVal = pItem->GetEmphasisMark();
182
0
        aFont.SetEmphasisMark(aVal);
183
0
        aCJKFont.SetEmphasisMark(aVal);
184
0
        aCTLFont.SetEmphasisMark(aVal);
185
0
    }
186
0
    if (const SvxColorItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR))
187
0
    {
188
0
        maFontColor = pItem->GetValue();
189
0
    }
190
0
    if (const SvxBrushItem* pItem = pItemSet->GetItem(SID_ATTR_BRUSH_CHAR))
191
0
    {
192
0
        maHighlightColor = pItem->GetColor();
193
0
    }
194
195
0
    if (mpStyle->GetFamily() == SfxStyleFamily::Para)
196
0
    {
197
0
        if (const XFillStyleItem* pItem = pItemSet->GetItem(XATTR_FILLSTYLE))
198
0
        {
199
0
            css::drawing::FillStyle aFillStyle = pItem->GetValue();
200
0
            if (aFillStyle == drawing::FillStyle_SOLID)
201
0
            {
202
0
                if (const XFillColorItem* pFillColorItem = pItemSet->GetItem(XATTR_FILLCOLOR))
203
0
                {
204
0
                    maBackgroundColor = pFillColorItem->GetColorValue();
205
0
                }
206
0
            }
207
0
        }
208
0
    }
209
210
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_FONT, aFont) &&
211
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_FONTHEIGHT, aFont))
212
0
        m_oFont = aFont;
213
214
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_CJK_FONT, aCJKFont) &&
215
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, aCJKFont))
216
0
        m_oCJKFont = aCJKFont;
217
218
0
    if (SetFont(*pItemSet, SID_ATTR_CHAR_CTL_FONT, aCTLFont) &&
219
0
        SetFontSize(*pItemSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, aCTLFont))
220
0
        m_oCTLFont = aCTLFont;
221
222
0
    CheckScript();
223
0
    CalcRenderSize();
224
0
    return true;
225
0
}
226
227
void CommonStylePreviewRenderer::CalcRenderSize()
228
0
{
229
0
    const OUString& rText = maStyleName;
230
231
0
    mnBaseLine = 0;
232
0
    mnHeight = 0;
233
0
    SvtScriptType aScript;
234
0
    sal_uInt16 nIdx = 0;
235
0
    sal_Int32 nStart = 0;
236
0
    sal_Int32 nEnd;
237
0
    size_t nCnt = maScriptChanges.size();
238
239
0
    if (nCnt)
240
0
    {
241
0
        nEnd = maScriptChanges[nIdx].changePos;
242
0
        aScript = maScriptChanges[nIdx].scriptType;
243
0
    }
244
0
    else
245
0
    {
246
0
        nEnd = rText.getLength();
247
0
        aScript = SvtScriptType::LATIN;
248
0
    }
249
250
0
    do
251
0
    {
252
0
        auto oFont = (aScript == SvtScriptType::ASIAN) ?
253
0
                         m_oCJKFont :
254
0
                         ((aScript == SvtScriptType::COMPLEX) ?
255
0
                             m_oCTLFont :
256
0
                             m_oFont);
257
258
0
        mrOutputDev.Push(vcl::PushFlags::FONT);
259
260
0
        tools::Long nWidth;
261
0
        if (oFont)
262
0
        {
263
0
            mrOutputDev.SetFont(*oFont);
264
0
            nWidth = oFont->GetTextSize(mrOutputDev, rText, nStart, nEnd - nStart).Width();
265
0
        }
266
0
        else
267
0
            nWidth = mrOutputDev.GetTextWidth(rText, nStart, nEnd - nStart);
268
269
0
        tools::Rectangle aRect;
270
0
        mrOutputDev.GetTextBoundRect(aRect, rText, nStart, nStart, nEnd - nStart);
271
272
0
        mrOutputDev.Pop();
273
274
0
        mnBaseLine = std::max(mnBaseLine, -aRect.Top());
275
0
        mnHeight = std::max(mnHeight, aRect.GetHeight());
276
0
        if (nIdx >= maScriptChanges.size())
277
0
            break;
278
279
0
        maScriptChanges[nIdx++].textWidth = nWidth;
280
281
0
        if (nEnd < rText.getLength() && nIdx < nCnt)
282
0
        {
283
0
            nStart = nEnd;
284
0
            nEnd = maScriptChanges[nIdx].changePos;
285
0
            aScript = maScriptChanges[nIdx].scriptType;
286
0
        }
287
0
        else
288
0
            break;
289
0
    }
290
0
    while(true);
291
292
0
    double fRatio = 1;
293
0
    if (mnHeight > mnMaxHeight && mnHeight != 0)
294
0
        fRatio = double(mnMaxHeight) / mnHeight;
295
296
0
    mnHeight *= fRatio;
297
0
    mnBaseLine *= fRatio;
298
0
    if (fRatio != 1)
299
0
    {
300
0
        Size aFontSize;
301
0
        if (m_oFont)
302
0
        {
303
0
            aFontSize = m_oFont->GetFontSize();
304
0
            m_oFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
305
0
        }
306
0
        if (m_oCJKFont)
307
0
        {
308
0
            aFontSize = m_oCJKFont->GetFontSize();
309
0
            m_oCJKFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
310
0
        }
311
0
        if (m_oCTLFont)
312
0
        {
313
0
            aFontSize = m_oCTLFont->GetFontSize();
314
0
            m_oCTLFont->SetFontSize(Size(aFontSize.Width() * fRatio, aFontSize.Height() * fRatio));
315
0
        }
316
317
0
        for (auto& aChange : maScriptChanges)
318
0
            aChange.textWidth *= fRatio;
319
0
    }
320
0
}
321
322
bool CommonStylePreviewRenderer::render(const tools::Rectangle& aRectangle, RenderAlign eRenderAlign)
323
0
{
324
0
    const OUString& rText = maStyleName;
325
326
    // setup the device & draw
327
0
    auto popIt = mrOutputDev.ScopedPush(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTFILLCOLOR);
328
329
0
    if (maBackgroundColor != COL_AUTO)
330
0
    {
331
0
        mrOutputDev.SetFillColor(maBackgroundColor);
332
0
        mrOutputDev.DrawRect(aRectangle);
333
0
    }
334
335
0
    Point aFontDrawPosition = aRectangle.TopLeft();
336
0
    aFontDrawPosition.AdjustY(mnBaseLine);
337
0
    if (eRenderAlign == RenderAlign::CENTER)
338
0
    {
339
0
        if (aRectangle.GetHeight() > mnHeight)
340
0
            aFontDrawPosition.AdjustY((aRectangle.GetHeight() - mnHeight) / 2 );
341
0
    }
342
343
0
    SvtScriptType aScript;
344
0
    sal_uInt16 nIdx = 0;
345
0
    sal_Int32 nStart = 0;
346
0
    sal_Int32 nEnd;
347
0
    size_t nCnt = maScriptChanges.size();
348
0
    if (nCnt)
349
0
    {
350
0
        nEnd = maScriptChanges[nIdx].changePos;
351
0
        aScript = maScriptChanges[nIdx].scriptType;
352
0
    }
353
0
    else
354
0
    {
355
0
        nEnd = rText.getLength();
356
0
        aScript = SvtScriptType::LATIN;
357
0
    }
358
359
0
    do
360
0
    {
361
0
        auto oFont = (aScript == SvtScriptType::ASIAN)
362
0
                         ? m_oCJKFont
363
0
                         : ((aScript == SvtScriptType::COMPLEX)
364
0
                             ? m_oCTLFont
365
0
                             : m_oFont);
366
367
0
        mrOutputDev.Push(vcl::PushFlags::FONT);
368
369
0
        if (oFont)
370
0
            mrOutputDev.SetFont(*oFont);
371
372
0
        if (maFontColor != COL_AUTO)
373
0
            mrOutputDev.SetTextColor(maFontColor);
374
0
        else
375
0
        {
376
0
            if (maBackgroundColor != COL_AUTO)
377
0
               mrOutputDev.SetTextColor(maBackgroundColor.IsDark() ? COL_WHITE : COL_BLACK);
378
0
            else if (maHighlightColor != COL_AUTO)
379
0
               mrOutputDev.SetTextColor(maHighlightColor.IsDark() ? COL_WHITE : COL_BLACK);
380
0
        }
381
382
0
        if (maHighlightColor != COL_AUTO)
383
0
            mrOutputDev.SetTextFillColor(maHighlightColor);
384
385
0
        if (oFont)
386
0
            oFont->QuickDrawText(&mrOutputDev, aFontDrawPosition, rText, nStart, nEnd - nStart, {});
387
0
        else
388
0
            mrOutputDev.DrawText(aFontDrawPosition, rText, nStart, nEnd - nStart);
389
390
0
        mrOutputDev.Pop();
391
392
0
        aFontDrawPosition.AdjustX(maScriptChanges[nIdx++].textWidth);
393
0
        if (nEnd < rText.getLength() && nIdx < nCnt)
394
0
        {
395
0
            nStart = nEnd;
396
0
            nEnd = maScriptChanges[nIdx].changePos;
397
0
            aScript = maScriptChanges[nIdx].scriptType;
398
0
        }
399
0
        else
400
0
            break;
401
0
    }
402
0
    while(true);
403
404
0
    return true;
405
0
}
406
407
void CommonStylePreviewRenderer::CheckScript()
408
0
{
409
0
    assert(!maStyleName.isEmpty()); // must have a preview text here!
410
0
    if (maStyleName == maScriptText)
411
0
        return; // already initialized
412
413
0
    maScriptText = maStyleName;
414
0
    maScriptChanges.clear();
415
416
0
    auto aEditEngine = EditEngine(nullptr);
417
0
    aEditEngine.SetText(maScriptText);
418
419
0
    auto aScript = aEditEngine.GetScriptType({ 0, 0, 0, 0 });
420
0
    for (sal_Int32 i = 1; i <= maScriptText.getLength(); i++)
421
0
    {
422
0
        auto aNextScript = aEditEngine.GetScriptType({ 0, i, 0, i });
423
0
        if (aNextScript != aScript)
424
0
            maScriptChanges.emplace_back(aScript, i - 1);
425
0
        if (i == maScriptText.getLength())
426
0
            maScriptChanges.emplace_back(aScript, i);
427
0
        aScript = aNextScript;
428
0
    }
429
0
}
430
431
} // end svx namespace
432
433
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */