Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/starmath/source/symbol.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 <symbol.hxx>
21
#include <utility.hxx>
22
#include <cfgitem.hxx>
23
#include <smmod.hxx>
24
#include <format.hxx>
25
#include <sal/log.hxx>
26
#include <osl/diagnose.h>
27
28
29
SmSym::SmSym() :
30
0
    m_aUiName(u"unknown"_ustr),
31
0
    m_aSetName(u"unknown"_ustr),
32
0
    m_cChar('\0'),
33
0
    m_bPredefined(false)
34
0
{
35
0
    m_aExportName = m_aUiName;
36
0
    m_aFace.SetTransparent(true);
37
0
    m_aFace.SetAlignment(ALIGN_BASELINE);
38
0
}
39
40
41
SmSym::SmSym(const SmSym& rSymbol)
42
0
{
43
0
    *this = rSymbol;
44
0
}
45
46
47
SmSym::SmSym(const OUString& rName, const vcl::Font& rFont, sal_UCS4 cChar,
48
             const OUString& rSet, bool bIsPredefined)
49
0
{
50
0
    m_aUiName   = m_aExportName   = rName;
51
52
0
    m_aFace     = SmFace(rFont);
53
0
    m_aFace.SetTransparent(true);
54
0
    m_aFace.SetAlignment(ALIGN_BASELINE);
55
56
0
    m_cChar         = cChar;
57
0
    m_aSetName      = rSet;
58
0
    m_bPredefined   = bIsPredefined;
59
0
}
60
61
62
SmSym& SmSym::operator = (const SmSym& rSymbol)
63
0
{
64
0
    m_aUiName       = rSymbol.m_aUiName;
65
0
    m_aExportName   = rSymbol.m_aExportName;
66
0
    m_cChar         = rSymbol.m_cChar;
67
0
    m_aFace         = rSymbol.m_aFace;
68
0
    m_aSetName      = rSymbol.m_aSetName;
69
0
    m_bPredefined   = rSymbol.m_bPredefined;
70
71
0
    SmModule::get()->GetSymbolManager().SetModified(true);
72
73
0
    return *this;
74
0
}
75
76
77
bool SmSym::IsEqualInUI( const SmSym& rSymbol ) const
78
0
{
79
0
    return  m_aUiName == rSymbol.m_aUiName &&
80
0
            m_aFace == rSymbol.m_aFace &&
81
0
            m_cChar == rSymbol.m_cChar;
82
0
}
83
84
const vcl::Font& SmSym::GetFace(const SmFormat* pFormat) const
85
0
{
86
0
    if (m_aFace.GetFamilyName().isEmpty())
87
0
    {
88
0
        if (!pFormat)
89
0
            pFormat = &SmModule::get()->GetConfig()->GetStandardFormat();
90
0
        return pFormat->GetFont(FNT_VARIABLE);
91
0
    }
92
0
    return m_aFace;
93
0
}
94
95
/**************************************************************************/
96
97
98
SmSymbolManager::SmSymbolManager()
99
1
{
100
1
    m_bModified     = false;
101
1
}
102
103
104
SmSymbolManager::SmSymbolManager(const SmSymbolManager& rSymbolSetManager)
105
0
{
106
0
    m_aSymbols      = rSymbolSetManager.m_aSymbols;
107
0
    m_bModified     = true;
108
0
}
109
110
111
SmSymbolManager::~SmSymbolManager()
112
0
{
113
0
}
114
115
116
SmSymbolManager& SmSymbolManager::operator = (const SmSymbolManager& rSymbolSetManager)
117
0
{
118
0
    m_aSymbols      = rSymbolSetManager.m_aSymbols;
119
0
    m_bModified     = true;
120
0
    return *this;
121
0
}
122
123
SmSym* SmSymbolManager::GetSymbolByName(std::u16string_view rSymbolName)
124
9.79k
{
125
9.79k
    SmSym* pRes = GetSymbolByUiName(rSymbolName);
126
9.79k
    if (!pRes)
127
9.79k
        pRes = GetSymbolByExportName(rSymbolName);
128
9.79k
    return pRes;
129
9.79k
}
130
131
SmSym *SmSymbolManager::GetSymbolByUiName(std::u16string_view rSymbolName)
132
9.79k
{
133
9.79k
    OUString aSymbolName(rSymbolName);
134
9.79k
    SmSym *pRes = nullptr;
135
9.79k
    SymbolMap_t::iterator aIt( m_aSymbols.find( aSymbolName ) );
136
9.79k
    if (aIt != m_aSymbols.end())
137
0
        pRes = &aIt->second;
138
9.79k
    return pRes;
139
9.79k
}
140
141
SmSym* SmSymbolManager::GetSymbolByExportName(std::u16string_view rSymbolName)
142
21.4k
{
143
21.4k
    SmSym* pRes = nullptr;
144
21.4k
    for (auto& rPair : m_aSymbols)
145
0
    {
146
0
        SmSym& rSymbol = rPair.second;
147
0
        if (rSymbol.GetExportName() == rSymbolName)
148
0
        {
149
0
            pRes = &rSymbol;
150
0
            break;
151
0
        }
152
0
    }
153
21.4k
    return pRes;
154
21.4k
}
155
156
157
SymbolPtrVec_t SmSymbolManager::GetSymbols() const
158
0
{
159
0
    SymbolPtrVec_t aRes;
160
0
    aRes.reserve(m_aSymbols.size());
161
0
    for (const auto& rEntry : m_aSymbols)
162
0
        aRes.push_back( &rEntry.second );
163
//    OSL_ENSURE( sSymbols.size() == m_aSymbols.size(), "number of symbols mismatch " );
164
0
    return aRes;
165
0
}
166
167
168
bool SmSymbolManager::AddOrReplaceSymbol( const SmSym &rSymbol, bool bForceChange )
169
0
{
170
0
    bool bAdded = false;
171
172
0
    const OUString& aSymbolName( rSymbol.GetUiName() );
173
0
    if (!aSymbolName.isEmpty() && !rSymbol.GetSymbolSetName().isEmpty())
174
0
    {
175
0
        const SmSym *pFound = GetSymbolByUiName( aSymbolName );
176
0
        const bool bSymbolConflict = pFound && !pFound->IsEqualInUI( rSymbol );
177
178
        // avoid having the same symbol name twice but with different symbols in use
179
0
        if (!pFound || bForceChange)
180
0
        {
181
0
            m_aSymbols[ aSymbolName ] = rSymbol;
182
0
            bAdded = true;
183
0
        }
184
0
        else if (bSymbolConflict)
185
0
        {
186
            // TODO: to solve this a document owned symbol manager would be required ...
187
0
                SAL_WARN("starmath", "symbol conflict, different symbol with same name found!");
188
            // symbols in all formulas. A copy of the global one would be needed here
189
            // and then the new symbol has to be forcefully applied. This would keep
190
            // the current formula intact but will leave the set of symbols in the
191
            // global symbol manager somewhat to chance.
192
0
        }
193
194
0
        OSL_ENSURE( bAdded, "failed to add symbol" );
195
0
        if (bAdded)
196
0
            m_bModified = true;
197
0
        OSL_ENSURE( bAdded || (pFound && !bSymbolConflict), "AddOrReplaceSymbol: unresolved symbol conflict" );
198
0
    }
199
200
0
    return bAdded;
201
0
}
202
203
204
void SmSymbolManager::RemoveSymbol( const OUString & rSymbolName )
205
0
{
206
0
    if (!rSymbolName.isEmpty())
207
0
    {
208
0
        size_t nOldSize = m_aSymbols.size();
209
0
        m_aSymbols.erase( rSymbolName );
210
0
        m_bModified = nOldSize != m_aSymbols.size();
211
0
    }
212
0
}
213
214
215
std::set< OUString > SmSymbolManager::GetSymbolSetNames() const
216
0
{
217
0
    std::set< OUString >  aRes;
218
0
    for (const auto& rEntry : m_aSymbols)
219
0
        aRes.insert( rEntry.second.GetSymbolSetName() );
220
0
    return aRes;
221
0
}
222
223
224
SymbolPtrVec_t SmSymbolManager::GetSymbolSet( std::u16string_view rSymbolSetName )
225
1
{
226
1
    SymbolPtrVec_t aRes;
227
1
    if (!rSymbolSetName.empty())
228
1
    {
229
1
        for (const auto& rEntry : m_aSymbols)
230
0
        {
231
0
            if (rEntry.second.GetSymbolSetName() == rSymbolSetName)
232
0
                aRes.push_back( &rEntry.second );
233
0
        }
234
1
    }
235
1
    return aRes;
236
1
}
237
238
239
void SmSymbolManager::Load()
240
1
{
241
1
    std::vector< SmSym > aSymbols;
242
1
    SmModule::get()->GetConfig()->GetSymbols(aSymbols);
243
1
    size_t nSymbolCount = aSymbols.size();
244
245
1
    m_aSymbols.clear();
246
1
    for (size_t i = 0;  i < nSymbolCount;  ++i)
247
0
    {
248
0
        const SmSym &rSym = aSymbols[i];
249
0
        OSL_ENSURE( !rSym.GetUiName().isEmpty(), "symbol without name!" );
250
0
        if (!rSym.GetUiName().isEmpty())
251
0
            AddOrReplaceSymbol( rSym );
252
0
    }
253
1
    m_bModified = true;
254
255
1
    if (0 == nSymbolCount)
256
1
    {
257
1
        SAL_WARN("starmath", "no symbol set found");
258
1
        m_bModified = false;
259
1
    }
260
261
    // now add a %i... symbol to the 'iGreek' set for every symbol found in the 'Greek' set.
262
1
    const OUString aGreekSymbolSetName(SmLocalizedSymbolData::GetUiSymbolSetName(u"Greek"));
263
1
    const SymbolPtrVec_t    aGreekSymbols( GetSymbolSet( aGreekSymbolSetName ) );
264
1
    OUString aSymbolSetName = "i" + aGreekSymbolSetName;
265
1
    size_t nSymbols = aGreekSymbols.size();
266
1
    for (size_t i = 0;  i < nSymbols;  ++i)
267
0
    {
268
        // make the new symbol a copy but with ITALIC_NORMAL, and add it to iGreek
269
0
        const SmSym &rSym = *aGreekSymbols[i];
270
0
        vcl::Font aFont( rSym.GetFace() );
271
0
        OSL_ENSURE( aFont.GetItalicMaybeAskConfig() == ITALIC_NONE, "expected Font with ITALIC_NONE, failed." );
272
0
        aFont.SetItalic( ITALIC_NORMAL );
273
0
        OUString aSymbolName = "i" + rSym.GetUiName();
274
0
        SmSym aSymbol( aSymbolName, aFont, rSym.GetCharacter(),
275
0
                aSymbolSetName, true /*bIsPredefined*/ );
276
0
        aSymbol.SetExportName("i" + rSym.GetExportName());
277
278
0
        AddOrReplaceSymbol( aSymbol );
279
0
    }
280
1
}
281
282
void SmSymbolManager::Save()
283
0
{
284
0
    if (!m_bModified)
285
0
        return;
286
287
    // prepare to skip symbols from iGreek on saving
288
0
    OUString aSymbolSetName = "i" +
289
0
        SmLocalizedSymbolData::GetUiSymbolSetName(u"Greek");
290
291
0
    SymbolPtrVec_t aTmp( GetSymbols() );
292
0
    std::vector< SmSym > aSymbols;
293
0
    for (const SmSym* i : aTmp)
294
0
    {
295
        // skip symbols from iGreek set since those symbols always get added
296
        // by computational means in SmSymbolManager::Load
297
0
        if (i->GetSymbolSetName() != aSymbolSetName)
298
0
            aSymbols.push_back( *i );
299
0
    }
300
0
    SmModule::get()->GetConfig()->SetSymbols(aSymbols);
301
302
0
    m_bModified = false;
303
0
}
304
305
306
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */