Merge remote branch 'origin/trunk' into trac-8658
[xbmc:xbmc-antiquated.git] / xbmc / utils / CharsetConverter.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "CharsetConverter.h"
23 #include "Util.h"
24 #include "ArabicShaping.h"
25 #include "GUISettings.h"
26 #include "LangInfo.h"
27 #include "SingleLock.h"
28 #include "log.h"
29
30 #include <iconv.h>
31
32 #ifdef __APPLE__
33 #ifdef __POWERPC__
34   #define WCHAR_CHARSET "UTF-32BE"
35 #else
36   #define WCHAR_CHARSET "UTF-32LE"
37 #endif
38   #define UTF8_SOURCE "UTF-8-MAC"
39 #elif defined(WIN32)
40   #define WCHAR_CHARSET "UTF-16LE"
41   #define UTF8_SOURCE "UTF-8"
42 #else
43   #define WCHAR_CHARSET "WCHAR_T"
44   #define UTF8_SOURCE "UTF-8"
45 #endif
46
47
48 static iconv_t m_iconvStringCharsetToFontCharset = (iconv_t)-1;
49 static iconv_t m_iconvSubtitleCharsetToW         = (iconv_t)-1;
50 static iconv_t m_iconvUtf8ToStringCharset        = (iconv_t)-1;
51 static iconv_t m_iconvStringCharsetToUtf8        = (iconv_t)-1;
52 static iconv_t m_iconvUcs2CharsetToStringCharset = (iconv_t)-1;
53 static iconv_t m_iconvUtf32ToStringCharset       = (iconv_t)-1;
54 static iconv_t m_iconvWtoUtf8                    = (iconv_t)-1;
55 static iconv_t m_iconvUtf16LEtoW                 = (iconv_t)-1;
56 static iconv_t m_iconvUtf16BEtoUtf8              = (iconv_t)-1;
57 static iconv_t m_iconvUtf16LEtoUtf8              = (iconv_t)-1;
58 static iconv_t m_iconvUtf8toW                    = (iconv_t)-1;
59 static iconv_t m_iconvUcs2CharsetToUtf8          = (iconv_t)-1;
60
61 static FriBidiCharSet m_stringFribidiCharset     = FRIBIDI_CHAR_SET_NOT_FOUND;
62
63 static std::vector<CStdString>     m_vecCharsetNames;
64 static std::vector<CStdString>     m_vecCharsetLabels;
65 static std::vector<CStdString>     m_vecBidiCharsetNames;
66 static std::vector<FriBidiCharSet> m_vecBidiCharsets;
67 static CCriticalSection            m_critSection;
68
69 CCharsetConverter g_charsetConverter;
70
71 #define UTF8_DEST_MULTIPLIER 6
72
73 #define ICONV_PREPARE(iconv) iconv=(iconv_t)-1
74 #define ICONV_SAFE_CLOSE(iconv) if (iconv!=(iconv_t)-1) { iconv_close(iconv); iconv=(iconv_t)-1; }
75
76 size_t iconv_const (void* cd, const char** inbuf, size_t *inbytesleft,
77                     char* * outbuf, size_t *outbytesleft)
78 {
79     struct iconv_param_adapter {
80         iconv_param_adapter(const char**p) : p(p) {}
81         iconv_param_adapter(char**p) : p((const char**)p) {}
82         operator char**() const
83         {
84             return(char**)p;
85         }
86         operator const char**() const
87         {
88             return(const char**)p;
89         }
90         const char** p;
91     };
92
93     return iconv((iconv_t)cd, iconv_param_adapter(inbuf), inbytesleft, outbuf, outbytesleft);
94 }
95
96 template<class INPUT,class OUTPUT>
97 static bool convert_checked(iconv_t& type, int multiplier, const CStdString& strFromCharset, const CStdString& strToCharset, const INPUT& strSource,  OUTPUT& strDest)
98 {
99   if (type == (iconv_t) - 1)
100   {
101     type = iconv_open(strToCharset.c_str(), strFromCharset.c_str());
102   }
103
104   if (type != (iconv_t) - 1)
105   {
106     if (strSource.IsEmpty())
107     {
108       strDest.Empty();
109     }
110     else
111     {
112       size_t inBytes  = (strSource.length() + 1)*sizeof(strSource[0]);
113       size_t outBytes = (strSource.length() + 1)*multiplier;
114       const char *src = (const char*)strSource.c_str();
115       char       *dst = (char*)strDest.GetBuffer(outBytes);
116
117       if (iconv_const(type, &src, &inBytes, &dst, &outBytes) == (size_t)-1)
118       {
119         CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
120         strDest.ReleaseBuffer();
121         return false;
122       }
123
124       if (iconv_const(type, NULL, NULL, &dst, &outBytes) == (size_t)-1)
125       {
126         CLog::Log(LOGERROR, "%s failed cleanup", __FUNCTION__);
127         strDest.ReleaseBuffer();
128         return false;
129       }
130
131       strDest.ReleaseBuffer();
132     }
133   }
134   return true;
135 }
136
137 template<class INPUT,class OUTPUT>
138 static void convert(iconv_t& type, int multiplier, const CStdString& strFromCharset, const CStdString& strToCharset, const INPUT& strSource,  OUTPUT& strDest)
139 {
140   if(!convert_checked(type, multiplier, strFromCharset, strToCharset, strSource, strDest))
141     strDest = strSource;
142 }
143
144 using namespace std;
145
146 static void logicalToVisualBiDi(const CStdStringA& strSource, CStdStringA& strDest, FriBidiCharSet fribidiCharset, FriBidiCharType base = FRIBIDI_TYPE_LTR, bool* bWasFlipped =NULL)
147 {
148   // libfribidi is not threadsafe, so make sure we make it so
149   CSingleLock lock(m_critSection);
150
151   vector<CStdString> lines;
152   CUtil::Tokenize(strSource, lines, "\n");
153   CStdString resultString;
154
155   if (bWasFlipped)
156     *bWasFlipped = false;
157
158   for (unsigned int i = 0; i < lines.size(); i++)
159   {
160     int sourceLen = lines[i].length();
161
162     // Convert from the selected charset to Unicode
163     FriBidiChar* logical = (FriBidiChar*) malloc((sourceLen + 1) * sizeof(FriBidiChar));
164     int len = fribidi_charset_to_unicode(fribidiCharset, (char*) lines[i].c_str(), sourceLen, logical);
165
166     FriBidiChar* visual = (FriBidiChar*) malloc((len + 1) * sizeof(FriBidiChar));
167     FriBidiLevel* levels = (FriBidiLevel*) malloc((len + 1) * sizeof(FriBidiLevel));
168
169     // Shape Arabic Text
170     FriBidiChar *shaped_text = shape_arabic(logical, len);
171     for (int i = 0; i < len; i++)
172        logical[i] = shaped_text[i];
173     free(shaped_text);
174
175     if (fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL))
176     {
177       // Removes bidirectional marks
178       //len = fribidi_remove_bidi_marks(visual, len, NULL, NULL, NULL);
179
180       // Apperently a string can get longer during this transformation
181       // so make sure we allocate the maximum possible character utf8
182       // can generate atleast, should cover all bases
183       char *result = strDest.GetBuffer(len*4);
184
185       // Convert back from Unicode to the charset
186       int len2 = fribidi_unicode_to_charset(fribidiCharset, visual, len, result);
187       ASSERT(len2 <= len*4);
188       strDest.ReleaseBuffer();
189
190       resultString += strDest;
191
192       // Check whether the string was flipped if one of the embedding levels is greater than 0
193       if (bWasFlipped && !*bWasFlipped)
194       {
195         for (int i = 0; i < len; i++)
196         {
197           if ((int) levels[i] > 0)
198           {
199             *bWasFlipped = true;
200             break;
201           }
202         }
203       }
204     }
205
206     free(logical);
207     free(visual);
208     free(levels);
209   }
210
211   strDest = resultString;
212 }
213
214 CCharsetConverter::CCharsetConverter()
215 {
216   m_vecCharsetNames.push_back("ISO-8859-1");
217   m_vecCharsetLabels.push_back("Western Europe (ISO)");
218   m_vecCharsetNames.push_back("ISO-8859-2");
219   m_vecCharsetLabels.push_back("Central Europe (ISO)");
220   m_vecCharsetNames.push_back("ISO-8859-3");
221   m_vecCharsetLabels.push_back("South Europe (ISO)");
222   m_vecCharsetNames.push_back("ISO-8859-4");
223   m_vecCharsetLabels.push_back("Baltic (ISO)");
224   m_vecCharsetNames.push_back("ISO-8859-5");
225   m_vecCharsetLabels.push_back("Cyrillic (ISO)");
226   m_vecCharsetNames.push_back("ISO-8859-6");
227   m_vecCharsetLabels.push_back("Arabic (ISO)");
228   m_vecCharsetNames.push_back("ISO-8859-7");
229   m_vecCharsetLabels.push_back("Greek (ISO)");
230   m_vecCharsetNames.push_back("ISO-8859-8");
231   m_vecCharsetLabels.push_back("Hebrew (ISO)");
232   m_vecCharsetNames.push_back("ISO-8859-9");
233   m_vecCharsetLabels.push_back("Turkish (ISO)");
234
235   m_vecCharsetNames.push_back("CP1250");
236   m_vecCharsetLabels.push_back("Central Europe (Windows)");
237   m_vecCharsetNames.push_back("CP1251");
238   m_vecCharsetLabels.push_back("Cyrillic (Windows)");
239   m_vecCharsetNames.push_back("CP1252");
240   m_vecCharsetLabels.push_back("Western Europe (Windows)");
241   m_vecCharsetNames.push_back("CP1253");
242   m_vecCharsetLabels.push_back("Greek (Windows)");
243   m_vecCharsetNames.push_back("CP1254");
244   m_vecCharsetLabels.push_back("Turkish (Windows)");
245   m_vecCharsetNames.push_back("CP1255");
246   m_vecCharsetLabels.push_back("Hebrew (Windows)");
247   m_vecCharsetNames.push_back("CP1256");
248   m_vecCharsetLabels.push_back("Arabic (Windows)");
249   m_vecCharsetNames.push_back("CP1257");
250   m_vecCharsetLabels.push_back("Baltic (Windows)");
251   m_vecCharsetNames.push_back("CP1258");
252   m_vecCharsetLabels.push_back("Vietnamesse (Windows)");
253   m_vecCharsetNames.push_back("CP874");
254   m_vecCharsetLabels.push_back("Thai (Windows)");
255
256   m_vecCharsetNames.push_back("BIG5");
257   m_vecCharsetLabels.push_back("Chinese Traditional (Big5)");
258   m_vecCharsetNames.push_back("GBK");
259   m_vecCharsetLabels.push_back("Chinese Simplified (GBK)");
260   m_vecCharsetNames.push_back("SHIFT_JIS");
261   m_vecCharsetLabels.push_back("Japanese (Shift-JIS)");
262   m_vecCharsetNames.push_back("CP949");
263   m_vecCharsetLabels.push_back("Korean");
264   m_vecCharsetNames.push_back("BIG5-HKSCS");
265   m_vecCharsetLabels.push_back("Hong Kong (Big5-HKSCS)");
266
267   m_vecBidiCharsetNames.push_back("ISO-8859-6");
268   m_vecBidiCharsetNames.push_back("ISO-8859-8");
269   m_vecBidiCharsetNames.push_back("CP1255");
270   m_vecBidiCharsetNames.push_back("Windows-1255");
271   m_vecBidiCharsetNames.push_back("CP1256");
272   m_vecBidiCharsetNames.push_back("Windows-1256");
273   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_ISO8859_6);
274   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_ISO8859_8);
275   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1255);
276   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1255);
277   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1256);
278   m_vecBidiCharsets.push_back(FRIBIDI_CHAR_SET_CP1256);
279 }
280
281 void CCharsetConverter::clear()
282 {
283   CSingleLock lock(m_critSection);
284
285   m_vecBidiCharsetNames.clear();
286   m_vecBidiCharsets.clear();
287   m_vecCharsetNames.clear();
288   m_vecCharsetLabels.clear();
289 }
290
291 vector<CStdString> CCharsetConverter::getCharsetLabels()
292 {
293   return m_vecCharsetLabels;
294 }
295
296 CStdString& CCharsetConverter::getCharsetLabelByName(const CStdString& charsetName)
297 {
298   for (unsigned int i = 0; i < m_vecCharsetNames.size(); i++)
299   {
300     if (m_vecCharsetNames[i].Equals(charsetName))
301     {
302       return m_vecCharsetLabels[i];
303     }
304   }
305
306   return EMPTY;
307 }
308
309 CStdString& CCharsetConverter::getCharsetNameByLabel(const CStdString& charsetLabel)
310 {
311   CSingleLock lock(m_critSection);
312
313   for (unsigned int i = 0; i < m_vecCharsetLabels.size(); i++)
314   {
315     if (m_vecCharsetLabels[i].Equals(charsetLabel))
316     {
317       return m_vecCharsetNames[i];
318     }
319   }
320
321   return EMPTY;
322 }
323
324 bool CCharsetConverter::isBidiCharset(const CStdString& charset)
325 {
326   CSingleLock lock(m_critSection);
327
328   for (unsigned int i = 0; i < m_vecBidiCharsetNames.size(); i++)
329   {
330     if (m_vecBidiCharsetNames[i].Equals(charset))
331     {
332       return true;
333     }
334   }
335
336   return false;
337 }
338
339 void CCharsetConverter::reset(void)
340 {
341   CSingleLock lock(m_critSection);
342
343   ICONV_SAFE_CLOSE(m_iconvStringCharsetToFontCharset);
344   ICONV_SAFE_CLOSE(m_iconvUtf8ToStringCharset);
345   ICONV_SAFE_CLOSE(m_iconvStringCharsetToUtf8);
346   ICONV_SAFE_CLOSE(m_iconvUcs2CharsetToStringCharset);
347   ICONV_SAFE_CLOSE(m_iconvSubtitleCharsetToW);
348   ICONV_SAFE_CLOSE(m_iconvWtoUtf8);
349   ICONV_SAFE_CLOSE(m_iconvUtf16BEtoUtf8);
350   ICONV_SAFE_CLOSE(m_iconvUtf16LEtoUtf8);
351   ICONV_SAFE_CLOSE(m_iconvUtf32ToStringCharset);
352   ICONV_SAFE_CLOSE(m_iconvUtf8toW);
353   ICONV_SAFE_CLOSE(m_iconvUcs2CharsetToUtf8);
354
355   m_stringFribidiCharset = FRIBIDI_CHAR_SET_NOT_FOUND;
356
357   CStdString strCharset=g_langInfo.GetGuiCharSet();
358
359   for (unsigned int i = 0; i < m_vecBidiCharsetNames.size(); i++)
360   {
361     if (m_vecBidiCharsetNames[i].Equals(strCharset))
362     {
363       m_stringFribidiCharset = m_vecBidiCharsets[i];
364     }
365   }
366 }
367
368 // The bVisualBiDiFlip forces a flip of characters for hebrew/arabic languages, only set to false if the flipping
369 // of the string is already made or the string is not displayed in the GUI
370 void CCharsetConverter::utf8ToW(const CStdStringA& utf8String, CStdStringW &wString, bool bVisualBiDiFlip/*=true*/, bool forceLTRReadingOrder /*=false*/, bool* bWasFlipped/*=NULL*/)
371 {
372   // Try to flip hebrew/arabic characters, if any
373   if (bVisualBiDiFlip)
374   {
375     CStdStringA strFlipped;
376     FriBidiCharType charset = forceLTRReadingOrder ? FRIBIDI_TYPE_LTR : FRIBIDI_TYPE_PDF;
377     logicalToVisualBiDi(utf8String, strFlipped, FRIBIDI_CHAR_SET_UTF8, charset, bWasFlipped);
378     convert(m_iconvUtf8toW,sizeof(wchar_t),UTF8_SOURCE,WCHAR_CHARSET,strFlipped,wString);
379   }
380   else
381     convert(m_iconvUtf8toW,sizeof(wchar_t),UTF8_SOURCE,WCHAR_CHARSET,utf8String,wString);
382 }
383
384 void CCharsetConverter::subtitleCharsetToW(const CStdStringA& strSource, CStdStringW& strDest)
385 {
386   // No need to flip hebrew/arabic as mplayer does the flipping
387   CSingleLock lock(m_critSection);
388   convert(m_iconvSubtitleCharsetToW,sizeof(wchar_t),g_langInfo.GetSubtitleCharSet(),WCHAR_CHARSET,strSource,strDest);
389 }
390
391 void CCharsetConverter::fromW(const CStdStringW& strSource,
392                               CStdStringA& strDest, const CStdString& enc)
393 {
394   iconv_t iconvString;
395   ICONV_PREPARE(iconvString);
396   convert(iconvString,sizeof(wchar_t),WCHAR_CHARSET,enc,strSource,strDest);
397   iconv_close(iconvString);
398 }
399
400 void CCharsetConverter::toW(const CStdStringA& strSource,
401                             CStdStringW& strDest, const CStdString& enc)
402 {
403   iconv_t iconvString;
404   ICONV_PREPARE(iconvString);
405   convert(iconvString,sizeof(wchar_t),enc,WCHAR_CHARSET,strSource,strDest);
406   iconv_close(iconvString);
407 }
408
409 void CCharsetConverter::utf8ToStringCharset(const CStdStringA& strSource, CStdStringA& strDest)
410 {
411   CSingleLock lock(m_critSection);
412   convert(m_iconvUtf8ToStringCharset,1,UTF8_SOURCE,g_langInfo.GetGuiCharSet(),strSource,strDest);
413 }
414
415 void CCharsetConverter::utf8ToStringCharset(CStdStringA& strSourceDest)
416 {
417   CStdString strDest;
418   utf8ToStringCharset(strSourceDest, strDest);
419   strSourceDest=strDest;
420 }
421
422 void CCharsetConverter::stringCharsetToUtf8(const CStdStringA& strSourceCharset, const CStdStringA& strSource, CStdStringA& strDest)
423 {
424   iconv_t iconvString;
425   ICONV_PREPARE(iconvString);
426   convert(iconvString,UTF8_DEST_MULTIPLIER,strSourceCharset,"UTF-8",strSource,strDest);
427   iconv_close(iconvString);
428 }
429
430 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdStringA& strDest)
431 {
432   if (strDestCharset == "UTF-8")
433   { // simple case - no conversion necessary
434     strDest = strSource;
435     return;
436   }
437   iconv_t iconvString;
438   ICONV_PREPARE(iconvString);
439   convert(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest);
440   iconv_close(iconvString);
441 }
442
443 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdString16& strDest)
444 {
445   iconv_t iconvString;
446   ICONV_PREPARE(iconvString);
447   if(!convert_checked(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest))
448     strDest.Empty();
449   iconv_close(iconvString);
450 }
451
452 void CCharsetConverter::utf8To(const CStdStringA& strDestCharset, const CStdStringA& strSource, CStdString32& strDest)
453 {
454   iconv_t iconvString;
455   ICONV_PREPARE(iconvString);
456   if(!convert_checked(iconvString,UTF8_DEST_MULTIPLIER,UTF8_SOURCE,strDestCharset,strSource,strDest))
457     strDest.Empty();
458   iconv_close(iconvString);
459 }
460
461 void CCharsetConverter::unknownToUTF8(CStdStringA &sourceAndDest)
462 {
463   CStdString source = sourceAndDest;
464   unknownToUTF8(source, sourceAndDest);
465 }
466
467 void CCharsetConverter::unknownToUTF8(const CStdStringA &source, CStdStringA &dest)
468 {
469   // checks whether it's utf8 already, and if not converts using the sourceCharset if given, else the string charset
470   if (isValidUtf8(source))
471     dest = source;
472   else
473   {
474     CSingleLock lock(m_critSection);
475     convert(m_iconvStringCharsetToUtf8, UTF8_DEST_MULTIPLIER, g_langInfo.GetGuiCharSet(), "UTF-8//IGNORE", source, dest);
476   }
477 }
478
479 void CCharsetConverter::wToUTF8(const CStdStringW& strSource, CStdStringA &strDest)
480 {
481   CSingleLock lock(m_critSection);
482   convert(m_iconvWtoUtf8,UTF8_DEST_MULTIPLIER,WCHAR_CHARSET,"UTF-8",strSource,strDest);
483 }
484
485 void CCharsetConverter::utf16BEtoUTF8(const CStdString16& strSource, CStdStringA &strDest)
486 {
487   CSingleLock lock(m_critSection);
488   if(!convert_checked(m_iconvUtf16BEtoUtf8,UTF8_DEST_MULTIPLIER,"UTF-16BE","UTF-8",strSource,strDest))
489     strDest.empty();
490 }
491
492 void CCharsetConverter::utf16LEtoUTF8(const CStdString16& strSource,
493                                       CStdStringA &strDest)
494 {
495   CSingleLock lock(m_critSection);
496   if(!convert_checked(m_iconvUtf16LEtoUtf8,UTF8_DEST_MULTIPLIER,"UTF-16LE","UTF-8",strSource,strDest))
497     strDest.empty();
498 }
499
500 void CCharsetConverter::ucs2ToUTF8(const CStdString16& strSource, CStdStringA& strDest)
501 {
502   CSingleLock lock(m_critSection);
503   if(!convert_checked(m_iconvUcs2CharsetToUtf8,UTF8_DEST_MULTIPLIER,"UCS-2LE","UTF-8",strSource,strDest))
504     strDest.empty();
505 }
506
507 void CCharsetConverter::utf16LEtoW(const CStdString16& strSource, CStdStringW &strDest)
508 {
509   CSingleLock lock(m_critSection);
510   if(!convert_checked(m_iconvUtf16LEtoW,sizeof(wchar_t),"UTF-16LE",WCHAR_CHARSET,strSource,strDest))
511     strDest.empty();
512 }
513
514 void CCharsetConverter::ucs2CharsetToStringCharset(const CStdStringW& strSource, CStdStringA& strDest, bool swap)
515 {
516   CStdStringW strCopy = strSource;
517   if (swap)
518   {
519     char* s = (char*) strCopy.c_str();
520
521     while (*s || *(s + 1))
522     {
523       char c = *s;
524       *s = *(s + 1);
525       *(s + 1) = c;
526
527       s++;
528       s++;
529     }
530   }
531   CSingleLock lock(m_critSection);
532   convert(m_iconvUcs2CharsetToStringCharset,4,"UTF-16LE",
533           g_langInfo.GetGuiCharSet(),strCopy,strDest);
534 }
535
536 void CCharsetConverter::utf32ToStringCharset(const unsigned long* strSource, CStdStringA& strDest)
537 {
538   CSingleLock lock(m_critSection);
539
540   if (m_iconvUtf32ToStringCharset == (iconv_t) - 1)
541   {
542     CStdString strCharset=g_langInfo.GetGuiCharSet();
543     m_iconvUtf32ToStringCharset = iconv_open(strCharset.c_str(), "UTF-32LE");
544   }
545
546   if (m_iconvUtf32ToStringCharset != (iconv_t) - 1)
547   {
548     const unsigned long* ptr=strSource;
549     while (*ptr) ptr++;
550     const char* src = (const char*) strSource;
551     size_t inBytes = (ptr-strSource+1)*4;
552
553     char *dst = strDest.GetBuffer(inBytes);
554     size_t outBytes = inBytes;
555
556     if (iconv_const(m_iconvUtf32ToStringCharset, &src, &inBytes, &dst, &outBytes) == (size_t)-1)
557     {
558       CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
559       strDest.ReleaseBuffer();
560       strDest = (const char *)strSource;
561       return;
562     }
563
564     if (iconv(m_iconvUtf32ToStringCharset, NULL, NULL, &dst, &outBytes) == (size_t)-1)
565     {
566       CLog::Log(LOGERROR, "%s failed cleanup", __FUNCTION__);
567       strDest.ReleaseBuffer();
568       strDest = (const char *)strSource;
569       return;
570     }
571
572     strDest.ReleaseBuffer();
573   }
574 }
575
576 // Taken from RFC2640
577 bool CCharsetConverter::isValidUtf8(const char *buf, unsigned int len)
578 {
579   const unsigned char *endbuf = (unsigned char*)buf + len;
580   unsigned char byte2mask=0x00, c;
581   int trailing=0; // trailing (continuation) bytes to follow
582
583   while ((unsigned char*)buf != endbuf)
584   {
585     c = *buf++;
586     if (trailing)
587       if ((c & 0xc0) == 0x80) // does trailing byte follow UTF-8 format ?
588       {
589         if (byte2mask) // need to check 2nd byte for proper range
590         {
591           if (c & byte2mask) // are appropriate bits set ?
592             byte2mask = 0x00;
593           else
594             return false;
595         }
596         trailing--;
597       }
598       else
599         return 0;
600     else
601       if ((c & 0x80) == 0x00) continue; // valid 1-byte UTF-8
602       else if ((c & 0xe0) == 0xc0)      // valid 2-byte UTF-8
603         if (c & 0x1e)                   //is UTF-8 byte in proper range ?
604           trailing = 1;
605         else
606           return false;
607       else if ((c & 0xf0) == 0xe0)      // valid 3-byte UTF-8
608        {
609         if (!(c & 0x0f))                // is UTF-8 byte in proper range ?
610           byte2mask = 0x20;             // if not set mask
611         trailing = 2;                   // to check next byte
612       }
613       else if ((c & 0xf8) == 0xf0)      // valid 4-byte UTF-8
614       {
615         if (!(c & 0x07))                // is UTF-8 byte in proper range ?
616           byte2mask = 0x30;             // if not set mask
617         trailing = 3;                   // to check next byte
618       }
619       else if ((c & 0xfc) == 0xf8)      // valid 5-byte UTF-8
620       {
621         if (!(c & 0x03))                // is UTF-8 byte in proper range ?
622           byte2mask = 0x38;             // if not set mask
623         trailing = 4;                   // to check next byte
624       }
625       else if ((c & 0xfe) == 0xfc)      // valid 6-byte UTF-8
626       {
627         if (!(c & 0x01))                // is UTF-8 byte in proper range ?
628           byte2mask = 0x3c;             // if not set mask
629         trailing = 5;                   // to check next byte
630       }
631       else
632         return false;
633   }
634   return trailing == 0;
635 }
636
637 bool CCharsetConverter::isValidUtf8(const CStdString& str)
638 {
639   return isValidUtf8(str.c_str(), str.size());
640 }
641
642 void CCharsetConverter::utf8logicalToVisualBiDi(const CStdStringA& strSource, CStdStringA& strDest)
643 {
644   logicalToVisualBiDi(strSource, strDest, FRIBIDI_CHAR_SET_UTF8, FRIBIDI_TYPE_RTL);
645 }
646
647 CStdStringA CCharsetConverter::utf8Left(const CStdStringA &source, int num_chars)
648 {
649   CStdStringA result;
650   CStdStringW wide;
651   utf8ToW(source, wide);
652   wToUTF8(wide.Left(num_chars), result);
653   return result;
654 }