/src/poppler/poppler/UTF.cc
Line | Count | Source |
1 | | //======================================================================== |
2 | | // |
3 | | // UTF.cc |
4 | | // |
5 | | // Copyright 2001-2003 Glyph & Cog, LLC |
6 | | // |
7 | | //======================================================================== |
8 | | |
9 | | //======================================================================== |
10 | | // |
11 | | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | | // |
13 | | // All changes made under the Poppler project to this file are licensed |
14 | | // under GPL version 2 or later |
15 | | // |
16 | | // Copyright (C) 2008 Koji Otani <sho@bbr.jp> |
17 | | // Copyright (C) 2012, 2017, 2021, 2023, 2024 Adrian Johnson <ajohnson@redneon.com> |
18 | | // Copyright (C) 2012 Hib Eris <hib@hiberis.nl> |
19 | | // Copyright (C) 2016, 2018-2022, 2024, 2025 Albert Astals Cid <aacid@kde.org> |
20 | | // Copyright (C) 2016 Jason Crain <jason@aquaticape.us> |
21 | | // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
22 | | // Copyright (C) 2018, 2020 Nelson Benítez León <nbenitezl@gmail.com> |
23 | | // Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net. |
24 | | // Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
25 | | // Copyright (C) 2023 Even Rouault <even.rouault@spatialys.com> |
26 | | // Copyright (C) 2023, 2024 Oliver Sander <oliver.sander@tu-dresden.de> |
27 | | // Copyright (C) 2025 Jonathan Hähne <jonathan.haehne@hotmail.com> |
28 | | // |
29 | | // To see a description of the changes please see the Changelog file that |
30 | | // came with your tarball or type make ChangeLog if you are building from git |
31 | | // |
32 | | //======================================================================== |
33 | | |
34 | | #include "goo/gmem.h" |
35 | | #include "PDFDocEncoding.h" |
36 | | #include "GlobalParams.h" |
37 | | #include "UnicodeMap.h" |
38 | | #include "UTF.h" |
39 | | #include "UnicodeMapFuncs.h" |
40 | | #include <algorithm> |
41 | | |
42 | | #include <config.h> |
43 | | |
44 | | std::vector<Unicode> UTF16toUCS4(std::span<const Unicode> utf16) |
45 | 1.97k | { |
46 | | // count characters |
47 | 1.97k | int len = 0; |
48 | 4.64k | for (size_t i = 0; i < utf16.size(); i++) { |
49 | 2.66k | if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16.size() && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { |
50 | 0 | i++; /* surrogate pair */ |
51 | 0 | } |
52 | 2.66k | len++; |
53 | 2.66k | } |
54 | 1.97k | std::vector<Unicode> u; |
55 | 1.97k | u.reserve(len); |
56 | | // convert string |
57 | 4.64k | for (size_t i = 0; i < utf16.size(); i++) { |
58 | 2.66k | if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00) { /* surrogate pair */ |
59 | 5 | if (i + 1 < utf16.size() && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { |
60 | | /* next code is a low surrogate */ |
61 | 0 | u.push_back((((utf16[i] & 0x3ff) << 10) | (utf16[i + 1] & 0x3ff)) + 0x10000); |
62 | 0 | ++i; |
63 | 5 | } else { |
64 | | /* missing low surrogate |
65 | | replace it with REPLACEMENT CHARACTER (U+FFFD) */ |
66 | 5 | u.push_back(0xfffd); |
67 | 5 | } |
68 | 2.66k | } else if (utf16[i] >= 0xdc00 && utf16[i] < 0xe000) { |
69 | | /* invalid low surrogate |
70 | | replace it with REPLACEMENT CHARACTER (U+FFFD) */ |
71 | 11 | u.push_back(0xfffd); |
72 | 2.65k | } else { |
73 | 2.65k | u.push_back(utf16[i]); |
74 | 2.65k | } |
75 | 2.66k | if (!UnicodeIsValid(u.back())) { |
76 | 0 | u.back() = 0xfffd; |
77 | 0 | } |
78 | 2.66k | } |
79 | 1.97k | return u; |
80 | 1.97k | } |
81 | | |
82 | | std::vector<Unicode> TextStringToUCS4(std::string_view textStr) |
83 | 4.82k | { |
84 | 4.82k | bool isUnicode, isUnicodeLE; |
85 | | |
86 | 4.82k | int len = textStr.size(); |
87 | 4.82k | if (len == 0) { |
88 | 878 | return {}; |
89 | 878 | } |
90 | | |
91 | 3.94k | if (hasUnicodeByteOrderMark(textStr)) { |
92 | 0 | isUnicode = true; |
93 | 0 | isUnicodeLE = false; |
94 | 3.94k | } else if (hasUnicodeByteOrderMarkLE(textStr)) { |
95 | 0 | isUnicode = false; |
96 | 0 | isUnicodeLE = true; |
97 | 3.94k | } else { |
98 | 3.94k | isUnicode = false; |
99 | 3.94k | isUnicodeLE = false; |
100 | 3.94k | } |
101 | | |
102 | 3.94k | if (isUnicode || isUnicodeLE) { |
103 | 0 | len = len / 2 - 1; |
104 | 0 | if (len > 0) { |
105 | 0 | std::vector<Unicode> utf16; |
106 | 0 | utf16.reserve(len); |
107 | 0 | for (int i = 0; i < len; i++) { |
108 | 0 | if (isUnicode) { |
109 | 0 | utf16.push_back((textStr[2 + i * 2] & 0xff) << 8 | (textStr[3 + i * 2] & 0xff)); |
110 | 0 | } else { // UnicodeLE |
111 | 0 | utf16.push_back((textStr[3 + i * 2] & 0xff) << 8 | (textStr[2 + i * 2] & 0xff)); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | return UTF16toUCS4(utf16); |
115 | 0 | } |
116 | 0 | return {}; |
117 | 0 | } |
118 | 3.94k | std::vector<Unicode> u; |
119 | 3.94k | u.reserve(len); |
120 | 232k | for (int i = 0; i < len; i++) { |
121 | 228k | u.push_back(pdfDocEncoding[textStr[i] & 0xff]); |
122 | 228k | } |
123 | 3.94k | return u; |
124 | 3.94k | } |
125 | | |
126 | | bool UnicodeIsWhitespace(Unicode ucs4) |
127 | 1.13M | { |
128 | 1.13M | static Unicode const spaces[] = { 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000 }; |
129 | 1.13M | Unicode const *end = spaces + sizeof(spaces) / sizeof(spaces[0]); |
130 | 1.13M | Unicode const *i = std::lower_bound(spaces, end, ucs4); |
131 | 1.13M | return (i != end && *i == ucs4); |
132 | 1.13M | } |
133 | | |
134 | | // |
135 | | // decodeUtf8() and decodeUtf8Table are: |
136 | | // |
137 | | // Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> |
138 | | // |
139 | | // Permission is hereby granted, free of charge, to any person |
140 | | // obtaining a copy of this software and associated documentation |
141 | | // files (the "Software"), to deal in the Software without |
142 | | // restriction, including without limitation the rights to use, copy, |
143 | | // modify, merge, publish, distribute, sublicense, and/or sell copies |
144 | | // of the Software, and to permit persons to whom the Software is |
145 | | // furnished to do so, subject to the following conditions: |
146 | | |
147 | | // The above copyright notice and this permission notice shall be |
148 | | // included in all copies or substantial portions of the Software. |
149 | | // |
150 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
151 | | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
152 | | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
153 | | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
154 | | // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
155 | | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
156 | | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
157 | | // SOFTWARE. |
158 | | // |
159 | | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. |
160 | | // |
161 | | static const uint32_t UTF8_ACCEPT = 0; |
162 | | static const uint32_t UTF8_REJECT = 12; |
163 | | static const uint32_t UCS4_MAX = 0x10FFFF; |
164 | | static const Unicode REPLACEMENT_CHAR = 0xFFFD; |
165 | | |
166 | | // clang-format off |
167 | | static const uint8_t decodeUtf8Table[] = { |
168 | | // The first part of the table maps bytes to character classes |
169 | | // to reduce the size of the transition table and create bitmasks. |
170 | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f |
171 | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f |
172 | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f |
173 | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f |
174 | | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f |
175 | | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf |
176 | | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df |
177 | | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, // e0..ff |
178 | | |
179 | | // The second part is a transition table that maps a combination |
180 | | // of a state of the automaton and a character class to a state. |
181 | | 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, |
182 | | 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, |
183 | | 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, |
184 | | 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, |
185 | | 12,36,12,12,12,12,12,12,12,12,12,12, |
186 | | }; |
187 | | // clang-format on |
188 | | |
189 | | // Decode utf8 state machine for fast UTF-8 decoding. Initialise state |
190 | | // to 0 and call decodeUtf8() for each byte of UTF-8. Return value |
191 | | // (and state) is UTF8_ACCEPT when it has found a valid codepoint |
192 | | // (codepoint returned in codep), UTF8_REJECT when the byte is not |
193 | | // allowed to occur at its position, and some other positive value if |
194 | | // more bytes have to be read. Reset state to 0 to recover from |
195 | | // errors. |
196 | | inline uint32_t decodeUtf8(uint32_t *state, uint32_t *codep, char byte) |
197 | 736k | { |
198 | 736k | uint32_t b = static_cast<unsigned char>(byte); |
199 | 736k | uint32_t type = decodeUtf8Table[b]; |
200 | | |
201 | 736k | *codep = (*state != UTF8_ACCEPT) ? (b & 0x3fU) | (*codep << 6) : (0xff >> type) & b; |
202 | | |
203 | 736k | *state = decodeUtf8Table[256 + *state + type]; |
204 | 736k | return *state; |
205 | 736k | } |
206 | | |
207 | | std::vector<Unicode> utf8ToUCS4(std::string_view utf8) |
208 | 0 | { |
209 | 0 | uint32_t codepoint; |
210 | 0 | uint32_t state = 0; |
211 | |
|
212 | 0 | std::vector<Unicode> u; |
213 | |
|
214 | 0 | for (auto c : utf8) { |
215 | 0 | decodeUtf8(&state, &codepoint, c); |
216 | 0 | if (state == UTF8_ACCEPT) { |
217 | 0 | u.push_back(codepoint); |
218 | 0 | } else if (state == UTF8_REJECT) { |
219 | 0 | u.push_back(REPLACEMENT_CHAR); // invalid byte for this position |
220 | 0 | state = 0; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | if (state != UTF8_ACCEPT && state != UTF8_REJECT) { |
224 | 0 | u.push_back(REPLACEMENT_CHAR); // invalid byte for this position |
225 | 0 | } |
226 | 0 | u.shrink_to_fit(); |
227 | |
|
228 | 0 | return u; |
229 | 0 | } |
230 | | |
231 | | // Count number of UTF-16 code units required to convert a UTF-8 string |
232 | | // (excluding terminating NULL). Each invalid byte is counted as a |
233 | | // code point since the UTF-8 conversion functions will replace it with |
234 | | // REPLACEMENT_CHAR. |
235 | | int utf8CountUtf16CodeUnits(std::string_view utf8) |
236 | 0 | { |
237 | 0 | uint32_t codepoint; |
238 | 0 | uint32_t state = 0; |
239 | 0 | int count = 0; |
240 | |
|
241 | 0 | for (auto c : utf8) { |
242 | 0 | decodeUtf8(&state, &codepoint, c); |
243 | 0 | if (state == UTF8_ACCEPT) { |
244 | 0 | if (codepoint < 0x10000) { |
245 | 0 | count++; |
246 | 0 | } else if (codepoint <= UCS4_MAX) { |
247 | 0 | count += 2; |
248 | 0 | } else { |
249 | 0 | count++; // replace with REPLACEMENT_CHAR |
250 | 0 | } |
251 | 0 | } else if (state == UTF8_REJECT) { |
252 | 0 | count++; // replace with REPLACEMENT_CHAR |
253 | 0 | state = 0; |
254 | 0 | } |
255 | 0 | } |
256 | 0 | if (state != UTF8_ACCEPT && state != UTF8_REJECT) { |
257 | 0 | count++; // replace with REPLACEMENT_CHAR |
258 | 0 | } |
259 | |
|
260 | 0 | return count; |
261 | 0 | } |
262 | | |
263 | | std::u16string utf8ToUtf16(std::string_view utf8) |
264 | 409 | { |
265 | 409 | uint32_t codepoint; |
266 | 409 | uint32_t state = 0; |
267 | 409 | if (isUtf8WithBom(utf8)) { |
268 | 409 | utf8 = utf8.substr(3); |
269 | 409 | } |
270 | 409 | std::u16string utf16; |
271 | 736k | for (auto c : utf8) { |
272 | 736k | decodeUtf8(&state, &codepoint, c); |
273 | 736k | if (state == UTF8_ACCEPT) { |
274 | 353k | if (codepoint < 0x10000) { |
275 | 353k | utf16.push_back(static_cast<uint16_t>(codepoint)); |
276 | 353k | } else if (codepoint <= UCS4_MAX) { |
277 | 1 | utf16.push_back(static_cast<uint16_t>(0xD7C0 + (codepoint >> 10))); |
278 | 1 | utf16.push_back(static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF))); |
279 | 1 | } else { |
280 | 0 | utf16.push_back(REPLACEMENT_CHAR); |
281 | 0 | state = 0; |
282 | 0 | } |
283 | 383k | } else if (state == UTF8_REJECT) { |
284 | 382k | utf16.push_back(REPLACEMENT_CHAR); // invalid byte for this position |
285 | 382k | } |
286 | 736k | } |
287 | | // replace any trailing bytes too short for a valid UTF-8 with a replacement char |
288 | 409 | if (state != UTF8_ACCEPT && state != UTF8_REJECT) { |
289 | 57 | utf16.push_back(REPLACEMENT_CHAR); |
290 | 57 | } |
291 | 409 | return utf16; |
292 | 409 | } |
293 | | |
294 | | std::string utf8ToUtf16WithBom(std::string_view utf8) |
295 | 409 | { |
296 | 409 | if (utf8.empty()) { |
297 | 0 | return {}; |
298 | 0 | } |
299 | 409 | std::u16string utf16 = utf8ToUtf16(utf8); |
300 | 409 | char *tmp_str = reinterpret_cast<char *>(utf16.data()); |
301 | 409 | #if !WORDS_BIGENDIAN |
302 | 736k | for (size_t i = 0; i < utf16.size(); i++) { |
303 | 736k | std::swap(tmp_str[i * 2], tmp_str[i * 2 + 1]); |
304 | 736k | } |
305 | 409 | #endif |
306 | | |
307 | 409 | std::string result(unicodeByteOrderMark); |
308 | 409 | result.append(tmp_str, utf16.size() * 2); |
309 | 409 | return result; |
310 | 409 | } |
311 | | |
312 | | static const uint32_t UTF16_ACCEPT = 0; |
313 | | static const uint32_t UTF16_REJECT = -1; |
314 | | |
315 | | // Initialise state to 0. Returns UTF16_ACCEPT when a valid code point |
316 | | // has been found, UTF16_REJECT when invalid code unit for this state, |
317 | | // some other valid if another code unit needs to be read. |
318 | | inline uint32_t decodeUtf16(uint32_t *state, uint32_t *codePoint, uint16_t codeUnit) |
319 | 0 | { |
320 | 0 | if (*state == 0) { |
321 | 0 | if (codeUnit >= 0xd800 && codeUnit < 0xdc00) { /* surrogate pair */ |
322 | 0 | *state = codeUnit; |
323 | 0 | return *state; |
324 | 0 | } |
325 | 0 | if (codeUnit >= 0xdc00 && codeUnit < 0xe000) { |
326 | | /* invalid low surrogate */ |
327 | 0 | return UTF16_REJECT; |
328 | 0 | } |
329 | 0 | *codePoint = codeUnit; |
330 | 0 | return UTF16_ACCEPT; |
331 | 0 | } |
332 | 0 | if (codeUnit >= 0xdc00 && codeUnit < 0xe000) { |
333 | 0 | *codePoint = (((*state & 0x3ff) << 10) | (codeUnit & 0x3ff)) + 0x10000; |
334 | 0 | *state = 0; |
335 | 0 | return UTF16_ACCEPT; |
336 | 0 | } |
337 | | /* invalid high surrogate */ |
338 | 0 | return UTF16_REJECT; |
339 | 0 | } |
340 | | |
341 | | // Count number of UTF-8 bytes required to convert a UTF-16 string to |
342 | | // UTF-8 (excluding terminating NULL). |
343 | | int utf16CountUtf8Bytes(const uint16_t *utf16) |
344 | 0 | { |
345 | 0 | uint32_t codepoint = 0; |
346 | 0 | uint32_t state = 0; |
347 | 0 | int count = 0; |
348 | |
|
349 | 0 | while (*utf16) { |
350 | 0 | decodeUtf16(&state, &codepoint, *utf16); |
351 | 0 | if (state == UTF16_ACCEPT) { |
352 | 0 | if (codepoint < 0x80) { |
353 | 0 | count++; |
354 | 0 | } else if (codepoint < 0x800) { |
355 | 0 | count += 2; |
356 | 0 | } else if (codepoint < 0x10000) { |
357 | 0 | count += 3; |
358 | 0 | } else if (codepoint <= UCS4_MAX) { |
359 | 0 | count += 4; |
360 | 0 | } else { |
361 | 0 | count += 3; // replace with REPLACEMENT_CHAR |
362 | 0 | } |
363 | 0 | } else if (state == UTF16_REJECT) { |
364 | 0 | count += 3; // replace with REPLACEMENT_CHAR |
365 | 0 | state = 0; |
366 | 0 | } |
367 | 0 | utf16++; |
368 | 0 | } |
369 | 0 | if (state != UTF8_ACCEPT && state != UTF8_REJECT) { |
370 | 0 | count += 3; // replace with REPLACEMENT_CHAR |
371 | 0 | } |
372 | |
|
373 | 0 | return count; |
374 | 0 | } |
375 | | |
376 | | std::string utf16ToUtf8(const uint16_t *utf16, int maxUtf16) |
377 | 0 | { |
378 | 0 | uint32_t codepoint = 0; |
379 | 0 | uint32_t state = 0; |
380 | 0 | int nIn = 0; |
381 | 0 | char p[4]; |
382 | 0 | std::string utf8; |
383 | 0 | while (*utf16 && nIn < maxUtf16) { |
384 | 0 | decodeUtf16(&state, &codepoint, *utf16); |
385 | 0 | if (state == UTF16_ACCEPT || state == UTF16_REJECT) { |
386 | 0 | if (state == UTF16_REJECT || codepoint > UCS4_MAX) { |
387 | 0 | codepoint = REPLACEMENT_CHAR; |
388 | 0 | state = 0; |
389 | 0 | } |
390 | |
|
391 | 0 | int count = mapUTF8(codepoint, p, 4); |
392 | 0 | utf8.append(std::string_view(p, count)); |
393 | 0 | } |
394 | 0 | utf16++; |
395 | 0 | nIn++; |
396 | 0 | } |
397 | | // replace any trailing bytes too short for a valid UTF-8 with a replacement char |
398 | 0 | if (state != UTF16_ACCEPT && state != UTF16_REJECT) { |
399 | 0 | int count = mapUTF8(REPLACEMENT_CHAR, p, 4); |
400 | 0 | utf8.append(std::string_view(p, count)); |
401 | 0 | } |
402 | 0 | return utf8; |
403 | 0 | } |
404 | | |
405 | | void unicodeToAscii7(std::span<const Unicode> in, Unicode **ucs4_out, int *out_len, const int *in_idx, int **indices) |
406 | 0 | { |
407 | 0 | const UnicodeMap *uMap = globalParams->getUnicodeMap("ASCII7"); |
408 | 0 | int *idx = nullptr; |
409 | |
|
410 | 0 | if (in.empty()) { |
411 | 0 | *ucs4_out = nullptr; |
412 | 0 | *out_len = 0; |
413 | 0 | return; |
414 | 0 | } |
415 | | |
416 | 0 | if (indices) { |
417 | 0 | if (!in_idx) { |
418 | 0 | indices = nullptr; |
419 | 0 | } else { |
420 | 0 | idx = static_cast<int *>(gmallocn(in.size() * 8 + 1, sizeof(int))); |
421 | 0 | } |
422 | 0 | } |
423 | |
|
424 | 0 | std::string str; |
425 | |
|
426 | 0 | char buf[8]; // 8 is enough for mapping an unicode char to a string |
427 | 0 | size_t i; |
428 | 0 | int n, k; |
429 | |
|
430 | 0 | for (i = k = 0; i < in.size(); ++i) { |
431 | 0 | n = uMap->mapUnicode(in[i], buf, sizeof(buf)); |
432 | 0 | if (!n) { |
433 | | // the Unicode char could not be converted to ascii7 counterpart |
434 | | // so just fill with a non-printable ascii char |
435 | 0 | buf[0] = 31; |
436 | 0 | n = 1; |
437 | 0 | } |
438 | 0 | str.append(buf, n); |
439 | 0 | if (indices) { |
440 | 0 | for (; n > 0; n--) { |
441 | 0 | idx[k++] = in_idx[i]; |
442 | 0 | } |
443 | 0 | } |
444 | 0 | } |
445 | |
|
446 | 0 | std::vector<Unicode> ucs4 = TextStringToUCS4(str); |
447 | 0 | *out_len = ucs4.size(); |
448 | 0 | *ucs4_out = static_cast<Unicode *>(gmallocn(ucs4.size(), sizeof(Unicode))); |
449 | 0 | memcpy(*ucs4_out, ucs4.data(), ucs4.size() * sizeof(Unicode)); |
450 | |
|
451 | 0 | if (indices) { |
452 | 0 | idx[k] = in_idx[in.size()]; |
453 | 0 | *indices = idx; |
454 | 0 | } |
455 | 0 | } |
456 | | |
457 | | // Convert a PDF Text String to UTF-8 |
458 | | // textStr - PDF text string |
459 | | // returns UTF-8 string. |
460 | | std::string TextStringToUtf8(std::string_view textStr) |
461 | 0 | { |
462 | 0 | int i, len; |
463 | 0 | std::string utf8; |
464 | |
|
465 | 0 | len = textStr.size(); |
466 | 0 | if (hasUnicodeByteOrderMark(textStr)) { |
467 | 0 | std::vector<uint16_t> utf16; |
468 | 0 | len = len / 2 - 1; |
469 | 0 | utf16.resize(len + 1); |
470 | 0 | for (i = 0; i < len; i++) { |
471 | 0 | utf16[i] = (textStr[2 + i * 2] & 0xff) << 8 | (textStr[3 + i * 2] & 0xff); |
472 | 0 | } |
473 | 0 | utf16[i] = 0; |
474 | 0 | utf8 = utf16ToUtf8(utf16.data(), utf16.size()); |
475 | 0 | } else { |
476 | 0 | utf8.resize(len + 1); |
477 | 0 | for (i = 0; i < len; i++) { |
478 | 0 | utf8[i] = pdfDocEncoding[textStr[i] & 0xff]; |
479 | 0 | } |
480 | 0 | utf8[i] = 0; |
481 | 0 | } |
482 | 0 | return utf8; |
483 | 0 | } |