Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svtools/source/control/scriptedtext.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 <svtools/scriptedtext.hxx>
21
#include <vector>
22
#include <rtl/ustring.hxx>
23
#include <vcl/outdev.hxx>
24
#include <vcl/font.hxx>
25
#include <tools/debug.hxx>
26
#include <tools/gen.hxx>
27
#include <com/sun/star/i18n/ScriptType.hpp>
28
#include <com/sun/star/i18n/XBreakIterator.hpp>
29
30
31
using namespace ::com::sun::star;
32
33
34
class SvtScriptedTextHelper_Impl
35
{
36
private:
37
    OutputDevice&               mrOutDevice;        /// The output device for drawing the text.
38
    vcl::Font                   maLatinFont;        /// The font for latin text portions.
39
    vcl::Font                   maAsianFont;        /// The font for asian text portions.
40
    vcl::Font                   maCmplxFont;        /// The font for complex text portions.
41
    vcl::Font                   maDefltFont;        /// The default font of the output device.
42
    OUString                    maText;             /// The text.
43
44
    std::vector< sal_Int32 >         maPosVec;           /// The start position of each text portion.
45
    std::vector< sal_Int16 >         maScriptVec;        /// The script type of each text portion.
46
    std::vector< sal_Int32 >         maWidthVec;         /// The output width of each text portion.
47
    Size                        maTextSize;         /// The size the text will take in the current output device.
48
49
                                /** Gets the font of the given script type. */
50
    const vcl::Font&            GetFont( sal_uInt16 _nScript ) const;
51
                                /** Sets a font on the output device depending on the script type. */
52
    void                 SetOutDevFont( sal_uInt16 _nScript )
53
0
                                    { mrOutDevice.SetFont( GetFont( _nScript ) ); }
54
                                /** Fills maPosVec with positions of all changes of script type.
55
                                    This method expects correctly initialized maPosVec and maScriptVec. */
56
    void                        CalculateSizes();
57
                                /** Fills maPosVec with positions of all changes of script type and
58
                                    maScriptVec with the script type of each portion. */
59
    void                        CalculateBreaks(
60
                                    const uno::Reference< i18n::XBreakIterator >& _xBreakIter );
61
62
public:
63
                                /** This constructor sets an output device and fonts for all script types. */
64
    explicit                    SvtScriptedTextHelper_Impl(
65
                                    OutputDevice& _rOutDevice );
66
67
                                /** Sets new fonts and recalculates the text width. */
68
    void                        SetFonts( vcl::Font const * _pLatinFont, vcl::Font const * _pAsianFont, vcl::Font const * _pCmplxFont );
69
                                /** Sets a new text and calculates all script breaks and the text width. */
70
    void                        SetText(
71
                                    const OUString& _rText,
72
                                    const uno::Reference< i18n::XBreakIterator >& _xBreakIter );
73
74
                                /** Returns a size struct containing the width and height of the text in the current output device. */
75
0
    const Size&                 GetTextSize() const { return maTextSize;}
76
77
                                /** Draws the text in the current output device. */
78
    void                        DrawText( const Point& _rPos );
79
};
80
81
82
SvtScriptedTextHelper_Impl::SvtScriptedTextHelper_Impl(
83
        OutputDevice& _rOutDevice ) :
84
0
    mrOutDevice( _rOutDevice ),
85
0
    maLatinFont( _rOutDevice.GetFont() ),
86
0
    maAsianFont( _rOutDevice.GetFont() ),
87
0
    maCmplxFont( _rOutDevice.GetFont() ),
88
0
    maDefltFont( _rOutDevice.GetFont() )
