2006-09-21 Anders Carlsson <acarlsson@apple.com>
[WebKit-https.git] / WebCore / platform / TextEncoding.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "TextEncoding.h"
29
30 #include "CString.h"
31 #include "PlatformString.h"
32 #include "StreamingTextDecoder.h"
33 #include "TextDecoder.h"
34 #include "TextEncodingRegistry.h"
35 #include <unicode/unorm.h>
36 #include <wtf/HashSet.h>
37 #include <wtf/OwnPtr.h>
38
39 namespace WebCore {
40
41 static void addEncodingName(HashSet<const char*>& set, const char* name)
42 {
43     const char* atomicName = atomicCanonicalTextEncodingName(name);
44     if (atomicName)
45         set.add(atomicName);
46 }
47
48 TextEncoding::TextEncoding(const char* name)
49     : m_name(atomicCanonicalTextEncodingName(name))
50 {
51 }
52
53 TextEncoding::TextEncoding(const String& name)
54     : m_name(atomicCanonicalTextEncodingName(name.characters(), name.length()))
55 {
56 }
57
58 String TextEncoding::decode(const char* data, size_t length) const
59 {
60     if (!m_name)
61         return String();
62
63     return TextDecoder(*this).decode(data, length, true);
64 }
65
66 CString TextEncoding::encode(const UChar* characters, size_t length, bool allowEntities) const
67 {
68     if (!m_name)
69         return CString();
70
71     if (!length)
72         return "";
73
74     // FIXME: What's the right place to do normalization?
75     // It's a little strange to do it inside the encode function.
76     // Perhaps normalization should be an explicit step done before calling encode.
77
78     const UChar* source = characters;
79     size_t sourceLength = length;
80
81     Vector<UChar> normalizedCharacters;
82
83     UErrorCode err = U_ZERO_ERROR;
84     if (unorm_quickCheck(source, sourceLength, UNORM_NFC, &err) != UNORM_YES) {
85         // First try using the length of the original string, since normalization to NFC rarely increases length.
86         normalizedCharacters.resize(sourceLength);
87         int32_t normalizedLength = unorm_normalize(source, length, UNORM_NFC, 0, normalizedCharacters.data(), length, &err);
88         if (err == U_BUFFER_OVERFLOW_ERROR) {
89             err = U_ZERO_ERROR;
90             normalizedCharacters.resize(normalizedLength);
91             normalizedLength = unorm_normalize(source, length, UNORM_NFC, 0, normalizedCharacters.data(), normalizedLength, &err);
92         }
93         ASSERT(U_SUCCESS(err));
94
95         source = normalizedCharacters.data();
96         sourceLength = normalizedLength;
97     }
98
99     return newTextCodec(*this)->encode(source, sourceLength, allowEntities);
100 }
101
102 bool TextEncoding::usesVisualOrdering() const
103 {
104     static const char* const a = atomicCanonicalTextEncodingName("ISO-8859-8");
105     return m_name == a;
106 }
107
108 bool TextEncoding::isJapanese() const
109 {
110     static HashSet<const char*> set;
111     if (set.isEmpty()) {
112         addEncodingName(set, "x-mac-japanese");
113         addEncodingName(set, "cp932");
114         addEncodingName(set, "JIS_X0201");
115         addEncodingName(set, "JIS_X0208-1983");
116         addEncodingName(set, "JIS_X0208-1990");
117         addEncodingName(set, "JIS_X0212-1990");
118         addEncodingName(set, "JIS_C6226-1978");
119         addEncodingName(set, "Shift_JIS_X0213-2000");
120         addEncodingName(set, "ISO-2022-JP");
121         addEncodingName(set, "ISO-2022-JP-2");
122         addEncodingName(set, "ISO-2022-JP-1");
123         addEncodingName(set, "ISO-2022-JP-3");
124         addEncodingName(set, "EUC-JP");
125         addEncodingName(set, "Shift_JIS");
126     }
127     return m_name && set.contains(m_name);
128 }
129
130 UChar TextEncoding::backslashAsCurrencySymbol() const
131 {
132     // The text encodings below treat backslash as a currency symbol.
133     // See http://blogs.msdn.com/michkap/archive/2005/09/17/469941.aspx for more information.
134     static const char* const a = atomicCanonicalTextEncodingName("Shift_JIS_X0213-2000");
135     static const char* const b = atomicCanonicalTextEncodingName("EUC-JP");
136     return (m_name == a || m_name == b) ? 0x00A5 : '\\';
137 }
138
139 const TextEncoding& TextEncoding::closest8BitEquivalent() const
140 {
141     if (*this == UTF16BigEndianEncoding() || *this == UTF16LittleEndianEncoding())
142         return UTF8Encoding();
143     return *this;
144 }
145
146 const TextEncoding& ASCIIEncoding()
147 {
148     static TextEncoding globalASCIIEncoding("ASCII");
149     return globalASCIIEncoding;
150 }
151
152 const TextEncoding& Latin1Encoding()
153 {
154     static TextEncoding globalLatin1Encoding("Latin-1");
155     return globalLatin1Encoding;
156 }
157
158 const TextEncoding& UTF16BigEndianEncoding()
159 {
160     static TextEncoding globalUTF16BigEndianEncoding("UTF-16BE");
161     return globalUTF16BigEndianEncoding;
162 }
163
164 const TextEncoding& UTF16LittleEndianEncoding()
165 {
166     static TextEncoding globalUTF16LittleEndianEncoding("UTF-16LE");
167     return globalUTF16LittleEndianEncoding;
168 }
169
170 const TextEncoding& UTF8Encoding()
171 {
172     static TextEncoding globalUTF8Encoding("UTF-8");
173     return globalUTF8Encoding;
174 }
175
176 const TextEncoding& WindowsLatin1Encoding()
177 {
178     static TextEncoding globalWindowsLatin1Encoding("WinLatin-1");
179     return globalWindowsLatin1Encoding;
180 }
181
182 } // namespace WebCore