Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libetonyek/src/lib/IWORKLanguageManager.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/*
3
 * This file is part of the libe-book 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
#ifdef HAVE_CONFIG_H
11
#include "config.h"
12
#endif
13
14
#include "IWORKLanguageManager.h"
15
16
#include <cstdlib>
17
#include <memory>
18
#include <stdexcept>
19
20
#ifdef WITH_LIBLANGTAG
21
#include <liblangtag/langtag.h>
22
#endif
23
24
#include "libetonyek_utils.h"
25
26
namespace libetonyek
27
{
28
29
using librevenge::RVNGPropertyList;
30
31
using std::string;
32
using std::unordered_map;
33
34
#ifdef WITH_LIBLANGTAG
35
using std::shared_ptr;
36
using std::unordered_set;
37
namespace
38
{
39
40
const shared_ptr<lt_tag_t> parseTag(const std::string &lang)
41
{
42
  const shared_ptr<lt_tag_t> tag(lt_tag_new(), lt_tag_unref);
43
  lt_error_t *error = nullptr;
44
  lt_tag_parse(tag.get(), lang.c_str(), &error);
45
  if (error && lt_error_is_set(error, LT_ERR_ANY))
46
  {
47
    lt_error_unref(error);
48
    return shared_ptr<lt_tag_t>();
49
  }
50
  return tag;
51
}
52
53
const std::string makeFullTag(const shared_ptr<lt_tag_t> &tag)
54
{
55
  lt_error_t *error = nullptr;
56
  const shared_ptr<char> full(lt_tag_transform(tag.get(), &error), std::free);
57
  if (error && lt_error_is_set(error, LT_ERR_ANY))
58
  {
59
    lt_error_unref(error);
60
    return lt_tag_get_string(tag.get());
61
  }
62
  return full.get();
63
}
64
65
}
66
#endif
67
68
struct IWORKLanguageManager::LangDB
69
{
70
  LangDB();
71
72
  unordered_map<string, string> m_db;
73
};
74
75
IWORKLanguageManager::LangDB::LangDB()
76
0
  : m_db()
77
0
{
78
#ifdef WITH_LIBLANGTAG
79
  shared_ptr<lt_lang_db_t> langDB(lt_db_get_lang(), lt_lang_db_unref);
80
  shared_ptr<lt_iter_t> it(LT_ITER_INIT(langDB.get()), lt_iter_finish);
81
  lt_pointer_t key(nullptr);
82
  lt_pointer_t value(nullptr);
83
  while (lt_iter_next(it.get(), &key, &value))
84
  {
85
    const auto *const tag = reinterpret_cast<const char *>(key);
86
    auto *const lang = reinterpret_cast<lt_lang_t *>(value);
87
    m_db[lt_lang_get_name(lang)] = tag;
88
  }
89
#endif
90
0
}
91
92
IWORKLanguageManager::IWORKLanguageManager()
93
1.19k
  : m_tagMap()
94
1.19k
  , m_invalidTags()
95
1.19k
  , m_langMap()
96
1.19k
  , m_invalidLangs()
97
1.19k
  , m_localeMap()
98
1.19k
  , m_invalidLocales()
99
1.19k
  , m_propsMap()
100
1.19k
  , m_langDB()
101
1.19k
{
102
1.19k
}
103
104
const std::string IWORKLanguageManager::addTag(const std::string &tag)
105
888
{
106
#ifdef WITH_LIBLANGTAG
107
  // Check if the tag is already known
108
  const unordered_map<string, string>::const_iterator it = m_tagMap.find(tag);
109
  if (it != m_tagMap.end())
110
    return it->second;
111
  // Check if the tag was previously rejected as invalid
112
  const unordered_set<string>::const_iterator invIt = m_invalidTags.find(tag);
113
  if (invIt != m_invalidTags.end())
114
    return "";
115
116
  const shared_ptr<lt_tag_t> &langTag = parseTag(tag);
117
  if (!langTag)
118
  {
119
    m_invalidTags.insert(tag);
120
    return "";
121
  }
122
123
  const string fullTag(makeFullTag(langTag));
124
  m_tagMap[tag] = fullTag;
125
  addProperties(fullTag);
126
127
  return fullTag;
128
#else
129
888
  return tag;
130
888
#endif
131
888
}
132
133
const std::string IWORKLanguageManager::addLanguage(const std::string &lang)
134
0
{
135
#ifdef WITH_LIBLANGTAG
136
  // Check if the lang is already known
137
  const unordered_map<string, string>::const_iterator it = m_langMap.find(lang);
138
  if (it != m_langMap.end())
139
    return it->second;
140
  // Check if the lang was previously rejected as invalid
141
  const unordered_set<string>::const_iterator invIt = m_invalidLangs.find(lang);
142
  if (invIt != m_invalidLangs.end())
143
    return "";
144
145
  const unordered_map<string, string>::const_iterator langIt = getLangDB().m_db.find(lang);
146
  if (langIt == getLangDB().m_db.end())
147
  {
148
    m_invalidLangs.insert(lang);
149
    return "";
150
  }
151
152
  const shared_ptr<lt_tag_t> &langTag = parseTag(langIt->second);
153
  if (!langTag)
154
    throw std::logic_error("cannot parse tag that came from liblangtag language DB");
155
156
  const string fullTag(makeFullTag(langTag));
157
  m_langMap[lang] = fullTag;
158
  addProperties(fullTag);
159
160
  return fullTag;
161
#else
162
0
  (void) lang;
163
0
  return "";
164
0
#endif
165
0
}
166
167
const std::string IWORKLanguageManager::addLocale(const std::string &locale)
168
0
{
169
#ifdef WITH_LIBLANGTAG
170
  // Check if the locale is already known
171
  const unordered_map<string, string>::const_iterator it = m_localeMap.find(locale);
172
  if (it != m_localeMap.end())
173
    return it->second;
174
  // Check if the locale was previously rejected as invalid
175
  const unordered_set<string>::const_iterator invIt = m_invalidLocales.find(locale);
176
  if (invIt != m_invalidLocales.end())
177
    return "";
178
179
  lt_error_t *error = nullptr;
180
  const shared_ptr<lt_tag_t> tag(lt_tag_convert_from_locale_string(locale.c_str(), &error), lt_tag_unref);
181
  if ((error && lt_error_is_set(error, LT_ERR_ANY)) || !tag)
182
  {
183
    lt_error_unref(error);
184
    m_invalidLocales.insert(locale);
185
    return "";
186
  }
187
188
  const string fullTag(makeFullTag(tag));
189
  m_tagMap[locale] = fullTag;
190
  addProperties(fullTag);
191
192
  return fullTag;
193
#else
194
0
  (void) locale;
195
0
  return "";
196
0
#endif
197
0
}
198
199
const std::string IWORKLanguageManager::getLanguage(const std::string &tag) const
200
0
{
201
#ifdef WITH_LIBLANGTAG
202
  const shared_ptr<lt_tag_t> &langTag = parseTag(tag);
203
  if (!langTag)
204
    throw std::logic_error("cannot parse tag that has been successfully parsed before");
205
  return lt_lang_get_name(lt_tag_get_language(langTag.get()));
206
#else
207
0
  (void) tag;
208
0
  return "";
209
0
#endif
210
0
}
211
212
const IWORKLanguageManager::LangDB &IWORKLanguageManager::getLangDB() const
213
0
{
214
0
  if (!m_langDB)
215
0
    m_langDB = std::make_shared<LangDB>();
216
0
  return *m_langDB;
217
0
}
218
219
void IWORKLanguageManager::addProperties(const std::string &tag)
220
0
{
221
#ifdef WITH_LIBLANGTAG
222
  const shared_ptr<lt_tag_t> &langTag = parseTag(tag);
223
  if (!langTag)
224
    throw std::logic_error("cannot parse tag that has been successfully parsed before");
225
226
  RVNGPropertyList props;
227
  const lt_lang_t *const lang = lt_tag_get_language(langTag.get());
228
  if (lang)
229
    props.insert("fo:language", lt_lang_get_tag(lang));
230
  const lt_region_t *const region = lt_tag_get_region(langTag.get());
231
  if (region)
232
    props.insert("fo:country", lt_region_get_tag(region));
233
  const lt_script_t *const script = lt_tag_get_script(langTag.get());
234
  if (script)
235
    props.insert("fo:script", lt_script_get_tag(script));
236
237
  m_propsMap[tag] = props;
238
#else
239
0
  (void) tag;
240
0
#endif
241
0
}
242
243
void IWORKLanguageManager::writeProperties(const std::string &tag, librevenge::RVNGPropertyList &props) const
244
1.43k
{
245
#ifdef WITH_LIBLANGTAG
246
  const unordered_map<string, RVNGPropertyList>::const_iterator it = m_propsMap.find(tag);
247
  if (it == m_propsMap.end())
248
  {
249
    ETONYEK_DEBUG_MSG(("IWORKLanguageManager::writeProperties: unknown tag %s\n", tag.c_str()));
250
    return;
251
  }
252
  for (RVNGPropertyList::Iter iter(it->second); !iter.last(); iter.next())
253
    props.insert(iter.key(), iter()->getStr());
254
#else
255
1.43k
  (void) tag;
256
1.43k
  (void) props;
257
1.43k
#endif
258
1.43k
}
259
260
}
261
262
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */