Organize barcode codes into modules.
[pdfium.git] / xfa / src / fxbarcode / pdf417 / 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 // Original code is licensed as follows:\r
7 /*\r
8  * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part\r
9  *\r
10  * Licensed under the Apache License, Version 2.0 (the "License");\r
11  * you may not use this file except in compliance with the License.\r
12  * You may obtain a copy of the License at\r
13  *\r
14  * http://www.apache.org/licenses/LICENSE-2.0\r
15  *\r
16  * Unless required by applicable law or agreed to in writing, software\r
17  * distributed under the License is distributed on an "AS IS" BASIS,\r
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
19  * See the License for the specific language governing permissions and\r
20  * limitations under the License.\r
21  */\r
22 \r
23 #include "../barcode.h"\r
24 #include "../BC_UtilCodingConvert.h"\r
25 #include "../../../../third_party/bigint/BigIntegerLibrary.hh"\r
26 #include "BC_PDF417Compaction.h"\r
27 #include "BC_PDF417HighLevelEncoder.h"\r
28 #define  SUBMODE_ALPHA  0\r
29 #define  SUBMODE_LOWER  1\r
30 #define  SUBMODE_MIXED  2\r
31 FX_INT32 CBC_PDF417HighLevelEncoder::TEXT_COMPACTION = 0;\r
32 FX_INT32 CBC_PDF417HighLevelEncoder::BYTE_COMPACTION = 1;\r
33 FX_INT32 CBC_PDF417HighLevelEncoder::NUMERIC_COMPACTION = 2;\r
34 FX_INT32 CBC_PDF417HighLevelEncoder::SUBMODE_PUNCTUATION = 3;\r
35 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_TEXT = 900;\r
36 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE_PADDED = 901;\r
37 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_NUMERIC = 902;\r
38 FX_INT32 CBC_PDF417HighLevelEncoder::SHIFT_TO_BYTE = 913;\r
39 FX_INT32 CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE = 924;\r
40 FX_BYTE CBC_PDF417HighLevelEncoder::TEXT_MIXED_RAW[] = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,\r
41                                                         35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0\r
42                                                        };\r
43 FX_BYTE CBC_PDF417HighLevelEncoder::TEXT_PUNCTUATION_RAW[] = {59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,\r
44                                                               10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0\r
45                                                              };\r
46 FX_INT32 CBC_PDF417HighLevelEncoder::MIXED[128] = {0};\r
47 FX_INT32 CBC_PDF417HighLevelEncoder::PUNCTUATION[128] = {0};\r
48 void CBC_PDF417HighLevelEncoder::Initialize()\r
49 {\r
50     Inverse();\r
51 }\r
52 void CBC_PDF417HighLevelEncoder::Finalize()\r
53 {\r
54 }\r
55 CFX_WideString CBC_PDF417HighLevelEncoder::encodeHighLevel(CFX_WideString wideMsg, Compaction compaction, FX_INT32 &e)\r
56 {\r
57     CFX_ByteString bytes;\r
58     CBC_UtilCodingConvert::UnicodeToUTF8(wideMsg, bytes);\r
59     CFX_WideString msg;\r
60     FX_INT32 len = bytes.GetLength();\r
61     for (FX_INT32 i = 0; i < len; i++) {\r
62         FX_WCHAR ch =  (FX_WCHAR)(bytes.GetAt(i) & 0xff);\r
63         if (ch == '?' && bytes.GetAt(i) != '?') {\r
64             e = BCExceptionCharactersOutsideISO88591Encoding;\r
65             return (FX_LPWSTR)"";\r
66         }\r
67         msg += ch;\r
68     }\r
69     CFX_ByteArray byteArr;\r
70     for (FX_INT32 k = 0; k < bytes.GetLength(); k++) {\r
71         byteArr.Add(bytes.GetAt(k));\r
72     }\r
73     CFX_WideString sb;\r
74     len = msg.GetLength();\r
75     FX_INT32 p = 0;\r
76     FX_INT32 textSubMode = SUBMODE_ALPHA;\r
77     if (compaction == TEXT) {\r
78         encodeText(msg, p, len, sb, textSubMode);\r
79     } else if (compaction == BYTES) {\r
80         encodeBinary(&byteArr, p, byteArr.GetSize(), BYTE_COMPACTION, sb);\r
81     } else if (compaction == NUMERIC) {\r
82         sb += (FX_WCHAR) LATCH_TO_NUMERIC;\r
83         encodeNumeric(msg, p, len, sb);\r
84     } else {\r
85         FX_INT32 encodingMode = LATCH_TO_TEXT;\r
86         while (p < len) {\r
87             FX_INT32 n = determineConsecutiveDigitCount(msg, p);\r
88             if (n >= 13) {\r
89                 sb += (FX_WCHAR) LATCH_TO_NUMERIC;\r
90                 encodingMode = NUMERIC_COMPACTION;\r
91                 textSubMode = SUBMODE_ALPHA;\r
92                 encodeNumeric(msg, p, n, sb);\r
93                 p += n;\r
94             } else {\r
95                 FX_INT32 t = determineConsecutiveTextCount(msg, p);\r
96                 if (t >= 5 || n == len) {\r
97                     if (encodingMode != TEXT_COMPACTION) {\r
98                         sb += (FX_WCHAR) LATCH_TO_TEXT;\r
99                         encodingMode = TEXT_COMPACTION;\r
100                         textSubMode = SUBMODE_ALPHA;\r
101                     }\r
102                     textSubMode = encodeText(msg, p, t, sb, textSubMode);\r
103                     p += t;\r
104                 } else {\r
105                     FX_INT32 b = determineConsecutiveBinaryCount(msg, &byteArr, p, e);\r
106                     BC_EXCEPTION_CHECK_ReturnValue(e,  (FX_WCHAR)' ');\r
107                     if (b == 0) {\r
108                         b = 1;\r
109                     }\r
110                     if (b == 1 && encodingMode == TEXT_COMPACTION) {\r
111                         encodeBinary(&byteArr, p, 1, TEXT_COMPACTION, sb);\r
112                     } else {\r
113                         encodeBinary(&byteArr, p, b, encodingMode, sb);\r
114                         encodingMode = BYTE_COMPACTION;\r
115                         textSubMode = SUBMODE_ALPHA;\r
116                     }\r
117                     p += b;\r
118                 }\r
119             }\r
120         }\r
121     }\r
122     return sb;\r
123 }\r
124 void CBC_PDF417HighLevelEncoder::Inverse()\r
125 {\r
126     FX_BYTE i = 0;\r
127     FX_INT32 l = 0;\r
128     for (l = 0; l < sizeof(MIXED) / sizeof(MIXED[0]); l++) {\r
129         MIXED[l] = -1;\r
130     }\r
131     for (i = 0; i < sizeof(TEXT_MIXED_RAW) / sizeof(TEXT_MIXED_RAW[0]); i++) {\r
132         FX_BYTE b = TEXT_MIXED_RAW[i];\r
133         if (b > 0) {\r
134             MIXED[b] = i;\r
135         }\r
136     }\r
137     for (l = 0; l < sizeof(PUNCTUATION) / sizeof(PUNCTUATION[0]); l++) {\r
138         PUNCTUATION[l] = -1;\r
139     }\r
140     for (i = 0; i < sizeof(TEXT_PUNCTUATION_RAW) / sizeof(TEXT_PUNCTUATION_RAW[0]); i++) {\r
141         FX_BYTE b = TEXT_PUNCTUATION_RAW[i];\r
142         if (b > 0) {\r
143             PUNCTUATION[b] = i;\r
144         }\r
145     }\r
146 }\r
147 FX_INT32 CBC_PDF417HighLevelEncoder::encodeText(CFX_WideString msg, FX_INT32 startpos, FX_INT32 count, CFX_WideString &sb, FX_INT32 initialSubmode)\r
148 {\r
149     CFX_WideString tmp;\r
150     FX_INT32 submode = initialSubmode;\r
151     FX_INT32 idx = 0;\r
152     while (TRUE) {\r
153         FX_WCHAR ch = msg.GetAt(startpos + idx);\r
154         switch (submode) {\r
155             case SUBMODE_ALPHA:\r
156                 if (isAlphaUpper(ch)) {\r
157                     if (ch == ' ') {\r
158                         tmp += (FX_WCHAR) 26;\r
159                     } else {\r
160                         tmp += (FX_WCHAR) (ch - 65);\r
161                     }\r
162                 } else {\r
163                     if (isAlphaLower(ch)) {\r
164                         submode = SUBMODE_LOWER;\r
165                         tmp += (FX_WCHAR) 27;\r
166                         continue;\r
167                     } else if (isMixed(ch)) {\r
168                         submode = SUBMODE_MIXED;\r
169                         tmp += (FX_WCHAR) 28;\r
170                         continue;\r
171                     } else {\r
172                         tmp += (FX_WCHAR) 29;\r
173                         tmp += PUNCTUATION[ch];\r
174                         break;\r
175                     }\r
176                 }\r
177                 break;\r
178             case SUBMODE_LOWER:\r
179                 if (isAlphaLower(ch)) {\r
180                     if (ch == ' ') {\r
181                         tmp += (FX_WCHAR) 26;\r
182                     } else {\r
183                         tmp += (FX_WCHAR) (ch - 97);\r
184                     }\r
185                 } else {\r
186                     if (isAlphaUpper(ch)) {\r
187                         tmp += (FX_WCHAR) 27;\r
188                         tmp += (FX_WCHAR) (ch - 65);\r
189                         break;\r
190                     } else if (isMixed(ch)) {\r
191                         submode = SUBMODE_MIXED;\r
192                         tmp += (FX_WCHAR) 28;\r
193                         continue;\r
194                     } else {\r
195                         tmp += (FX_WCHAR) 29;\r
196                         tmp += PUNCTUATION[ch];\r
197                         break;\r
198                     }\r
199                 }\r
200                 break;\r
201             case SUBMODE_MIXED:\r
202                 if (isMixed(ch)) {\r
203                     FX_WCHAR a =  MIXED[ch];\r
204                     FX_INT32 b = (FX_INT32)a;\r
205                     tmp += MIXED[ch];\r
206                 } else {\r
207                     if (isAlphaUpper(ch)) {\r
208                         submode = SUBMODE_ALPHA;\r
209                         tmp += (FX_WCHAR) 28;\r
210                         continue;\r
211                     } else if (isAlphaLower(ch)) {\r
212                         submode = SUBMODE_LOWER;\r
213                         tmp += (FX_WCHAR) 27;\r
214                         continue;\r
215                     } else {\r
216                         if (startpos + idx + 1 < count) {\r
217                             FX_WCHAR next = msg.GetAt(startpos + idx + 1);\r
218                             if (isPunctuation(next)) {\r
219                                 submode = SUBMODE_PUNCTUATION;\r
220                                 tmp += (FX_WCHAR) 25;\r
221                                 continue;\r
222                             }\r
223                         }\r
224                         tmp += (FX_WCHAR) 29;\r
225                         tmp += PUNCTUATION[ch];\r
226                     }\r
227                 }\r
228                 break;\r
229             default:\r
230                 if (isPunctuation(ch)) {\r
231                     tmp += PUNCTUATION[ch];\r
232                 } else {\r
233                     submode = SUBMODE_ALPHA;\r
234                     tmp += (FX_WCHAR) 29;\r
235                     continue;\r
236                 }\r
237         }\r
238         idx++;\r
239         if (idx >= count) {\r
240             break;\r
241         }\r
242     }\r
243     FX_WCHAR h = 0;\r
244     FX_INT32 len = tmp.GetLength();\r
245     for (FX_INT32 i = 0; i < len; i++) {\r
246         FX_BOOL odd = (i % 2) != 0;\r
247         if (odd) {\r
248             h = (FX_WCHAR) ((h * 30) + tmp.GetAt(i));\r
249             sb += h;\r
250         } else {\r
251             h = tmp.GetAt(i);\r
252         }\r
253     }\r
254     if ((len % 2) != 0) {\r
255         sb += (FX_WCHAR) ((h * 30) + 29);\r
256     }\r
257     return submode;\r
258 }\r
259 void CBC_PDF417HighLevelEncoder::encodeBinary(CFX_ByteArray* bytes, FX_INT32 startpos, FX_INT32 count, FX_INT32 startmode, CFX_WideString &sb)\r
260 {\r
261     if (count == 1 && startmode == TEXT_COMPACTION) {\r
262         sb += (FX_WCHAR) SHIFT_TO_BYTE;\r
263     }\r
264     FX_INT32 idx = startpos;\r
265     FX_INT32 i = 0;\r
266     if (count >= 6) {\r
267         sb += (FX_WCHAR) LATCH_TO_BYTE;\r
268         FX_WCHAR chars[5];\r
269         while ((startpos + count - idx) >= 6) {\r
270             FX_INT64 t = 0;\r
271             for (i = 0; i < 6; i++) {\r
272                 t <<= 8;\r
273                 t += bytes->GetAt(idx + i) & 0xff;\r
274             }\r
275             for (i = 0; i < 5; i++) {\r
276                 chars[i] = (FX_WCHAR) (t % 900);\r
277                 t /= 900;\r
278             }\r
279             for (i = 4; i >= 0; i--) {\r
280                 sb += (chars[i]);\r
281             }\r
282             idx += 6;\r
283         }\r
284     }\r
285     if (idx < startpos + count) {\r
286         sb += (FX_WCHAR) LATCH_TO_BYTE_PADDED;\r
287     }\r
288     for (i = idx; i < startpos + count; i++) {\r
289         FX_INT32 ch = bytes->GetAt(i) & 0xff;\r
290         sb += (FX_WCHAR) ch;\r
291     }\r
292 }\r
293 void CBC_PDF417HighLevelEncoder::encodeNumeric(CFX_WideString msg, FX_INT32 startpos, FX_INT32 count, CFX_WideString &sb)\r
294 {\r
295     FX_INT32 idx = 0;\r
296     BigInteger num900 = 900;\r
297     while (idx < count - 1) {\r
298         CFX_WideString tmp;\r
299         FX_INT32 len = 44 < count - idx ? 44 : count - idx;\r
300         CFX_ByteString part = ((FX_WCHAR)'1' + msg.Mid(startpos + idx, len)).UTF8Encode();\r
301         BigInteger bigint = stringToBigInteger(FX_LPCSTR(part));\r
302         do {\r
303             FX_INT32 c = (bigint % num900).toInt();\r
304             tmp += (FX_WCHAR)(c);\r
305             bigint = bigint / num900;\r
306         } while (!bigint.isZero());\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