chardet2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,292 @@
1
+ ######################## BEGIN LICENSE BLOCK ########################
2
+ # The Original Code is mozilla.org code.
3
+ #
4
+ # The Initial Developer of the Original Code is
5
+ # Netscape Communications Corporation.
6
+ # Portions created by the Initial Developer are Copyright (C) 1998
7
+ # the Initial Developer. All Rights Reserved.
8
+ #
9
+ # Contributor(s):
10
+ # Hui (zhengzhengzheng@gmail.com) - port to Ruby
11
+ # Mark Pilgrim - first port to Python
12
+ #
13
+ # This library is free software; you can redistribute it and/or
14
+ # modify it under the terms of the GNU Lesser General Public
15
+ # License as published by the Free Software Foundation; either
16
+ # version 2.1 of the License, or (at your option) any later version.
17
+ #
18
+ # This library is distributed in the hope that it will be useful,
19
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
+ # Lesser General Public License for more details.
22
+ #
23
+ # You should have received a copy of the GNU Lesser General Public
24
+ # License along with this library; if not, write to the Free Software
25
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26
+ # 02110-1301 USA
27
+ ######################### END LICENSE BLOCK #########################
28
+
29
+ require 'UniversalDetector'
30
+ require 'CharSetProber'
31
+
32
+ module UniversalDetector
33
+ # This prober doesn't actually recognize a language or a charset.
34
+ # It is a helper prober for the use of the Hebrew model probers
35
+
36
+ ### General ideas of the Hebrew charset recognition ###
37
+ #
38
+ # Four main charsets exist in Hebrew:
39
+ # "ISO-8859-8" - Visual Hebrew
40
+ # "windows-1255" - Logical Hebrew
41
+ # "ISO-8859-8-I" - Logical Hebrew
42
+ # "x-mac-hebrew" - ?? Logical Hebrew ??
43
+ #
44
+ # Both "ISO" charsets use a completely identical set of code points, whereas
45
+ # "windows-1255" and "x-mac-hebrew" are two different proper supersets of
46
+ # these code points. windows-1255 defines additional characters in the range
47
+ # 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific
48
+ # diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6.
49
+ # x-mac-hebrew defines similar additional code points but with a different
50
+ # mapping.
51
+ #
52
+ # As far as an average Hebrew text with no diacritics is concerned, all four
53
+ # charsets are identical with respect to code points. Meaning that for the
54
+ # main Hebrew alphabet, all four map the same values to all 27 Hebrew letters
55
+ # (including final letters).
56
+ #
57
+ # The dominant difference between these charsets is their directionality.
58
+ # "Visual" directionality means that the text is ordered as if the renderer is
59
+ # not aware of a BIDI rendering algorithm. The renderer sees the text and
60
+ # draws it from left to right. The text itself when ordered naturally is read
61
+ # backwards. A buffer of Visual Hebrew generally looks like so:
62
+ # "[last word of first line spelled backwards] [whole line ordered backwards
63
+ # and spelled backwards] [first word of first line spelled backwards]
64
+ # [end of line] [last word of second line] ... etc' "
65
+ # adding punctuation marks, numbers and English text to visual text is
66
+ # naturally also "visual" and from left to right.
67
+ #
68
+ # "Logical" directionality means the text is ordered "naturally" according to
69
+ # the order it is read. It is the responsibility of the renderer to display
70
+ # the text from right to left. A BIDI algorithm is used to place general
71
+ # punctuation marks, numbers and English text in the text.
72
+ #
73
+ # Texts in x-mac-hebrew are almost impossible to find on the Internet. From
74
+ # what little evidence I could find, it seems that its general directionality
75
+ # is Logical.
76
+ #
77
+ # To sum up all of the above, the Hebrew probing mechanism knows about two
78
+ # charsets:
79
+ # Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are
80
+ # backwards while line order is natural. For charset recognition purposes
81
+ # the line order is unimportant (In fact, for this implementation, even
82
+ # word order is unimportant).
83
+ # Logical Hebrew - "windows-1255" - normal, naturally ordered text.
84
+ #
85
+ # "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be
86
+ # specifically identified.
87
+ # "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew
88
+ # that contain special punctuation marks or diacritics is displayed with
89
+ # some unconverted characters showing as question marks. This problem might
90
+ # be corrected using another model prober for x-mac-hebrew. Due to the fact
91
+ # that x-mac-hebrew texts are so rare, writing another model prober isn't
92
+ # worth the effort and performance hit.
93
+ #
94
+ #### The Prober ####
95
+ #
96
+ # The prober is divided between two SBCharSetProbers and a HebrewProber,
97
+ # all of which are managed, created, fed data, inquired and deleted by the
98
+ # SBCSGroupProber. The two SBCharSetProbers identify that the text is in
99
+ # fact some kind of Hebrew, Logical or Visual. The final decision about which
100
+ # one is it is made by the HebrewProber by combining final-letter scores
101
+ # with the scores of the two SBCharSetProbers to produce a final answer.
102
+ #
103
+ # The SBCSGroupProber is responsible for stripping the original text of HTML
104
+ # tags, English characters, numbers, low-ASCII punctuation characters, spaces
105
+ # and new lines. It reduces any sequence of such characters to a single space.
106
+ # The buffer fed to each prober in the SBCS group prober is pure text in
107
+ # high-ASCII.
108
+ # The two SBCharSetProbers (model probers) share the same language model:
109
+ # Win1255Model.
110
+ # The first SBCharSetProber uses the model normally as any other
111
+ # SBCharSetProber does, to recognize windows-1255, upon which this model was
112
+ # built. The second SBCharSetProber is told to make the pair-of-letter
113
+ # lookup in the language model backwards. This in practice exactly simulates
114
+ # a visual Hebrew model using the windows-1255 logical Hebrew model.
115
+ #
116
+ # The HebrewProber is not using any language model. All it does is look for
117
+ # final-letter evidence suggesting the text is either logical Hebrew or visual
118
+ # Hebrew. Disjointed from the model probers, the results of the HebrewProber
119
+ # alone are meaningless. HebrewProber always returns 0.00 as confidence
120
+ # since it never identifies a charset by it@ Instead, the pointer to the
121
+ # HebrewProber is passed to the model probers as a helper "Name Prober".
122
+ # When the Group prober receives a positive identification from any prober,
123
+ # it asks for the name of the charset identified. If the prober queried is a
124
+ # Hebrew model prober, the model prober forwards the call to the
125
+ # HebrewProber to make the final decision. In the HebrewProber, the
126
+ # decision is made according to the final-letters scores maintained and Both
127
+ # model probers scores. The answer is returned in the form of the name of the
128
+ # charset identified, either "windows-1255" or "ISO-8859-8".
129
+
130
+ # windows-1255 / ISO-8859-8 code points of interest
131
+ FINAL_KAF = '\xea'
132
+ NORMAL_KAF = '\xeb'
133
+ FINAL_MEM = '\xed'
134
+ NORMAL_MEM = '\xee'
135
+ FINAL_NUN = '\xef'
136
+ NORMAL_NUN = '\xf0'
137
+ FINAL_PE = '\xf3'
138
+ NORMAL_PE = '\xf4'
139
+ FINAL_TSADI = '\xf5'
140
+ NORMAL_TSADI = '\xf6'
141
+
142
+ # Minimum Visual vs Logical final letter score difference.
143
+ # If the difference is below this, don't rely solely on the final letter score distance.
144
+ MIN_FINAL_CHAR_DISTANCE = 5
145
+
146
+ # Minimum Visual vs Logical model score difference.
147
+ # If the difference is below this, don't rely at all on the model score distance.
148
+ MIN_MODEL_DISTANCE = 0.01
149
+
150
+ VISUAL_HEBREW_NAME = "ISO-8859-8"
151
+ LOGICAL_HEBREW_NAME = "windows-1255"
152
+
153
+ class HebrewProber < CharSetProber
154
+ def initialize
155
+ super
156
+ @_mLogicalProber = nil
157
+ @_mVisualProber = nil
158
+ reset()
159
+ end
160
+
161
+ def reset
162
+ @_mFinalCharLogicalScore = 0
163
+ @_mFinalCharVisualScore = 0
164
+ # The two last characters seen in the previous buffer,
165
+ # mPrev and mBeforePrev are initialized to space in order to simulate a word
166
+ # delimiter at the beginning of the data
167
+ @_mPrev = ' '
168
+ @_mBeforePrev = ' '
169
+ # These probers are owned by the group prober.
170
+ end
171
+
172
+ def set_model_probers(logicalProber, visualProber)
173
+ @_mLogicalProber = logicalProber
174
+ @_mVisualProber = visualProber
175
+ end
176
+
177
+ def is_final(c)
178
+ return [FINAL_KAF, FINAL_MEM, FINAL_NUN, FINAL_PE, FINAL_TSADI].include?(c)
179
+ end
180
+
181
+ def is_non_final(c)
182
+ # The normal Tsadi is not a good Non-Final letter due to words like
183
+ # 'lechotet' (to chat) containing an apostrophe after the tsadi. This
184
+ # apostrophe is converted to a space in FilterWithoutEnglishLetters causing
185
+ # the Non-Final tsadi to appear at an end of a word even though this is not
186
+ # the case in the original text.
187
+ # The letters Pe and Kaf rarely display a related behavior of not being a
188
+ # good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' for
189
+ # example legally end with a Non-Final Pe or Kaf. However, the benefit of
190
+ # these letters as Non-Final letters outweighs the damage since these words
191
+ # are quite rare.
192
+ return [NORMAL_KAF, NORMAL_MEM, NORMAL_NUN, NORMAL_PE].include?(c)
193
+ end
194
+
195
+ def feed(aBuf)
196
+ # Final letter analysis for logical-visual decision.
197
+ # Look for evidence that the received buffer is either logical Hebrew or
198
+ # visual Hebrew.
199
+ # The following cases are checked:
200
+ # 1) A word longer than 1 letter, ending with a final letter. This is an
201
+ # indication that the text is laid out "naturally" since the final letter
202
+ # really appears at the end. +1 for logical score.
203
+ # 2) A word longer than 1 letter, ending with a Non-Final letter. In normal
204
+ # Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, should not end with
205
+ # the Non-Final form of that letter. Exceptions to this rule are mentioned
206
+ # above in isNonFinal(). This is an indication that the text is laid out
207
+ # backwards. +1 for visual score
208
+ # 3) A word longer than 1 letter, starting with a final letter. Final letters
209
+ # should not appear at the beginning of a word. This is an indication that
210
+ # the text is laid out backwards. +1 for visual score.
211
+ #
212
+ # The visual score and logical score are accumulated throughout the text and
213
+ # are finally checked against each other in GetCharSetName().
214
+ # No checking for final letters in the middle of words is done since that case
215
+ # is not an indication for either Logical or Visual text.
216
+ #
217
+ # We automatically filter out all 7-bit characters (replace them with spaces)
218
+ # so the word boundary detection works properly. [MAP]
219
+
220
+ if get_state() == :NotMe
221
+ # Both model probers say it's not them. No reason to continue.
222
+ return :NotMe
223
+ end
224
+
225
+ aBuf = filter_high_bit_only(aBuf)
226
+
227
+ for cur in aBuf
228
+ if cur == ' '
229
+ # We stand on a space - a word just ended
230
+ if @_mBeforePrev != ' '
231
+ # next-to-last char was not a space so @_mPrev is not a 1 letter word
232
+ if is_final(@_mPrev)
233
+ # case (1) [-2:not space][-1:final letter][cur:space]
234
+ @_mFinalCharLogicalScore += 1
235
+ elsif is_non_final(@_mPrev)
236
+ # case (2) [-2:not space][-1:Non-Final letter][cur:space]
237
+ @_mFinalCharVisualScore += 1
238
+ end
239
+ end
240
+ else
241
+ # Not standing on a space
242
+ if (@_mBeforePrev == ' ') and (is_final(@_mPrev)) and (cur != ' ')
243
+ # case (3) [-2:space][-1:final letter][cur:not space]
244
+ @_mFinalCharVisualScore += 1
245
+ end
246
+ end
247
+ @_mBeforePrev = @_mPrev
248
+ @_mPrev = cur
249
+ end
250
+
251
+ # Forever detecting, till the end or until both model probers return eNotMe (handled above)
252
+ return :Detecting
253
+ end
254
+
255
+ def get_charset_name
256
+ # Make the decision: is it Logical or Visual?
257
+ # If the final letter score distance is dominant enough, rely on it.
258
+ finalsub = @_mFinalCharLogicalScore - @_mFinalCharVisualScore
259
+ if finalsub >= MIN_FINAL_CHAR_DISTANCE
260
+ return LOGICAL_HEBREW_NAME
261
+ end
262
+ if finalsub <= -MIN_FINAL_CHAR_DISTANCE
263
+ return VISUAL_HEBREW_NAME
264
+ end
265
+
266
+ # It's not dominant enough, try to rely on the model scores instead.
267
+ modelsub = @_mLogicalProber.get_confidence() - @_mVisualProber.get_confidence()
268
+ if modelsub > MIN_MODEL_DISTANCE
269
+ return LOGICAL_HEBREW_NAME
270
+ end
271
+ if modelsub < -MIN_MODEL_DISTANCE
272
+ return VISUAL_HEBREW_NAME
273
+ end
274
+
275
+ # Still no good, back to final letter distance, maybe it'll save the day.
276
+ if finalsub < 0.0
277
+ return VISUAL_HEBREW_NAME
278
+ end
279
+
280
+ # (finalsub > 0 - Logical) or (don't know what to do) default to Logical.
281
+ return LOGICAL_HEBREW_NAME
282
+ end
283
+
284
+ def get_state
285
+ # Remain active as long as any of the model probers are active.
286
+ if (@_mLogicalProber.get_state() == :NotMe) and (@_mVisualProber.get_state() == :NotMe)
287
+ return :NotMe
288
+ end
289
+ return :Detecting
290
+ end
291
+ end
292
+ end