• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kencodingdetector.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of the KDE libraries
00003 
00004     Copyright (C) 1999 Lars Knoll (knoll@kde.org)
00005     Copyright (C) 2003 Dirk Mueller (mueller@kde.org)
00006     Copyright (C) 2003 Apple Computer, Inc.
00007     Copyright (C) 2007 Nick Shaforostoff (shafff@ukr.net)
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022     Boston, MA 02110-1301, USA.
00023 */
00024 //----------------------------------------------------------------------------
00025 //
00026 // decoder for input stream
00027 
00028 #include "kencodingdetector.h"
00029 
00030 #undef DECODE_DEBUG
00031 //#define DECODE_DEBUG
00032 
00033 #define MAX_BUFFER 16*1024
00034 
00035 #include <assert.h>
00036 
00037 #include "guess_ja_p.h"
00038 
00039 #include <QRegExp>
00040 #include <QTextCodec>
00041 
00042 #include <kglobal.h>
00043 #include <kcharsets.h>
00044 #include <kdebug.h>
00045 #include <klocale.h>
00046 
00047 #include <ctype.h>
00048 
00049 enum MIB
00050 {
00051     MibLatin1  = 4,
00052     Mib8859_8  = 85,
00053     MibUtf8    = 106,
00054     MibUcs2    = 1000,
00055     MibUtf16   = 1015,
00056     MibUtf16BE = 1013,
00057     MibUtf16LE = 1014
00058 };
00059 
00060 static bool is16Bit(QTextCodec* codec)
00061 {
00062     switch (codec->mibEnum())
00063     {
00064     case MibUtf16:
00065     case MibUtf16BE:
00066     case MibUtf16LE:
00067     case MibUcs2:
00068         return true;
00069     default:
00070         return false;
00071     }
00072 }
00073 
00074 class KEncodingDetectorPrivate
00075 {
00076 public:
00077     QTextCodec *m_codec;
00078     QTextDecoder *m_decoder; // utf16
00079     QTextCodec *m_defaultCodec;
00080     QByteArray  m_storeDecoderName;
00081 
00082     KEncodingDetector::EncodingChoiceSource m_source;
00083     KEncodingDetector::AutoDetectScript m_autoDetectLanguage;
00084 
00085     bool m_visualRTL : 1;
00086     bool m_seenBody : 1;
00087     bool m_writtingHappened : 1;
00088     bool m_analyzeCalled : 1; //for decode()
00089     int m_multiByte;
00090 
00091     QByteArray m_bufferForDefferedEncDetection;
00092 
00093     KEncodingDetectorPrivate()
00094             : m_codec(QTextCodec::codecForMib(MibLatin1))
00095             , m_decoder(m_codec->makeDecoder())
00096             , m_defaultCodec(m_codec)
00097             , m_source(KEncodingDetector::DefaultEncoding)
00098             , m_autoDetectLanguage(KEncodingDetector::SemiautomaticDetection)
00099             , m_visualRTL(false)
00100             , m_seenBody(false)
00101             , m_writtingHappened(false)
00102             , m_analyzeCalled(false)
00103             , m_multiByte(0)
00104     {
00105     }
00106 
00107     KEncodingDetectorPrivate(QTextCodec* codec,KEncodingDetector::EncodingChoiceSource source, KEncodingDetector::AutoDetectScript script)
00108             : m_codec(codec)
00109             , m_decoder(m_codec->makeDecoder())
00110             , m_defaultCodec(m_codec)
00111             , m_source(source)
00112             , m_autoDetectLanguage(script)
00113             , m_visualRTL(false)
00114             , m_seenBody(false)
00115             , m_writtingHappened(false)
00116             , m_analyzeCalled(false)
00117             , m_multiByte(0)
00118     {
00119     }
00120 
00121     ~KEncodingDetectorPrivate()
00122     {
00123         delete m_decoder;
00124     }
00125 };
00126 
00127 
00128 static QByteArray automaticDetectionForArabic( const unsigned char* ptr, int size )
00129 {
00130     for ( int i = 0; i < size; ++i ) {
00131         if ( ( ptr[ i ] >= 0x80 && ptr[ i ] <= 0x9F ) || ptr[ i ] == 0xA1 || ptr[ i ] == 0xA2 || ptr[ i ] == 0xA3
00132              || ( ptr[ i ] >= 0xA5 && ptr[ i ] <= 0xAB ) || ( ptr[ i ] >= 0xAE && ptr[ i ] <= 0xBA )
00133              || ptr[ i ] == 0xBC || ptr[ i ] == 0xBD || ptr[ i ] == 0xBE || ptr[ i ] == 0xC0
00134              || ( ptr[ i ] >= 0xDB && ptr[ i ] <= 0xDF ) || ( ptr[ i ] >= 0xF3 ) ) {
00135             return "cp1256";
00136         }
00137     }
00138 
00139     return "iso-8859-6";
00140 }
00141 
00142 static QByteArray automaticDetectionForBaltic( const unsigned char* ptr, int size )
00143 {
00144     for ( int i = 0; i < size; ++i ) {
00145         if ( ( ptr[ i ] >= 0x80 && ptr[ i ] <= 0x9E ) )
00146              return "cp1257";
00147 
00148         if ( ptr[ i ] == 0xA1 || ptr[ i ] == 0xA5 )
00149             return "iso-8859-13";
00150     }
00151 
00152     return "iso-8859-13";
00153 }
00154 
00155 static QByteArray automaticDetectionForCentralEuropean(const unsigned char* ptr, int size )
00156 {
00157     QByteArray charset = QByteArray();
00158     for ( int i = 0; i < size; ++i ) {
00159         if ( ptr[ i ] >= 0x80 && ptr[ i ] <= 0x9F ) {
00160             if ( ptr[ i ] == 0x81 || ptr[ i ] == 0x83 || ptr[ i ] == 0x90 || ptr[ i ] == 0x98 )
00161                 return "ibm852";
00162 
00163             if ( i + 1 > size )
00164                 return "cp1250";
00165             else { // maybe ibm852 ?
00166                 charset = "cp1250";
00167                 continue;
00168             }
00169         }
00170         if ( ptr[ i ] == 0xA5 || ptr[ i ] == 0xAE || ptr[ i ] == 0xBE || ptr[ i ] == 0xC3 || ptr[ i ] == 0xD0 || ptr[ i ] == 0xE3 || ptr[ i ] == 0xF0 ) {
00171             if ( i + 1 > size )
00172                 return "iso-8859-2";
00173             else {  // maybe ibm852 ?
00174                 if ( charset.isNull() )
00175                     charset = "iso-8859-2";
00176                 continue;
00177             }
00178         }
00179     }
00180 
00181     if ( charset.isNull() )
00182         charset = "iso-8859-3";
00183 
00184     return charset.data();
00185 }
00186 
00187 static QByteArray automaticDetectionForCyrillic( const unsigned char* ptr, int size)
00188 {
00189 #ifdef DECODE_DEBUG
00190         kWarning() << "KEncodingDetector: Cyr heuristics";
00191 #endif
00192 
00193 //     if (ptr[0]==0xef && ptr[1]==0xbb && ptr[2]==0xbf)
00194 //         return "utf8";
00195     int utf8_mark=0;
00196     int koi_score=0;
00197     int cp1251_score=0;
00198 
00199     int koi_st=0;
00200     int cp1251_st=0;
00201 
00202 //     int koi_na=0;
00203 //     int cp1251_na=0;
00204 
00205     int koi_o_capital=0;
00206     int koi_o=0;
00207     int cp1251_o_capital=0;
00208     int cp1251_o=0;
00209 
00210     int koi_a_capital=0;
00211     int koi_a=0;
00212     int cp1251_a_capital=0;
00213     int cp1251_a=0;
00214 
00215     int koi_s_capital=0;
00216     int koi_s=0;
00217     int cp1251_s_capital=0;
00218     int cp1251_s=0;
00219 
00220     int koi_i_capital=0;
00221     int koi_i=0;
00222     int cp1251_i_capital=0;
00223     int cp1251_i=0;
00224 
00225     int cp1251_small_range=0;
00226     int koi_small_range=0;
00227     int ibm866_small_range=0;
00228 
00229     int i;
00230     for (i=1; (i<size) && (cp1251_small_range+koi_small_range<1000) ;++i)
00231     {
00232         if (ptr[i]>0xdf)
00233         {
00234             ++cp1251_small_range;
00235 
00236             if (ptr[i]==0xee)//small o
00237                 ++cp1251_o;
00238             else if (ptr[i]==0xe0)//small a
00239                 ++cp1251_a;
00240             else if (ptr[i]==0xe8)//small i
00241                 ++cp1251_i;
00242             else if (ptr[i]==0xf1)//small s
00243                 ++cp1251_s;
00244             else if (ptr[i]==0xf2 && ptr[i-1]==0xf1)//small st
00245                 ++cp1251_st;
00246 
00247             else if (ptr[i]==0xef)
00248                 ++koi_o_capital;
00249             else if (ptr[i]==0xe1)
00250                 ++koi_a_capital;
00251             else if (ptr[i]==0xe9)
00252                 ++koi_i_capital;
00253             else if (ptr[i]==0xf3)
00254                 ++koi_s_capital;
00255 
00256         }
00257         else if (ptr[i]>0xbf)
00258         {
00259             ++koi_small_range;
00260 
00261             if (ptr[i]==0xd0||ptr[i]==0xd1)//small o
00262                 ++utf8_mark;
00263             else if (ptr[i]==0xcf)//small o
00264                 ++koi_o;
00265             else if (ptr[i]==0xc1)//small a
00266                 ++koi_a;
00267             else if (ptr[i]==0xc9)//small i
00268                 ++koi_i;
00269             else if (ptr[i]==0xd3)//small s
00270                 ++koi_s;
00271             else if (ptr[i]==0xd4 && ptr[i-1]==0xd3)//small st
00272                 ++koi_st;
00273 
00274             else if (ptr[i]==0xce)
00275                 ++cp1251_o_capital;
00276             else if (ptr[i]==0xc0)
00277                 ++cp1251_a_capital;
00278             else if (ptr[i]==0xc8)
00279                 ++cp1251_i_capital;
00280             else if (ptr[i]==0xd1)
00281                 ++cp1251_s_capital;
00282         }
00283         else if (ptr[i]>0x9f && ptr[i]<0xb0) //first 16 letterz is 60%
00284             ++ibm866_small_range;
00285 
00286     }
00287 
00288     //cannot decide?
00289     if (cp1251_small_range+koi_small_range+ibm866_small_range<8)
00290     {
00291         return "";
00292     }
00293 
00294     if (3*utf8_mark>cp1251_small_range+koi_small_range+ibm866_small_range)
00295     {
00296 #ifdef DECODE_DEBUG
00297         kWarning() << "Cyr Enc Detection: UTF8";
00298 #endif
00299         return "UTF-8";
00300     }
00301 
00302     if (ibm866_small_range>cp1251_small_range+koi_small_range)
00303         return "ibm866";
00304 
00305 //     QByteArray koi_string = "koi8-u";
00306 //     QByteArray cp1251_string = "cp1251";
00307 
00308     if (cp1251_st==0 && koi_st>1)
00309         koi_score+=10;
00310     else if (koi_st==0 && cp1251_st>1)
00311         cp1251_score+=10;
00312 
00313     if (cp1251_st && koi_st)
00314     {
00315         if (cp1251_st/koi_st>2)
00316             cp1251_score+=20;
00317         else if (koi_st/cp1251_st>2)
00318             koi_score+=20;
00319     }
00320 
00321     if (cp1251_a>koi_a)
00322         cp1251_score+=10;
00323     else if (cp1251_a || koi_a)
00324         koi_score+=10;
00325 
00326     if (cp1251_o>koi_o)
00327         cp1251_score+=10;
00328     else if (cp1251_o || koi_o)
00329         koi_score+=10;
00330 
00331     if (cp1251_i>koi_i)
00332         cp1251_score+=10;
00333     else if (cp1251_i || koi_i)
00334         koi_score+=10;
00335 
00336     if (cp1251_s>koi_s)
00337         cp1251_score+=10;
00338     else if (cp1251_s || koi_s)
00339         koi_score+=10;
00340 
00341     if (cp1251_a_capital>koi_a_capital)
00342         cp1251_score+=9;
00343     else if (cp1251_a_capital || koi_a_capital)
00344         koi_score+=9;
00345 
00346     if (cp1251_o_capital>koi_o_capital)
00347         cp1251_score+=9;
00348     else if (cp1251_o_capital || koi_o_capital)
00349         koi_score+=9;
00350 
00351     if (cp1251_i_capital>koi_i_capital)
00352         cp1251_score+=9;
00353     else if (cp1251_i_capital || koi_i_capital)
00354         koi_score+=9;
00355 
00356     if (cp1251_s_capital>koi_s_capital)
00357         cp1251_score+=9;
00358     else if (cp1251_s_capital || koi_s_capital)
00359         koi_score+=9;
00360 #ifdef DECODE_DEBUG
00361     kWarning()<<"koi_score " << koi_score << " cp1251_score " << cp1251_score;
00362 #endif
00363     if (abs(koi_score-cp1251_score)<10)
00364     {
00365         //fallback...
00366         cp1251_score=cp1251_small_range;
00367         koi_score=koi_small_range;
00368     }
00369     if (cp1251_score>koi_score)
00370         return "cp1251";
00371     else
00372         return "koi8-u";
00373 
00374 
00375 //     if (cp1251_score>koi_score)
00376 //         setEncoding("cp1251",AutoDetectedEncoding);
00377 //     else
00378 //         setEncoding("koi8-u",AutoDetectedEncoding);
00379 //     return true;
00380 
00381 }
00382 
00383 static QByteArray automaticDetectionForGreek( const unsigned char* ptr, int size )
00384 {
00385     for ( int i = 0; i < size; ++i ) {
00386         if ( ptr[ i ] == 0x80 || ( ptr[ i ] >= 0x82 && ptr[ i ] <= 0x87 ) || ptr[ i ] == 0x89 || ptr[ i ] == 0x8B
00387              || ( ptr[ i ] >= 0x91 && ptr[ i ] <= 0x97 ) || ptr[ i ] == 0x99 || ptr[ i ] == 0x9B || ptr[ i ] == 0xA4
00388              || ptr[ i ] == 0xA5 || ptr[ i ] == 0xAE ) {
00389             return "cp1253";
00390         }
00391     }
00392 
00393     return "iso-8859-7";
00394 }
00395 
00396 static QByteArray automaticDetectionForHebrew( const unsigned char* ptr, int size )
00397 {
00398     for ( int i = 0; i < size; ++i ) {
00399         if ( ptr[ i ] == 0x80 || ( ptr[ i ] >= 0x82 && ptr[ i ] <= 0x89 ) || ptr[ i ] == 0x8B
00400              || ( ptr[ i ] >= 0x91 && ptr[ i ] <= 0x99 ) || ptr[ i ] == 0x9B || ptr[ i ] == 0xA1 || ( ptr[ i ] >= 0xBF && ptr[ i ] <= 0xC9 )
00401              || ( ptr[ i ] >= 0xCB && ptr[ i ] <= 0xD8 ) ) {
00402             return "cp1255";
00403         }
00404 
00405         if ( ptr[ i ] == 0xDF )
00406             return "iso-8859-8-i";
00407     }
00408 
00409     return "iso-8859-8-i";
00410 }
00411 
00412 static QByteArray automaticDetectionForJapanese( const unsigned char* ptr, int size )
00413 {
00414     JapaneseCode kc;
00415 
00416     switch ( kc.guess_jp( (const char*)ptr, size ) ) {
00417     case JapaneseCode::JIS:
00418         return "jis7";
00419     case JapaneseCode::EUC:
00420         return "eucjp";
00421     case JapaneseCode::SJIS:
00422         return "sjis";
00423      case JapaneseCode::UTF8:
00424         return "utf8";
00425     default:
00426         break;
00427     }
00428 
00429     return "";
00430 }
00431 
00432 static QByteArray automaticDetectionForTurkish( const unsigned char* ptr, int size )
00433 {
00434     for ( int i = 0; i < size; ++i ) {
00435         if ( ptr[ i ] == 0x80 || ( ptr[ i ] >= 0x82 && ptr[ i ] <= 0x8C ) || ( ptr[ i ] >= 0x91 && ptr[ i ] <= 0x9C ) || ptr[ i ] == 0x9F ) {
00436             return "cp1254";
00437         }
00438     }
00439 
00440     return "iso-8859-9";
00441 }
00442 
00443 static QByteArray automaticDetectionForWesternEuropean( const unsigned char* ptr, int size )
00444 {
00445     --size;
00446     uint nonansi_count=0;
00447     for (int i=0; i<size; ++i)
00448     {
00449         if (ptr[i]>0x79)
00450         {
00451              ++nonansi_count;
00452             if ( ptr[i]>0xc1 && ptr[i]<0xf0 && ptr[i+1]>0x7f && ptr[i+1]<0xc0)
00453             {
00454                 return "UTF-8";
00455             }
00456             if (ptr[i] >= 0x78 && ptr[i]<=0x9F )
00457             {
00458                 return "cp1252";
00459             }
00460         }
00461 
00462     }
00463 
00464     if (nonansi_count>0)
00465         return "iso-8859-15";
00466 
00467     return "";
00468 }
00469 
00470 // Other browsers allow comments in the head section, so we need to also.
00471 // It's important not to look for tags inside the comments.
00472 static void skipComment(const char *&ptr, const char *pEnd)
00473 {
00474     const char *p = ptr;
00475     // Allow <!-->; other browsers do.
00476     if (*p=='>')
00477     {
00478         p++;
00479     }
00480     else
00481     {
00482         while (p!=pEnd)
00483         {
00484             if (*p=='-')
00485             {
00486                 // This is the real end of comment, "-->".
00487                 if (p[1]=='-' && p[2]=='>')
00488                 {
00489                     p += 3;
00490                     break;
00491                 }
00492                 // This is the incorrect end of comment that other browsers allow, "--!>".
00493                 if (p[1] == '-' && p[2] == '!' && p[3] == '>')
00494                 {
00495                     p += 4;
00496                     break;
00497                 }
00498             }
00499             p++;
00500         }
00501     }
00502     ptr=p;
00503 }
00504 
00505 // Returns the position of the encoding string.
00506 static int findXMLEncoding(const QByteArray &str, int &encodingLength)
00507 {
00508     int len = str.length();
00509     int pos = str.indexOf("encoding");
00510     if (pos == -1)
00511         return -1;
00512     pos += 8;
00513 
00514     // Skip spaces and stray control characters.
00515     while (pos<len && str[pos]<=' ')
00516         ++pos;
00517 
00518     //Bail out if nothing after
00519     // Skip equals sign.
00520     if (pos>=len || str[pos] != '=')
00521         return -1;
00522     ++pos;
00523 
00524     // Skip spaces and stray control characters.
00525     while (pos<len && str[pos]<=' ')
00526         ++pos;
00527 
00528     //Bail out if nothing after
00529     if (pos >= len)
00530         return -1;
00531 
00532     // Skip quotation mark.
00533     char quoteMark = str[pos];
00534     if (quoteMark != '"' && quoteMark != '\'')
00535         return -1;
00536     ++pos;
00537 
00538     // Find the trailing quotation mark.
00539     int end=pos;
00540     while (end<len && str[end]!=quoteMark)
00541         ++end;
00542 
00543     if (end>=len)
00544         return -1;
00545 
00546     encodingLength = end-pos;
00547     return pos;
00548 }
00549 
00550 bool KEncodingDetector::processNull(char *data, int len)
00551 {
00552     bool bin=false;
00553     if(is16Bit(d->m_codec))
00554     {
00555         for (int i=1; i < len; i+=2)
00556         {
00557             if ((data[i]=='\0') && (data[i-1]=='\0'))
00558             {
00559                 bin=true;
00560                 data[i]=' ';
00561             }
00562         }
00563         return bin;
00564     }
00565     // replace '\0' by spaces, for buggy pages
00566     int i = len-1;
00567     while(--i>=0)
00568     {
00569         if(data[i]==0)
00570         {
00571             bin=true;
00572             data[i]=' ';
00573         }
00574     }
00575     return bin;
00576 }
00577 
00578 
00579 bool KEncodingDetector::errorsIfUtf8 (const char* data, int length)
00580 {
00581     if (d->m_codec->mibEnum()!=MibUtf8)
00582         return false; //means no errors
00583 // #define highest1Bits (unsigned char)0x80
00584 // #define highest2Bits (unsigned char)0xC0
00585 // #define highest3Bits (unsigned char)0xE0
00586 // #define highest4Bits (unsigned char)0xF0
00587 // #define highest5Bits (unsigned char)0xF8
00588 static const unsigned char highest1Bits = 0x80;
00589 static const unsigned char highest2Bits = 0xC0;
00590 static const unsigned char highest3Bits = 0xE0;
00591 static const unsigned char highest4Bits = 0xF0;
00592 static const unsigned char highest5Bits = 0xF8;
00593 
00594     for (int i=0; i<length; ++i)
00595     {
00596         unsigned char c = data[i];
00597 
00598         if (d->m_multiByte>0)
00599         {
00600             if ((c & highest2Bits) == 0x80)
00601             {
00602                 --(d->m_multiByte);
00603                 continue;
00604             }
00605 #ifdef DECODE_DEBUG
00606             kWarning() << "EncDetector: Broken UTF8";
00607 #endif
00608             return true;
00609         }
00610 
00611         // most significant bit zero, single char
00612         if ((c & highest1Bits) == 0x00)
00613             continue;
00614 
00615         // 110xxxxx => init 1 following bytes
00616         if ((c & highest3Bits) == 0xC0)
00617         {
00618             d->m_multiByte = 1;
00619             continue;
00620         }
00621 
00622         // 1110xxxx => init 2 following bytes
00623         if ((c & highest4Bits) == 0xE0)
00624         {
00625             d->m_multiByte = 2;
00626             continue;
00627         }
00628 
00629         // 11110xxx => init 3 following bytes
00630         if ((c & highest5Bits) == 0xF0)
00631         {
00632             d->m_multiByte = 3;
00633             continue;
00634         }
00635 #ifdef DECODE_DEBUG
00636         kWarning() << "EncDetector:_Broken UTF8";
00637 #endif
00638         return true;
00639     }
00640     return false;
00641 }
00642 
00643 
00644 KEncodingDetector::KEncodingDetector() : d(new KEncodingDetectorPrivate)
00645 {
00646 }
00647 
00648 KEncodingDetector::KEncodingDetector(QTextCodec* codec, EncodingChoiceSource source, AutoDetectScript script) :
00649     d(new KEncodingDetectorPrivate(codec,source,script))
00650 {
00651 }
00652 
00653 KEncodingDetector::~KEncodingDetector()
00654 {
00655     delete d;
00656 }
00657 
00658 void KEncodingDetector::setAutoDetectLanguage( KEncodingDetector::AutoDetectScript lang)
00659 {
00660     d->m_autoDetectLanguage=lang;
00661 }
00662 KEncodingDetector::AutoDetectScript KEncodingDetector::autoDetectLanguage() const
00663 {
00664     return d->m_autoDetectLanguage;
00665 }
00666 
00667 KEncodingDetector::EncodingChoiceSource KEncodingDetector::encodingChoiceSource() const
00668 {
00669     return d->m_source;
00670 }
00671 
00672 const char* KEncodingDetector::encoding() const
00673 {
00674     d->m_storeDecoderName = d->m_codec->name();
00675     return d->m_storeDecoderName.constData();
00676 }
00677 
00678 bool KEncodingDetector::visuallyOrdered() const
00679 {
00680     return d->m_visualRTL;
00681 }
00682 
00683 // const QTextCodec* KEncodingDetector::codec() const
00684 // {
00685 //     return d->m_codec;
00686 // }
00687 
00688 QTextDecoder* KEncodingDetector::decoder()
00689 {
00690     return d->m_decoder;
00691 }
00692 
00693 bool KEncodingDetector::setEncoding(const char *_encoding, EncodingChoiceSource type)
00694 {
00695     QTextCodec *codec;
00696     QByteArray enc(_encoding);
00697     if(/*enc.isNull() || */enc.isEmpty())
00698     {
00699         if (type==DefaultEncoding)
00700             codec=d->m_defaultCodec;
00701         else
00702             return false;
00703     }
00704     else
00705     {
00706         //QString->QTextCodec
00707 
00708         enc = enc.toLower();
00709          // hebrew visually ordered
00710         if(enc=="visual")
00711             enc="iso8859-8";
00712         bool b;
00713         codec = KGlobal::charsets()->codecForName(enc, b);
00714         if (!b)
00715         return false;
00716     }
00717 
00718     if (d->m_codec->mibEnum()==codec->mibEnum())
00719         return true;
00720 
00721     if ((type==EncodingFromMetaTag || type==EncodingFromXMLHeader) && is16Bit(codec))
00722     {
00723         //Sometimes the codec specified is absurd, i.e. UTF-16 despite
00724         //us decoding a meta tag as ASCII. In that case, ignore it.
00725         return false;
00726     }
00727 
00728     if (codec->mibEnum() == Mib8859_8)
00729     {
00730         //We do NOT want to use Qt's QHebrewCodec, since it tries to reorder itself.
00731         codec = QTextCodec::codecForName("iso8859-8-i");
00732 
00733         // visually ordered unless one of the following
00734         if(!(enc=="iso-8859-8-i"||enc=="iso_8859-8-i"||enc=="csiso88598i"||enc=="logical"))
00735             d->m_visualRTL = true;
00736     }
00737 
00738     d->m_codec = codec;
00739     d->m_source = type;
00740     delete d->m_decoder;
00741     d->m_decoder = d->m_codec->makeDecoder();
00742 #ifdef DECODE_DEBUG
00743     kDebug(6005) << "KEncodingDetector::encoding used is" << d->m_codec->name();
00744 #endif
00745     return true;
00746 }
00747 
00748 QString KEncodingDetector::decode(const char *data, int len)
00749 {
00750     processNull(const_cast<char *>(data),len);
00751     if (!d->m_analyzeCalled)
00752     {
00753         analyze(data,len);
00754         d->m_analyzeCalled=true;
00755     }
00756 
00757     return d->m_decoder->toUnicode(data,len);
00758 }
00759 
00760 QString KEncodingDetector::decode(const QByteArray &data)
00761 {
00762     processNull(const_cast<char *>(data.data()),data.size());
00763     if (!d->m_analyzeCalled)
00764     {
00765         analyze(data.data(),data.size());
00766         d->m_analyzeCalled=true;
00767     }
00768 
00769     return d->m_decoder->toUnicode(data);
00770 }
00771 
00772 QString KEncodingDetector::decodeWithBuffering(const char *data, int len)
00773 {
00774 #ifdef DECODE_DEBUG
00775         kWarning() << "KEncodingDetector: decoding "<<len<<" bytes";
00776 #endif
00777     if (d->m_writtingHappened)
00778     {
00779 #ifdef DECODE_DEBUG
00780         kWarning() << "KEncodingDetector: d->m_writtingHappened "<< d->m_codec->name();
00781 #endif
00782         processNull(const_cast<char *>(data),len);
00783         return d->m_decoder->toUnicode(data, len);
00784     }
00785     else
00786     {
00787         if (d->m_bufferForDefferedEncDetection.isEmpty())
00788         {
00789             if (analyze(data,len)
00790                 && (d->m_seenBody
00791                     || !(d->m_source==AutoDetectedEncoding
00792                         ||d->m_source==DefaultEncoding
00793                         )
00794                    )
00795                )//dontWannaSeeHead()
00796             {
00797 #ifdef DECODE_DEBUG
00798                 kWarning() << "KEncodingDetector: m_writtingHappened first time "<< d->m_codec->name();
00799 #endif
00800                 processNull(const_cast<char *>(data),len);
00801                 d->m_writtingHappened=true;
00802                 return d->m_decoder->toUnicode(data, len);
00803             }
00804             else
00805             {
00806 #ifdef DECODE_DEBUG
00807                 kWarning() << "KEncodingDetector: begin deffer";
00808 #endif
00809                 d->m_bufferForDefferedEncDetection=data;
00810             }
00811         }
00812         else
00813         {
00814             d->m_bufferForDefferedEncDetection+=data;
00815             if ( (analyze(data,len)
00816                   && (d->m_seenBody
00817                      || !(d->m_source==AutoDetectedEncoding
00818                         ||d->m_source==DefaultEncoding
00819                          )
00820                      )
00821                  ) || d->m_bufferForDefferedEncDetection.length()>MAX_BUFFER
00822                )//dontWannaSeeHead()
00823             {
00824                 d->m_writtingHappened=true;
00825                 d->m_bufferForDefferedEncDetection.replace('\0',' ');
00826                 QString result(d->m_decoder->toUnicode(d->m_bufferForDefferedEncDetection));
00827                 d->m_bufferForDefferedEncDetection.clear();
00828 #ifdef DECODE_DEBUG
00829                 kWarning() << "KEncodingDetector: m_writtingHappened in the middle " << d->m_codec->name();
00830 #endif
00831                 return result;
00832             }
00833         }
00834     }
00835 
00836     return QString();
00837 }
00838 
00839 QString KEncodingDetector::flush()
00840 {
00841     if (d->m_bufferForDefferedEncDetection.isEmpty())
00842         return QString();
00843 
00844     d->m_bufferForDefferedEncDetection.replace('\0',' ');
00845     QString result(d->m_decoder->toUnicode(d->m_bufferForDefferedEncDetection));
00846     d->m_bufferForDefferedEncDetection.clear();
00847 #ifdef DECODE_DEBUG
00848     kWarning() << "KEncodingDetector:flush() "<< d->m_bufferForDefferedEncDetection.length()<<" bytes "<< d->m_codec->name();
00849 #endif
00850     return result;
00851 }
00852 
00853 bool KEncodingDetector::analyze(const char *data, int len)
00854 {
00855     // Check for UTF-16 or UTF-8 BOM mark at the beginning, which is a sure sign of a Unicode encoding.
00856     // maximumBOMLength = 10
00857     // Even if the user has chosen utf16 we still need to auto-detect the endianness
00858     if (len >= 10 && ((d->m_source != UserChosenEncoding) || is16Bit(d->m_codec)))
00859     {
00860         // Extract the first three bytes.
00861         const uchar *udata = (const uchar *)data;
00862         uchar c1 = *udata++;
00863         uchar c2 = *udata++;
00864         uchar c3 = *udata++;
00865 
00866         // Check for the BOM
00867         const char *autoDetectedEncoding;
00868         if ((c1 == 0xFE && c2 == 0xFF) || (c1 == 0xFF && c2 == 0xFE))
00869         {
00870             autoDetectedEncoding = "ISO-10646-UCS-2";
00871         }
00872         else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF)
00873         {
00874             autoDetectedEncoding = "UTF-8";
00875         }
00876         else if (c1 == 0x00 || c2 == 0x00)
00877         {
00878             uchar c4 = *udata++;
00879             uchar c5 = *udata++;
00880             uchar c6 = *udata++;
00881             uchar c7 = *udata++;
00882             uchar c8 = *udata++;
00883             uchar c9 = *udata++;
00884             uchar c10 = *udata++;
00885 
00886             int nul_count_even = (c2 != 0) + (c4 != 0) + (c6 != 0) + (c8 != 0) + (c10 != 0);
00887             int nul_count_odd = (c1 != 0) + (c3 != 0) + (c5 != 0) + (c7 != 0) + (c9 != 0);
00888             if ((nul_count_even==0 && nul_count_odd==5) || (nul_count_even==5 && nul_count_odd==0))
00889                 autoDetectedEncoding = "ISO-10646-UCS-2";
00890             else
00891                 autoDetectedEncoding = 0;
00892         }
00893         else
00894         {
00895             autoDetectedEncoding = 0;
00896         }
00897 
00898         // If we found a BOM, use the encoding it implies.
00899         if (autoDetectedEncoding != 0)
00900         {
00901             d->m_source = BOM;
00902             d->m_codec = QTextCodec::codecForName(autoDetectedEncoding);
00903             assert(d->m_codec);
00904             //enc = d->m_codec->name();
00905             delete d->m_decoder;
00906             d->m_decoder = d->m_codec->makeDecoder();
00907 #ifdef DECODE_DEBUG
00908             kWarning() << "Detection by BOM";
00909 #endif
00910             if (is16Bit(d->m_codec) && c2==0x00)
00911             {
00912                 // utf16LE, we need to put the decoder in LE mode
00913                 char reverseUtf16[3] = {(char)0xFF, (char)0xFE, 0x00};
00914                 d->m_decoder->toUnicode(reverseUtf16, 2);
00915             }
00916             return true;
00917         }
00918     }
00919 
00920     //exit from routine in case it was called to only detect byte order for utf-16
00921     if (d->m_source==UserChosenEncoding)
00922     {
00923 #ifdef DECODE_DEBUG
00924         kWarning() << "KEncodingDetector: UserChosenEncoding exit ";
00925 #endif
00926 
00927         if (errorsIfUtf8(data, len))
00928             setEncoding("",DefaultEncoding);
00929         return true;
00930     }
00931 
00932     if (!d->m_seenBody)
00933     {
00934         // we still don't have an encoding, and are in the head
00935         // the following tags are allowed in <head>:
00936         // SCRIPT|STYLE|META|LINK|OBJECT|TITLE|BASE
00937         const char *ptr = data;
00938         const char *pEnd = data+len;
00939 
00940         while(ptr != pEnd)
00941         {
00942             if(*ptr!='<')
00943             {
00944                 ++ptr;
00945                 continue;
00946             }
00947             ++ptr;
00948             // Handle comments.
00949             if (ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-')
00950             {
00951                 ptr += 3;
00952                 skipComment(ptr, pEnd);
00953                 continue;
00954             }
00955 
00956             // Handle XML header, which can have encoding in it.
00957             if (ptr[0]=='?' && ptr[1]=='x' && ptr[2]=='m' && ptr[3]=='l')
00958             {
00959                 const char *end = ptr;
00960                 while (*end != '>' && end < pEnd)
00961                     end++;
00962                 if (*end == '\0' || end == pEnd)
00963                     break;
00964                 QByteArray str(ptr, end - ptr); // qbytearray provides the \0 terminator
00965                 int length;
00966                 int pos = findXMLEncoding(str, length);
00967                 // also handles the case when specified encoding aint correct
00968                 if (pos!=-1 && setEncoding(str.mid(pos, length), EncodingFromXMLHeader))
00969                 {
00970                     return true;
00971                 }
00972             }
00973 
00974             //look for <meta>, stop if we reach <body>
00975             while (
00976                         !((*ptr >= 'a') && (*ptr <= 'z') ||
00977                         (*ptr >= 'A') && (*ptr <= 'Z'))
00978                         && ptr < pEnd
00979                 )
00980                 ++ptr;
00981 
00982             char tmp[5];
00983             int length=0;
00984             const char* max=ptr+4;
00985             if (pEnd<max)
00986                 max=pEnd;
00987             while (
00988                         ((*ptr >= 'a') && (*ptr <= 'z') ||
00989                         (*ptr >= 'A') && (*ptr <= 'Z') ||
00990                         (*ptr >= '0') && (*ptr <= '9'))
00991                         && ptr < max
00992                 )
00993             {
00994                 tmp[length] = tolower( *ptr );
00995                 ++ptr;
00996                 ++length;
00997             }
00998             tmp[length] = 0;
00999             if (tmp[0]=='m'&&tmp[1]=='e'&&tmp[2]=='t'&&tmp[3]=='a')
01000             {
01001                 // found a meta tag...
01002                 const char* end = ptr;
01003                 while(*end != '>' && *end != '\0' && end<pEnd)
01004                     end++;
01005                 //if ( *end == '\0' ) break;
01006                 QByteArray str( ptr, (end-ptr)+1);
01007                 str = str.toLower();
01008                 int pos=0;
01009                         //if( (pos = str.find("http-equiv", pos)) == -1) break;
01010                         //if( (pos = str.find("content-type", pos)) == -1) break;
01011                 if( (pos = str.indexOf("charset")) == -1)
01012                     continue;
01013                 pos+=6;
01014                 // skip to '='
01015                 if( (pos = str.indexOf("=", pos)) == -1)
01016                     continue;
01017 
01018                 // skip whitespace before encoding itself
01019                 while (pos < (int)str.length() && str[pos] <= ' ')
01020                     ++pos;
01021                 if ( pos == (int)str.length())
01022                     continue;
01023 
01024                 int endpos = pos;
01025                 while( endpos < str.length() &&
01026                         (str[endpos] != ' ' && str[endpos] != '"' && str[endpos] != '\''
01027                                     && str[endpos] != ';' && str[endpos] != '>') )
01028                     ++endpos;
01029     #ifdef DECODE_DEBUG
01030                 kDebug( 6005 ) << "KEncodingDetector: found charset in <meta>: " << str.mid(pos,endpos-pos).data();
01031     #endif
01032                 if (setEncoding(str.mid(pos,endpos-pos), EncodingFromMetaTag))
01033                     return true;
01034             }
01035             else if (tmp[0]=='b'&&tmp[1]=='o'&&tmp[2]=='d'&&tmp[3]=='y')
01036             {
01037                 d->m_seenBody=true;
01038                 break;
01039             }
01040         }
01041     }
01042 
01043     if (d->m_source==EncodingFromHTTPHeader)
01044         return true;
01045 
01046     if (len<20)
01047     {
01048         setEncoding("",DefaultEncoding);
01049         return false;
01050     }
01051 #ifdef DECODE_DEBUG
01052     kDebug( 6005 ) << "KEncodingDetector: using heuristics (" << strlen(data) << ")";
01053 #endif
01054 
01055     switch ( d->m_autoDetectLanguage)
01056     {
01057         case KEncodingDetector::Arabic:
01058             return setEncoding(automaticDetectionForArabic( (const unsigned char*) data, len ), AutoDetectedEncoding);
01059 //             break;
01060         case KEncodingDetector::Baltic:
01061             return setEncoding(automaticDetectionForBaltic( (const unsigned char*) data, len ), AutoDetectedEncoding);
01062 //             break;
01063         case KEncodingDetector::CentralEuropean:
01064             return setEncoding(automaticDetectionForCentralEuropean( (const unsigned char*) data, len ), AutoDetectedEncoding);
01065             break;
01066         case KEncodingDetector::Cyrillic:
01067             return setEncoding(automaticDetectionForCyrillic( (const unsigned char*) data, len), AutoDetectedEncoding);
01068 //             break;
01069         case KEncodingDetector::Greek:
01070             return setEncoding(automaticDetectionForGreek( (const unsigned char*) data, len ), AutoDetectedEncoding);
01071 //             break;
01072         case KEncodingDetector::Hebrew:
01073             return setEncoding(automaticDetectionForHebrew( (const unsigned char*) data, len ), AutoDetectedEncoding);
01074 //             break;
01075         case KEncodingDetector::Japanese:
01076             return setEncoding(automaticDetectionForJapanese( (const unsigned char*) data, len ), AutoDetectedEncoding);
01077 //             break;
01078         case KEncodingDetector::Turkish:
01079             return setEncoding(automaticDetectionForTurkish( (const unsigned char*) data, len ), AutoDetectedEncoding);
01080 //             break;
01081         case KEncodingDetector::WesternEuropean:
01082             if (setEncoding(automaticDetectionForWesternEuropean( (const unsigned char*) data, len ), AutoDetectedEncoding))
01083                 return true;
01084             else if (d->m_defaultCodec->mibEnum()==MibLatin1) //detection for khtml
01085             {
01086                 return setEncoding("iso-8859-15",AutoDetectedEncoding);
01087             }
01088             else //use default provided by eg katepart
01089             {
01090                 return setEncoding("",DefaultEncoding);
01091             }
01092 //             break;
01093         case KEncodingDetector::SemiautomaticDetection:
01094         case KEncodingDetector::ChineseSimplified:
01095         case KEncodingDetector::ChineseTraditional:
01096         case KEncodingDetector::Korean:
01097         case KEncodingDetector::Thai:
01098         case KEncodingDetector::Unicode:
01099         case KEncodingDetector::NorthernSaami:
01100         case KEncodingDetector::SouthEasternEurope:
01101         case KEncodingDetector::None:
01102             // huh. somethings broken in this code ### FIXME
01103             //enc = 0; //Reset invalid codec we tried, so we get back to latin1 fallback.
01104             break;
01105         }
01106 
01107         setEncoding("",DefaultEncoding);
01108         return true;
01109 }
01110 
01111 
01112 KEncodingDetector::AutoDetectScript KEncodingDetector::scriptForName(const QString& lang)
01113 {
01114     if (lang.isEmpty())
01115         return KEncodingDetector::None;
01116     else if (lang==i18nc("@item Text character set", "Unicode"))
01117         return KEncodingDetector::Unicode;
01118     else if (lang==i18nc("@item Text character set", "Cyrillic"))
01119         return KEncodingDetector::Cyrillic;
01120     else if (lang==i18nc("@item Text character set", "Western European"))
01121         return KEncodingDetector::WesternEuropean;
01122     else if (lang==i18nc("@item Text character set", "Central European"))
01123         return KEncodingDetector::CentralEuropean;
01124     else if (lang==i18nc("@item Text character set", "Greek"))
01125         return KEncodingDetector::Greek;
01126     else if (lang==i18nc("@item Text character set", "Hebrew"))
01127         return KEncodingDetector::Hebrew;
01128     else if (lang==i18nc("@item Text character set", "Turkish"))
01129         return KEncodingDetector::Turkish;
01130     else if (lang==i18nc("@item Text character set", "Japanese"))
01131         return KEncodingDetector::Japanese;
01132     else if (lang==i18nc("@item Text character set", "Baltic"))
01133         return KEncodingDetector::Baltic;
01134     else if (lang==i18nc("@item Text character set", "Arabic"))
01135         return KEncodingDetector::Arabic;
01136 
01137     return KEncodingDetector::None;
01138 }
01139 
01140 bool KEncodingDetector::hasAutoDetectionForScript(KEncodingDetector::AutoDetectScript script)
01141 {
01142     switch (script)
01143     {
01144         case KEncodingDetector::Arabic:
01145             return true;
01146         case KEncodingDetector::Baltic:
01147             return true;
01148         case KEncodingDetector::CentralEuropean:
01149             return true;
01150         case KEncodingDetector::Cyrillic:
01151             return true;
01152         case KEncodingDetector::Greek:
01153             return true;
01154         case KEncodingDetector::Hebrew:
01155             return true;
01156         case KEncodingDetector::Japanese:
01157             return true;
01158         case KEncodingDetector::Turkish:
01159             return true;
01160         case KEncodingDetector::WesternEuropean:
01161             return true;
01162         case KEncodingDetector::ChineseTraditional:
01163             return true;
01164         case KEncodingDetector::ChineseSimplified:
01165             return true;
01166         case KEncodingDetector::Unicode:
01167             return true;
01168             break;
01169         default:
01170             return false;
01171     }
01172 }
01173 
01174 QString KEncodingDetector::nameForScript(KEncodingDetector::AutoDetectScript script)
01175 {
01176     switch (script)
01177     {
01178         case KEncodingDetector::Arabic:
01179             return i18nc("@item Text character set", "Arabic");
01180             break;
01181         case KEncodingDetector::Baltic:
01182             return i18nc("@item Text character set", "Baltic");
01183             break;
01184         case KEncodingDetector::CentralEuropean:
01185             return i18nc("@item Text character set", "Central European");
01186             break;
01187         case KEncodingDetector::Cyrillic:
01188             return i18nc("@item Text character set", "Cyrillic");
01189             break;
01190         case KEncodingDetector::Greek:
01191             return i18nc("@item Text character set", "Greek");
01192             break;
01193         case KEncodingDetector::Hebrew:
01194             return i18nc("@item Text character set", "Hebrew");
01195             break;
01196         case KEncodingDetector::Japanese:
01197             return i18nc("@item Text character set", "Japanese");
01198             break;
01199         case KEncodingDetector::Turkish:
01200             return i18nc("@item Text character set", "Turkish");
01201             break;
01202         case KEncodingDetector::WesternEuropean:
01203             return i18nc("@item Text character set", "Western European");
01204             break;
01205         case KEncodingDetector::ChineseTraditional:
01206             return i18nc("@item Text character set", "Chinese Traditional");
01207             break;
01208         case KEncodingDetector::ChineseSimplified:
01209             return i18nc("@item Text character set", "Chinese Simplified");
01210             break;
01211         case KEncodingDetector::Korean:
01212             return i18nc("@item Text character set", "Korean");
01213             break;
01214         case KEncodingDetector::Thai:
01215             return i18nc("@item Text character set", "Thai");
01216             break;
01217         case KEncodingDetector::Unicode:
01218             return i18nc("@item Text character set", "Unicode");
01219             break;
01220         //case KEncodingDetector::SemiautomaticDetection:
01221         default:
01222             return QString();
01223 
01224         }
01225 }
01226 
01227 #undef DECODE_DEBUG
01228 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal