Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa / src / fxbarcode / src / BC_PDF417HighLevelEncoder.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "barcode.h"\r
8 #include "include/BC_PDF417Compaction.h"\r
9 #include "include/BC_UtilCodingConvert.h"\r
10 #include "include/BC_PDF417HighLevelEncoder.h"\r
11 #define  SUBMODE_ALPHA  0\r
12 #define  SUBMODE_LOWER  1\r
13 #define  SUBMODE_MIXED  2\r
14 FX_INT32 CBC_PDF417HighLevelEncoder::TEXT_COMPACTION = 0;\r
15 FX_INT32 CBC_PDF417HighLevelEncoder::BYTE_COMPACTION = 1;\r
16 FX_INT32 CBC_PDF417HighLevelEncoder::NUMERIC_COMPACTION = 2;\r
17 FX_INT32 CBC_PDF417HighLevelEncoder::SUBMODE_PUNCTUATION = 3;\r
18 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_TEXT = 900;\r
19 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE_PADDED = 901;\r
20 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_NUMERIC = 902;\r
21 FX_INT32 CBC_PDF417HighLevelEncoder::SHIFT_TO_BYTE = 913;\r
22 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE = 924;\r
23 FX_BYTE CBC_PDF417HighLevelEncoder::TEXT_MIXED_RAW[] = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,\r
24                                                         35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0\r
25                                                        };\r
26 FX_BYTE CBC_PDF417HighLevelEncoder::TEXT_PUNCTUATION_RAW[] = {59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,\r
27                                                               10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0\r
28                                                              };\r
29 FX_INT32 CBC_PDF417HighLevelEncoder::MIXED[128] = {0};\r
30 FX_INT32 CBC_PDF417HighLevelEncoder::PUNCTUATION[128] = {0};\r
31 void CBC_PDF417HighLevelEncoder::Initialize()\r
32 {\r
33     Inverse();\r
34 }\r
35 void CBC_PDF417HighLevelEncoder::Finalize()\r
36 {\r
37 }\r
38 CBC_PDF417HighLevelEncoder::CBC_PDF417HighLevelEncoder()\r
39 {\r
40 }\r
41 CBC_PDF417HighLevelEncoder::~CBC_PDF417HighLevelEncoder()\r
42 {\r
43 }\r
44 CFX_WideString CBC_PDF417HighLevelEncoder::encodeHighLevel(CFX_WideString wideMsg, Compaction compaction, FX_INT32 &e)\r
45 {\r
46     CFX_ByteString bytes;\r
47     CBC_UtilCodingConvert::UnicodeToUTF8(wideMsg, bytes);\r
48     CFX_WideString msg;\r
49     FX_INT32 len = bytes.GetLength();\r
50     for (FX_INT32 i = 0; i < len; i++) {\r
51         FX_WCHAR ch =  (FX_WCHAR)(bytes.GetAt(i) & 0xff);\r
52         if (ch == '?' && bytes.GetAt(i) != '?') {\r
53             e = BCExceptionCharactersOutsideISO88591Encoding;\r
54             return (FX_LPWSTR)"";\r
55         }\r
56         msg += ch;\r
57     }\r
58     CFX_ByteArray byteArr;\r
59     for (FX_INT32 k = 0; k < bytes.GetLength(); k++) {\r
60         byteArr.Add(bytes.GetAt(k));\r
61     }\r
62     CFX_WideString sb;\r
63     len = msg.GetLength();\r
64     FX_INT32 p = 0;\r
65     FX_INT32 textSubMode = SUBMODE_ALPHA;\r
66     if (compaction == TEXT) {\r
67         encodeText(msg, p, len, sb, textSubMode);\r
68     } else if (compaction == BYTES) {\r
69         encodeBinary(&byteArr, p, byteArr.GetSize(), BYTE_COMPACTION, sb);\r
70     } else if (compaction == NUMERIC) {\r
71         sb += (FX_WCHAR) LATCH_TO_NUMERIC;\r
72         encodeNumeric(msg, p, len, sb);\r
73     } else {\r
74         FX_INT32 encodingMode = LATCH_TO_TEXT;\r
75         while (p < len) {\r
76             FX_INT32 n = determineConsecutiveDigitCount(msg, p);\r
77             if (n >= 13) {\r
78                 sb += (FX_WCHAR) LATCH_TO_NUMERIC;\r
79                 encodingMode = NUMERIC_COMPACTION;\r
80                 textSubMode = SUBMODE_ALPHA;\r
81                 encodeNumeric(msg, p, n, sb);\r
82                 p += n;\r
83             } else {\r
84                 FX_INT32 t = determineConsecutiveTextCount(msg, p);\r
85                 if (t >= 5 || n == len) {\r
86                     if (encodingMode != TEXT_COMPACTION) {\r
87                         sb += (FX_WCHAR) LATCH_TO_TEXT;\r
88                         encodingMode = TEXT_COMPACTION;\r
89                         textSubMode = SUBMODE_ALPHA;\r
90                     }\r
91                     textSubMode = encodeText(msg, p, t, sb, textSubMode);\r
92                     p += t;\r
93                 } else {\r
94                     FX_INT32 b = determineConsecutiveBinaryCount(msg, &byteArr, p, e);\r
95                     BC_EXCEPTION_CHECK_ReturnValue(e,  (FX_WCHAR)' ');\r
96                     if (b == 0) {\r
97                         b = 1;\r
98                     }\r
99                     if (b == 1 && encodingMode == TEXT_COMPACTION) {\r
100                         encodeBinary(&byteArr, p, 1, TEXT_COMPACTION, sb);\r
101                     } else {\r
102                         encodeBinary(&byteArr, p, b, encodingMode, sb);\r
103                         encodingMode = BYTE_COMPACTION;\r
104                         textSubMode = SUBMODE_ALPHA;\r
105                     }\r
106                     p += b;\r
107                 }\r
108             }\r
109         }\r
110     }\r
111     return sb;\r
112 }\r
113 void CBC_PDF417HighLevelEncoder::Inverse()\r
114 {\r
115     FX_BYTE i = 0;\r
116     FX_INT32 l = 0;\r
117     for (l = 0; l < sizeof(MIXED) / sizeof(MIXED[0]); l++) {\r
118         MIXED[l] = -1;\r
119     }\r
120     for (i = 0; i < sizeof(TEXT_MIXED_RAW) / sizeof(TEXT_MIXED_RAW[0]); i++) {\r
121         FX_BYTE b = TEXT_MIXED_RAW[i];\r
122         if (b > 0) {\r
123             MIXED[b] = i;\r
124         }\r
125     }\r
126     for (l = 0; l < sizeof(PUNCTUATION) / sizeof(PUNCTUATION[0]); l++) {\r
127         PUNCTUATION[l] = -1;\r
128     }\r
129     for (i = 0; i < sizeof(TEXT_PUNCTUATION_RAW) / sizeof(TEXT_PUNCTUATION_RAW[0]); i++) {\r
130         FX_BYTE b = TEXT_PUNCTUATION_RAW[i];\r
131         if (b > 0) {\r
132             PUNCTUATION[b] = i;\r
133         }\r
134     }\r
135 }\r
136 CFX_ByteArray* CBC_PDF417HighLevelEncoder::getBytesForMessage(CFX_WideString msg)\r
137 {\r
138     CFX_ByteString bytestring;\r
139     CBC_UtilCodingConvert::UnicodeToUTF8(msg, bytestring);\r
140     CFX_ByteArray* bytearray = FX_NEW CFX_ByteArray;\r
141     for (FX_INT32 i = 0; i < bytestring.GetLength(); i++) {\r
142         bytearray->Add(bytestring.GetAt(i));\r
143     }\r
144     return bytearray;\r
145 }\r
146 FX_INT32 CBC_PDF417HighLevelEncoder::encodeText(CFX_WideString msg, FX_INT32 startpos, FX_INT32 count, CFX_WideString &sb, FX_INT32 initialSubmode)\r
147 {\r
148     CFX_WideString tmp;\r
149     FX_INT32 submode = initialSubmode;\r
150     FX_INT32 idx = 0;\r
151     while (TRUE) {\r
152         FX_WCHAR ch = msg.GetAt(startpos + idx);\r
153         switch (submode) {\r
154             case SUBMODE_ALPHA:\r
155                 if (isAlphaUpper(ch)) {\r
156                     if (ch == ' ') {\r
157                         tmp += (FX_WCHAR) 26;\r
158                     } else {\r
159                         tmp += (FX_WCHAR) (ch - 65);\r
160                     }\r
161                 } else {\r
162                     if (isAlphaLower(ch)) {\r
163                         submode = SUBMODE_LOWER;\r
164                         tmp += (FX_WCHAR) 27;\r
165                         continue;\r
166                     } else if (isMixed(ch)) {\r
167                         submode = SUBMODE_MIXED;\r
168                         tmp += (FX_WCHAR) 28;\r
169                         continue;\r
170                     } else {\r
171                         tmp += (FX_WCHAR) 29;\r
172                         tmp += PUNCTUATION[ch];\r
173                         break;\r
174                     }\r
175                 }\r
176                 break;\r
177             case SUBMODE_LOWER:\r
178                 if (isAlphaLower(ch)) {\r
179                     if (ch == ' ') {\r
180                         tmp += (FX_WCHAR) 26;\r
181                     } else {\r
182                         tmp += (FX_WCHAR) (ch - 97);\r
183                     }\r
184                 } else {\r
185                     if (isAlphaUpper(ch)) {\r
186                         tmp += (FX_WCHAR) 27;\r
187                         tmp += (FX_WCHAR) (ch - 65);\r
188                         break;\r
189                     } else if (isMixed(ch)) {\r
190                         submode = SUBMODE_MIXED;\r
191                         tmp += (FX_WCHAR) 28;\r
192                         continue;\r
193                     } else {\r
194                         tmp += (FX_WCHAR) 29;\r
195                         tmp += PUNCTUATION[ch];\r
196                         break;\r
197                     }\r
198                 }\r
199                 break;\r
200             case SUBMODE_MIXED:\r
201                 if (isMixed(ch)) {\r
202                     FX_WCHAR a =  MIXED[ch];\r
203                     FX_INT32 b = (FX_INT32)a;\r
204                     tmp += MIXED[ch];\r
205                 } else {\r
206                     if (isAlphaUpper(ch)) {\r
207                         submode = SUBMODE_ALPHA;\r
208                         tmp += (FX_WCHAR) 28;\r
209                         continue;\r
210                     } else if (isAlphaLower(ch)) {\r
211                         submode = SUBMODE_LOWER;\r
212                         tmp += (FX_WCHAR) 27;\r
213                         continue;\r
214                     } else {\r
215                         if (startpos + idx + 1 < count) {\r
216                             FX_WCHAR next = msg.GetAt(startpos + idx + 1);\r
217                             if (isPunctuation(next)) {\r
218                                 submode = SUBMODE_PUNCTUATION;\r
219                                 tmp += (FX_WCHAR) 25;\r
220                                 continue;\r
221                             }\r
222                         }\r
223                         tmp += (FX_WCHAR) 29;\r
224                         tmp += PUNCTUATION[ch];\r
225                     }\r
226                 }\r
227                 break;\r
228             default:\r
229                 if (isPunctuation(ch)) {\r
230                     tmp += PUNCTUATION[ch];\r
231                 } else {\r
232                     submode = SUBMODE_ALPHA;\r
233                     tmp += (FX_WCHAR) 29;\r
234                     continue;\r
235                 }\r
236         }\r
237         idx++;\r
238         if (idx >= count) {\r
239             break;\r
240         }\r
241     }\r
242     FX_WCHAR h = 0;\r
243     FX_INT32 len = tmp.GetLength();\r
244     for (FX_INT32 i = 0; i < len; i++) {\r
245         FX_BOOL odd = (i % 2) != 0;\r
246         if (odd) {\r
247             h = (FX_WCHAR) ((h * 30) + tmp.GetAt(i));\r
248             sb += h;\r
249         } else {\r
250             h = tmp.GetAt(i);\r
251         }\r
252     }\r
253     if ((len % 2) != 0) {\r
254         sb += (FX_WCHAR) ((h * 30) + 29);\r
255     }\r
256     return submode;\r
257 }\r
258 void CBC_PDF417HighLevelEncoder::encodeBinary(CFX_ByteArray* bytes, FX_INT32 startpos, FX_INT32 count, FX_INT32 startmode, CFX_WideString &sb)\r
259 {\r
260     if (count == 1 && startmode == TEXT_COMPACTION) {\r
261         sb += (FX_WCHAR) SHIFT_TO_BYTE;\r
262     }\r
263     FX_INT32 idx = startpos;\r
264     FX_INT32 i = 0;\r
265     if (count >= 6) {\r
266         sb += (FX_WCHAR) LATCH_TO_BYTE;\r
267         FX_WCHAR chars[5];\r
268         while ((startpos + count - idx) >= 6) {\r
269             FX_INT64 t = 0;\r
270             for (i = 0; i < 6; i++) {\r
271                 t <<= 8;\r
272                 t += bytes->GetAt(idx + i) & 0xff;\r
273             }\r
274             for (i = 0; i < 5; i++) {\r
275                 chars[i] = (FX_WCHAR) (t % 900);\r
276                 t /= 900;\r
277             }\r
278             for (i = 4; i >= 0; i--) {\r
279                 sb += (chars[i]);\r
280             }\r
281             idx += 6;\r
282         }\r
283     }\r
284     if (idx < startpos + count) {\r
285         sb += (FX_WCHAR) LATCH_TO_BYTE_PADDED;\r
286     }\r
287     for (i = idx; i < startpos + count; i++) {\r
288         FX_INT32 ch = bytes->GetAt(i) & 0xff;\r
289         sb += (FX_WCHAR) ch;\r
290     }\r
291 }\r
292 void CBC_PDF417HighLevelEncoder::encodeNumeric(CFX_WideString msg, FX_INT32 startpos, FX_INT32 count, CFX_WideString &sb)\r
293 {\r
294     FX_INT32 idx = 0;\r
295     CFX_WideString tmp;\r
296     FX_INT32 num900 = 900;\r
297     FX_INT32 num0 = 0;\r
298     while (idx < count - 1) {\r
299         FX_INT32 len = 44 < count - idx ? 44 : count - idx;\r
300         CFX_WideString part = (FX_WCHAR)'1' + msg.Mid(startpos + idx, len);\r
301         FX_INT32 bigint = part.GetInteger();\r
302         do {\r
303             FX_INT32 c = bigint % num900;\r
304             tmp += (FX_WCHAR) (c);\r
305             bigint = bigint / num900;\r
306         } while (bigint != num0);\r
307         for (FX_INT32 i = tmp.GetLength() - 1; i >= 0; i--) {\r
308             sb += (tmp.GetAt(i));\r
309         }\r
310         idx += len;\r
311     }\r
312 }\r
313 FX_BOOL CBC_PDF417HighLevelEncoder::isDigit(FX_WCHAR ch)\r
314 {\r
315     return ch >= '0' && ch <= '9';\r
316 }\r
317 FX_BOOL CBC_PDF417HighLevelEncoder::isAlphaUpper(FX_WCHAR ch)\r
318 {\r
319     return ch == ' ' || (ch >= 'A' && ch <= 'Z');\r
320 }\r
321 FX_BOOL CBC_PDF417HighLevelEncoder::isAlphaLower(FX_WCHAR ch)\r
322 {\r
323     return ch == ' ' || (ch >= 'a' && ch <= 'z');\r
324 }\r
325 FX_BOOL CBC_PDF417HighLevelEncoder::isMixed(FX_WCHAR ch)\r
326 {\r
327     return MIXED[ch] != -1;\r
328 }\r
329 FX_BOOL CBC_PDF417HighLevelEncoder::isPunctuation(FX_WCHAR ch)\r
330 {\r
331     return PUNCTUATION[ch] != -1;\r
332 }\r
333 FX_BOOL CBC_PDF417HighLevelEncoder::isText(FX_WCHAR ch)\r
334 {\r
335     return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126);\r
336 }\r
337 FX_INT32 CBC_PDF417HighLevelEncoder::determineConsecutiveDigitCount(CFX_WideString msg, FX_INT32 startpos)\r
338 {\r
339     FX_INT32 count = 0;\r
340     FX_INT32 len = msg.GetLength();\r
341     FX_INT32 idx = startpos;\r
342     if (idx < len) {\r
343         FX_WCHAR ch = msg.GetAt(idx);\r
344         while (isDigit(ch) && idx < len) {\r
345             count++;\r
346             idx++;\r
347             if (idx < len) {\r
348                 ch = msg.GetAt(idx);\r
349             }\r
350         }\r
351     }\r
352     return count;\r
353 }\r
354 FX_INT32 CBC_PDF417HighLevelEncoder::determineConsecutiveTextCount(CFX_WideString msg, FX_INT32 startpos)\r
355 {\r
356     FX_INT32 len = msg.GetLength();\r
357     FX_INT32 idx = startpos;\r
358     while (idx < len) {\r
359         FX_WCHAR ch = msg.GetAt(idx);\r
360         FX_INT32 numericCount = 0;\r
361         while (numericCount < 13 && isDigit(ch) && idx < len) {\r
362             numericCount++;\r
363             idx++;\r
364             if (idx < len) {\r
365                 ch = msg.GetAt(idx);\r
366             }\r
367         }\r
368         if (numericCount >= 13) {\r
369             return idx - startpos - numericCount;\r
370         }\r
371         if (numericCount > 0) {\r
372             continue;\r
373         }\r
374         ch = msg.GetAt(idx);\r
375         if (!isText(ch)) {\r
376             break;\r
377         }\r
378         idx++;\r
379     }\r
380     return idx - startpos;\r
381 }\r
382 FX_INT32 CBC_PDF417HighLevelEncoder::determineConsecutiveBinaryCount(CFX_WideString msg, CFX_ByteArray* bytes, FX_INT32 startpos, FX_INT32 &e)\r
383 {\r
384     FX_INT32 len = msg.GetLength();\r
385     FX_INT32 idx = startpos;\r
386     while (idx < len) {\r
387         FX_WCHAR ch = msg.GetAt(idx);\r
388         FX_INT32 numericCount = 0;\r
389         while (numericCount < 13 && isDigit(ch)) {\r
390             numericCount++;\r
391             FX_INT32 i = idx + numericCount;\r
392             if (i >= len) {\r
393                 break;\r
394             }\r
395             ch = msg.GetAt(i);\r
396         }\r
397         if (numericCount >= 13) {\r
398             return idx - startpos;\r
399         }\r
400         FX_INT32 textCount = 0;\r
401         while (textCount < 5 && isText(ch)) {\r
402             textCount++;\r
403             FX_INT32 i = idx + textCount;\r
404             if (i >= len) {\r
405                 break;\r
406             }\r
407             ch = msg.GetAt(i);\r
408         }\r
409         if (textCount >= 5) {\r
410             return idx - startpos;\r
411         }\r
412         ch = msg.GetAt(idx);\r
413         if (bytes->GetAt(idx) == 63 && ch != '?') {\r
414             e = BCExceptionNonEncodableCharacterDetected;\r
415             return -1;\r
416         }\r
417         idx++;\r
418     }\r
419     return idx - startpos;\r
420 }\r