89
0
{
90
0
}
91
92
const vcl::Font& SvtScriptedTextHelper_Impl::GetFont( sal_uInt16 _nScript ) const
93
0
{
94
0
    switch( _nScript )
95
0
    {
96
0
        case i18n::ScriptType::LATIN:       return maLatinFont;
97
0
        case i18n::ScriptType::ASIAN:       return maAsianFont;
98
0
        case i18n::ScriptType::COMPLEX:     return maCmplxFont;
99
0
    }
100
0
    return maDefltFont;
101
0
}
102
103
void SvtScriptedTextHelper_Impl::CalculateSizes()
104
0
{
105
0
    maTextSize.setWidth(0);
106
0
    maTextSize.setHeight(0);
107
0
    auto popIt = mrOutDevice.ScopedPush(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
108
109
    // calculate text portion widths and total width
110
0
    maWidthVec.clear();
111
0
    if( !maPosVec.empty() )
112
0
    {
113
0
        DBG_ASSERT( maPosVec.size() - 1 == maScriptVec.size(),
114
0
            "SvtScriptedTextHelper_Impl::CalculateWidth - invalid std::vectors" );
115
116
0
        sal_Int32 nThisPos = maPosVec[ 0 ];
117
0
        sal_Int32 nNextPos;
118
0
        sal_Int32 nPosVecSize = maPosVec.size();
119
0
        sal_Int32 nPosVecIndex = 1;
120
121
0
        sal_Int16 nScript;
122
0
        sal_Int32 nScriptVecIndex = 0;
123
124
0
        sal_Int32 nCurrWidth;
125
126
0
        while( nPosVecIndex < nPosVecSize )
127
0
        {
128
0
            nNextPos = maPosVec[ nPosVecIndex++ ];
129
0
            nScript = maScriptVec[ nScriptVecIndex++ ];
130
131
0
            SetOutDevFont( nScript );
132
0
            nCurrWidth = mrOutDevice.GetTextWidth( maText, nThisPos, nNextPos - nThisPos );
133
0
            maWidthVec.push_back( nCurrWidth );
134
0
            maTextSize.AdjustWidth(nCurrWidth );
135
0
            nThisPos = nNextPos;
136
0
        }
137
0
    }
138
139
    // calculate maximum font height
140
0
    SetOutDevFont( i18n::ScriptType::LATIN );
141
0
    maTextSize.setHeight( std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() ) );
142
0
    SetOutDevFont( i18n::ScriptType::ASIAN );
143
0
    maTextSize.setHeight( std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() ) );
144
0
    SetOutDevFont( i18n::ScriptType::COMPLEX );
145
0
    maTextSize.setHeight( std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() ) );
