Ticket #4501: rtfPreprocess.2.py

File rtfPreprocess.2.py, 15.8 KB (added by sanyi, 8 months ago)

version 1.01

Line 
1"""
2RTF tokenizer and token parser. v.1.01 (1/18/2010)
3Author: Gerendi Sandor Attila
4License: GPL3 (http://www.gnu.org/licenses/gpl-3.0.html)
5
6At this point this will tokenize a RTF file then rebuild it from the tokens.
7In the process the UTF8 tokens are altered to be supported by the RTF2XML and also remain RTF specification compilant.
8"""
9class tokenDelimitatorStart():
10    def __init__(self):
11        pass
12    def toRTF(self):
13        return b'{'
14    def __repr__(self):
15        return '{'
16
17class tokenDelimitatorEnd():
18    def __init__(self):
19        pass
20    def toRTF(self):
21        return b'}'
22    def __repr__(self):
23        return '}'
24
25class tokenControlWord():
26    def __init__(self, name, separator = ''):
27        self.name = name
28        self.separator = separator
29    def toRTF(self):
30        return self.name + self.separator
31    def __repr__(self):
32        return self.name + self.separator
33   
34class tokenControlWordWithNumericArgument():
35    def __init__(self, name, argument, separator = ''): 
36        self.name = name
37        self.argument = argument
38        self.separator = separator
39    def toRTF(self):
40        return self.name + repr(self.argument) + self.separator
41    def __repr__(self):
42        return self.name + repr(self.argument) + self.separator
43       
44class tokenControlSymbol():
45    def __init__(self, name):
46        self.name = name
47    def toRTF(self):
48        return self.name
49    def __repr__(self):
50        return self.name
51       
52class tokenData():
53    def __init__(self, data):
54        self.data = data
55    def toRTF(self):
56        return self.data
57    def __repr__(self):
58        return self.data       
59       
60class tokenBinN():
61    def __init__(self, data, separator = ''):
62        self.data = data
63        self.separator = separator
64    def toRTF(self):
65        return "\\bin" + repr(len(self.data)) + self.separator + self.data
66    def __repr__(self):
67        return "\\bin" + repr(len(self.data)) + self.separator + self.data
68   
69class token8bitChar():
70    def __init__(self, value):
71        self.value = value
72    def toRTF(self):
73        return "\\'" + hex(self.value)[2:4]
74    def __repr__(self):
75        return "\\'" + hex(self.value)[2:4]
76       
77class tokenUnicode():
78    def __init__(self, value, separator = '', current_ucn = 1, eqList = []):
79        self.value = value
80        self.separator = separator
81        self.current_ucn = current_ucn
82        self.eqList = eqList
83    def toRTF(self):
84        result = '\\u' + repr(self.value) + ' '
85        ucn = self.current_ucn
86        if len(self.eqList) < ucn:
87            ucn = len(self.eqList)
88            result =  tokenControlWordWithNumericArgument('\\uc', ucn).toRTF() + result       
89        i = 0
90        for eq in self.eqList:
91            if i >= ucn:
92                break
93            result = result + eq.toRTF()
94        return result
95    def __repr__(self):
96        return '\\u' + repr(self.data)
97       
98
99def isAsciiLetter(value):
100    return ((value >= 'a') and (value <= 'z')) or ((value >= 'A') and (value <= 'Z'))
101               
102def isDigit(value):
103    return (value >= '0') and (value <= '9')   
104
105def isChar(value, char):
106    return value == char
107
108def isString(buffer, string):
109    return buffer == string
110
111
112class RtfTokenParser():
113    def __init__(self, tokens):
114        self.tokens = tokens
115        self.process()
116        self.processUnicode()
117        self.processUnicode4Calibre()
118        #self.processRomanianAndHungarian4Calibre()
119       
120    def process(self):
121        i = 0
122        newTokens = []
123        while i < len(self.tokens):
124            if isinstance(self.tokens[i], tokenControlSymbol):
125                if isString(self.tokens[i].name, "\\'"):
126                    i = i + 1
127                    if not isinstance(self.tokens[i], tokenData):
128                        raise BaseException('Error: token8bitChar without data.')
129                    if len(self.tokens[i].data) < 2:
130                        raise BaseException('Error: token8bitChar without data.')
131                    newTokens.append(token8bitChar(int(self.tokens[i].data[0:2], 16)))
132                    if len(self.tokens[i].data) > 2:
133                        newTokens.append(tokenData(self.tokens[i].data[2:]))
134                    i = i + 1
135                    continue
136           
137            newTokens.append(self.tokens[i])
138            i = i + 1
139           
140        self.tokens = list(newTokens)
141       
142    def processUnicode(self):
143        i = 0
144        newTokens = []
145        ucNbStack = [1]
146        while i < len(self.tokens):
147            if isinstance(self.tokens[i], tokenDelimitatorStart):
148                ucNbStack.append(ucNbStack[len(ucNbStack) - 1])
149                newTokens.append(self.tokens[i])
150                i = i + 1
151                continue
152            if isinstance(self.tokens[i], tokenDelimitatorEnd):
153                ucNbStack.pop()
154                newTokens.append(self.tokens[i])
155                i = i + 1
156                continue
157            if isinstance(self.tokens[i], tokenControlWordWithNumericArgument):
158                if isString(self.tokens[i].name, '\\uc'):
159                    ucNbStack[len(ucNbStack) - 1] = self.tokens[i].argument
160                    newTokens.append(self.tokens[i])
161                    i = i + 1
162                    continue
163                if isString(self.tokens[i].name, '\\u'):
164                    x = i
165                    j = 0
166                    i = i + 1
167                    replace = []
168                    partialData = None
169                    ucn = ucNbStack[len(ucNbStack) - 1]
170                    while (i < len(self.tokens)) and (j < ucn):
171                        if isinstance(self.tokens[i], tokenDelimitatorStart):
172                            break
173                        if isinstance(self.tokens[i], tokenDelimitatorEnd):
174                            break
175                        if isinstance(self.tokens[i], tokenData):
176                            if len(self.tokens[i].data) >= ucn - j:
177                                replace.append(tokenData(self.tokens[i].data[0 : ucn - j]))
178                                if len(self.tokens[i].data) > ucn - j:
179                                    partialData = tokenData(self.tokens[i].data[ucn - j:])
180                                i = i + 1
181                                break
182                            else:
183                                replace.append(self.tokens[i])
184                                j = j + len(self.tokens[i].data)
185                                i = i + 1
186                                continue
187                        if isinstance(self.tokens[i], token8bitChar) or isinstance(self.tokens[i], tokenBinN):
188                            replace.append(self.tokens[i])
189                            i = i + 1
190                            j = j + 1
191                            continue
192                        raise BaseException('Error: incorect utf replacement.')                           
193                   
194                    newTokens.append(tokenUnicode(self.tokens[x].argument, self.tokens[x].separator, ucn, replace))
195                    if partialData != None:
196                        newTokens.append(partialData)
197                    continue
198           
199            newTokens.append(self.tokens[i])
200            i = i + 1
201       
202        self.tokens = list(newTokens) 
203       
204    def processUnicode4Calibre(self):
205        i = 0
206        toRemove = []
207        while i < len(self.tokens):
208            if isinstance(self.tokens[i], tokenControlWordWithNumericArgument):
209                if isString(self.tokens[i].name, '\\uc'):
210                    toRemove.append(i)
211                    i = i + 1
212                    continue 
213            if isinstance(self.tokens[i], tokenUnicode):
214                self.tokens[i].eqList = []
215                self.tokens[i].current_ucn = 0
216            i = i + 1
217        for i in toRemove:
218            self.tokens.remove(self.tokens[i])
219        i = 0
220        while i < len(self.tokens):
221            if isinstance(self.tokens[i], tokenControlWordWithNumericArgument):
222                if isString(self.tokens[i].name, '\\rtf'):
223                    self.tokens.insert(i + 1, tokenControlWordWithNumericArgument('\\uc', 0))
224                    break
225            i = i + 1
226       
227           
228    def processRomanianAndHungarian4Calibre(self):
229        i = 0
230        while i < len(self.tokens):
231            if isinstance(self.tokens[i], token8bitChar):
232                if self.tokens[i].value == 0xe3:
233                    self.tokens[i] = tokenUnicode(259, current_ucn = 0)
234                    i = i + 1
235                    continue
236                if self.tokens[i].value == 0xc3:
237                    self.tokens[i] = tokenUnicode(258, current_ucn = 0)
238                    i = i + 1
239                    continue
240                if self.tokens[i].value == 0xfe:
241                    self.tokens[i] = tokenUnicode(539, current_ucn = 0)
242                    i = i + 1
243                    continue
244                if self.tokens[i].value == 0xde:
245                    self.tokens[i] = tokenUnicode(538, current_ucn = 0)
246                    i = i + 1
247                    continue
248                if self.tokens[i].value == 0xba:
249                    self.tokens[i] = tokenUnicode(537, current_ucn = 0)
250                    i = i + 1
251                    continue
252                if self.tokens[i].value == 0xaa:
253                    self.tokens[i] = tokenUnicode(536, current_ucn = 0)
254                    i = i + 1
255                    continue
256                if self.tokens[i].value == 0xf5:
257                    self.tokens[i] = tokenUnicode(337, current_ucn = 0)
258                    i = i + 1
259                    continue
260                if self.tokens[i].value == 0xd5:
261                    self.tokens[i] = tokenUnicode(336, current_ucn = 0)
262                    i = i + 1
263                    continue
264                if self.tokens[i].value == 0xfb:
265                    self.tokens[i] = tokenUnicode(369, current_ucn = 0)
266                    i = i + 1
267                    continue
268                if self.tokens[i].value == 0xdb:
269                    self.tokens[i] = tokenUnicode(368, current_ucn = 0)
270                    i = i + 1
271                    continue
272               
273            i = i + 1       
274           
275    def toRTF(self):
276        result = []
277        for token in self.tokens:
278            result.append(token.toRTF())
279        return "".join(result)
280                       
281                   
282class RtfTokenizer():
283    def __init__(self, rtfData):
284        self.rtfData = []
285        self.tokens = []
286        self.rtfData = rtfData
287        self.tokenize()             
288           
289    def tokenize(self):
290        i = 0
291        lastDataStart = -1                   
292        while i < len(self.rtfData):
293                                   
294            if isChar(self.rtfData[i], '{'): 
295                if lastDataStart > -1:
296                    self.tokens.append(tokenData(self.rtfData[lastDataStart : i]))                 
297                    lastDataStart = -1             
298                self.tokens.append(tokenDelimitatorStart())                                       
299                i = i + 1
300                continue
301           
302            if isChar(self.rtfData[i], '}'):
303                if lastDataStart > -1:                 
304                    self.tokens.append(tokenData(self.rtfData[lastDataStart : i]))
305                    lastDataStart = -1
306                self.tokens.append(tokenDelimitatorEnd())
307                i = i + 1
308                continue
309           
310            if isChar(self.rtfData[i], '\\'):               
311                if i + 1 >= len(self.rtfData):
312                    raise BaseException('Error: Control character found at the end of the document.')
313               
314                if lastDataStart > -1:                 
315                    self.tokens.append(tokenData(self.rtfData[lastDataStart : i]))
316                    lastDataStart = -1
317               
318                tokenStart = i
319                i = i + 1                               
320           
321                #Control Words
322                if isAsciiLetter(self.rtfData[i]):               
323                    #consume <ASCII Letter Sequence>
324                    consumed = False
325                    while i < len(self.rtfData):
326                        if not isAsciiLetter(self.rtfData[i]):
327                            tokenEnd = i
328                            consumed = True
329                            break
330                        i = i + 1                   
331                   
332                    if not consumed:
333                        raise BaseException('Error (at:%d): Control Word without end.'%(tokenStart))
334                   
335                    #we have numeric argument before delimiter   
336                    if isChar(self.rtfData[i], '-') or isDigit(self.rtfData[i]):
337                        #consume the numeric argument
338                        consumed = False
339                        l = 0
340                        while i < len(self.rtfData):
341                            if not isDigit(self.rtfData[i]):
342                                consumed = True
343                                break
344                            l = l + 1
345                            i = i + 1
346                            if l > 10 :
347                                raise BaseException('Error (at:%d): Too many digits in control word numeric argument.'%[tokenStart])
348                           
349                        if not consumed:
350                            raise BaseException('Error (at:%d): Control Word without numeric argument end.'%[tokenStart])
351                   
352                    separator = ''
353                    if isChar(self.rtfData[i], ' '):
354                        separator = ' ' 
355                         
356                    controlWord = self.rtfData[tokenStart: tokenEnd]
357                    if tokenEnd < i: 
358                        value = int(self.rtfData[tokenEnd: i])
359                        if isString(controlWord, "\\bin"):
360                            i = i + value
361                            self.tokens.append(tokenBinN(self.rtfData[tokenStart:i], separator))
362                        else: 
363                            self.tokens.append(tokenControlWordWithNumericArgument(controlWord, value, separator))                                                       
364                    else:
365                        self.tokens.append(tokenControlWord(controlWord, separator))
366                    #space delimiter, we should discard it
367                    if self.rtfData[i] == ' ':
368                        i = i + 1                       
369                                 
370                #Control Symbol
371                else:                                           
372                    self.tokens.append(tokenControlSymbol(self.rtfData[tokenStart : i + 1]))
373                    i = i + 1                   
374                continue
375                   
376            if lastDataStart < 0:
377                lastDataStart = i                                                         
378            i = i + 1
379           
380    def toRTF(self):
381        result = []
382        for token in self.tokens:
383            result.append(token.toRTF())
384        return "".join(result)
385           
386
387if __name__ == "__main__":
388    import sys
389    if len(sys.argv) < 2:
390        print ("Ussage %prog rtfFileToConvert")
391        sys.exit()
392       
393    fileName = sys.argv[1] 
394   
395    f = open(fileName, 'rb')
396    data = f.read()
397    f.close()
398   
399    tokenizer = RtfTokenizer(data)
400    parsedTokens = RtfTokenParser(tokenizer.tokens)
401   
402    data = parsedTokens.toRTF()
403   
404    f = open(fileName, 'wb')
405    f.write(data)
406    f.close()
407
408