146
0
}
147
148
void SvtScriptedTextHelper_Impl::CalculateBreaks( const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
149
0
{
150
0
    maPosVec.clear();
151
0
    maScriptVec.clear();
152
153
0
    DBG_ASSERT( _xBreakIter.is(), "SvtScriptedTextHelper_Impl::CalculateBreaks - no break iterator" );
154
155
0
    sal_Int32 nLen = maText.getLength();
156
0
    if( nLen )
157
0
    {
158
0
        if( _xBreakIter.is() )
159
0
        {
160
0
            sal_Int32 nThisPos = 0;         // first position of this portion
161
0
            sal_Int32 nNextPos = 0;         // first position of next portion
162
0
            sal_Int16 nPortScript;          // script type of this portion
163
0
            do
164
0
            {
165
0
                nPortScript = _xBreakIter->getScriptType( maText, nThisPos );
166
0
                nNextPos = _xBreakIter->endOfScript( maText, nThisPos, nPortScript );
167
168
0
                switch( nPortScript )
169
0
                {
170
0
                    case i18n::ScriptType::LATIN:
171
0
                    case i18n::ScriptType::ASIAN:
172
0
                    case i18n::ScriptType::COMPLEX:
173
0
                        maPosVec.push_back( nThisPos );
174
0
                        maScriptVec.push_back( nPortScript );
175
0
                    break;
176
0
                    default:
177
0
                    {
178
/* *** handling of weak characters ***
179
- first portion is weak: Use OutputDevice::HasGlyphs() to find the correct font
180
- weak portion follows another portion: Script type of preceding portion is used */
181
0
                        if( maPosVec.empty() )
182
0
                        {
183
0
                            sal_Int32 nCharIx = 0;
184
0
                            sal_Int32 nNextCharIx = 0;
185
0
                            sal_Int16 nScript;
186
0
                            do
187
0
                            {
188
0
                                nScript = i18n::ScriptType::LATIN;
189
0
                                while( (nScript != i18n::ScriptType::WEAK) && (nCharIx == nNextCharIx) )
190
0
                                {
191
0
                                    nNextCharIx = mrOutDevice.HasGlyphs( GetFont( nScript ), maText, nCharIx, nNextPos - nCharIx );
192
0
                                    if( nCharIx == nNextCharIx )
193
0
                                        ++nScript;
194
0
                                }
195
0
                                if( nNextCharIx == nCharIx )
196
0
                                    ++nNextCharIx;
197
198
0
                                maPosVec.push_back( nCharIx );
199
0
                                maScriptVec.push_back( nScript );
200
0
                                nCharIx = nNextCharIx;
201
0
                            }
202
0
                            while( nCharIx < nNextPos && nCharIx != -1 );
203
0
                        }
204
                        // nothing to do for following portions
205
0
                    }
206
0
                }
207
0
                nThisPos = nNextPos;
208
0
            }
209
0
            while( (0 <= nThisPos) && (nThisPos < nLen) );
210
0
        }
211
0
        else            // no break iterator: whole text LATIN
212
0
        {
213
0
            maPosVec.push_back( 0 );
214
0
            maScriptVec.push_back( i18n::ScriptType::LATIN );
215
0
        }
216
217
        // push end position of last portion
218
0
        if( !maPosVec.empty() )
219
0
            maPosVec.push_back( nLen );
220
0
    }
221
0
    CalculateSizes();
222
0
}
223
224
void SvtScriptedTextHelper_Impl::SetFonts( vcl::Font const * _pLatinFont, vcl::Font const * _pAsianFont, vcl::Font const * _pCmplxFont )
225
0
{
226
0
    maLatinFont = _pLatinFont ? *_pLatinFont : maDefltFont;
227
0
    maAsianFont = _pAsianFont ? *_pAsianFont : maDefltFont;
228
0
    maCmplxFont = _pCmplxFont ? *_pCmplxFont : maDefltFont;
229
0
    CalculateSizes();
230
0
}
231
232
void SvtScriptedTextHelper_Impl::SetText( const OUString& _rText, const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
233
0
{
234
0
    maText = _rText;
235
0
    CalculateBreaks( _xBreakIter );
236
0
}
237
238
239
void SvtScriptedTextHelper_Impl::DrawText( const Point& _rPos )
240
0
{
241
0
    if( maText.isEmpty() || maPosVec.empty() )
242
0
        return;
243
244
0
    DBG_ASSERT( maPosVec.size() - 1 == maScriptVec.size(), "SvtScriptedTextHelper_Impl::DrawText - invalid std::vectors" );
245
0
    DBG_ASSERT( maScriptVec.size() == maWidthVec.size(), "SvtScriptedTextHelper_Impl::DrawText - invalid std::vectors" );
246
247
0
    auto popIt = mrOutDevice.ScopedPush(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
248
249
0
    Point aCurrPos( _rPos );
250
0
    sal_Int32 nThisPos = maPosVec[ 0 ];
251
0
    sal_Int32 nNextPos;
252
0
    sal_Int32 nPosVecSize = maPosVec.size();
253
0
    sal_Int32 nPosVecIndex = 1;
254
255
0
    sal_Int16 nScript;
256
0
    sal_Int32 nVecIndex = 0;
257
258
0
    while( nPosVecIndex < nPosVecSize )
259
0
    {
260
0
        nNextPos = maPosVec[ nPosVecIndex++ ];
261
0
        nScript = maScriptVec[ nVecIndex ];
262
0
        vcl::Font aFont = GetFont( nScript );
263
0
        mrOutDevice.SetFont( aFont );
264
0
        if (aFont.GetColor() == COL_AUTO)
265
0
            mrOutDevice.SetTextColor( mrOutDevice.GetFillColor().IsDark() ? COL_WHITE : COL_BLACK);
266
0
        mrOutDevice.DrawText( aCurrPos, maText, nThisPos, nNextPos - nThisPos );
267
0
        aCurrPos.AdjustX(maWidthVec[ nVecIndex++ ] );
268
0
        aCurrPos.AdjustX(mrOutDevice.GetTextHeight() / 5 );   // add 20% of font height as portion spacing
269
0
        nThisPos = nNextPos;
270
0
    }
271
0
}
272
273
274
SvtScriptedTextHelper::SvtScriptedTextHelper( OutputDevice& _rOutDevice ) :
275
0
    mpImpl( new SvtScriptedTextHelper_Impl( _rOutDevice ) )
276
0
{
277
0
}
278
279
SvtScriptedTextHelper::SvtScriptedTextHelper( const SvtScriptedTextHelper& _rCopy ) :
280
0
    mpImpl( new SvtScriptedTextHelper_Impl( *_rCopy.mpImpl ) )
281
0
{
282
0
}
283
284
SvtScriptedTextHelper::~SvtScriptedTextHelper()
285
0
{
286
0
}
287
288
void SvtScriptedTextHelper::SetFonts( vcl::Font const * _pLatinFont, vcl::Font const * _pAsianFont, vcl::Font const * _pCmplxFont )
289
0
{
290
0
    mpImpl->SetFonts( _pLatinFont, _pAsianFont, _pCmplxFont );
291
0
}
292
293
void SvtScriptedTextHelper::SetDefaultFont()
294
0
{
295
0
    mpImpl->SetFonts( nullptr, nullptr, nullptr );
296
0
}
297
298
void SvtScriptedTextHelper::SetText( const OUString& _rText, const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
299
0
{
300
0
    mpImpl->SetText( _rText, _xBreakIter );
301
0
}
302
303
const Size& SvtScriptedTextHelper::GetTextSize() const
304
0
{
305
0
    return mpImpl->GetTextSize();
306
0
}
307
308
void SvtScriptedTextHelper::DrawText( const Point& _rPos )
309
0
{
310
0
    mpImpl->DrawText( _rPos );
311
0
}
312
313
314
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */