Organize barcode codes into modules.
[pdfium.git] / xfa / src / fxbarcode / oned / BC_OnedCode39Reader.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 2008 ZXing authors\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_Reader.h"\r
25 #include "../common/BC_CommonBitArray.h"\r
26 #include "BC_OneDReader.h"\r
27 #include "BC_OnedCode39Reader.h"\r
28 FX_LPCSTR CBC_OnedCode39Reader::ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";\r
29 FX_LPCSTR CBC_OnedCode39Reader::CHECKSUM_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";\r
30 const FX_INT32 CBC_OnedCode39Reader::CHARACTER_ENCODINGS[44] = {\r
31     0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064,\r
32     0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C,\r
33     0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016,\r
34     0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094,\r
35     0x0A8, 0x0A2, 0x08A, 0x02A\r
36 };\r
37 const FX_INT32 CBC_OnedCode39Reader::ASTERISK_ENCODING = 0x094;\r
38 CBC_OnedCode39Reader::CBC_OnedCode39Reader(): m_extendedMode(FALSE), m_usingCheckDigit(FALSE)\r
39 {\r
40 }\r
41 CBC_OnedCode39Reader::CBC_OnedCode39Reader(FX_BOOL usingCheckDigit)\r
42 {\r
43     m_usingCheckDigit = usingCheckDigit;\r
44     m_extendedMode = FALSE;\r
45 }\r
46 CBC_OnedCode39Reader::CBC_OnedCode39Reader(FX_BOOL usingCheckDigit, FX_BOOL extendedMode)\r
47 {\r
48     m_extendedMode = extendedMode;\r
49     m_usingCheckDigit = usingCheckDigit;\r
50 }\r
51 CBC_OnedCode39Reader::~CBC_OnedCode39Reader()\r
52 {\r
53 }\r
54 CFX_ByteString CBC_OnedCode39Reader::DecodeRow(FX_INT32 rowNumber, CBC_CommonBitArray *row, FX_INT32 hints, FX_INT32 &e)\r
55 {\r
56     CFX_Int32Array *start = FindAsteriskPattern(row, e);\r
57     BC_EXCEPTION_CHECK_ReturnValue(e, "");\r
58     FX_INT32 nextStart = (*start)[1];\r
59     if(start != NULL) {\r
60         delete start;\r
61         start = NULL;\r
62     }\r
63     FX_INT32 end = row->GetSize();\r
64     while (nextStart < end && !row->Get(nextStart)) {\r
65         nextStart++;\r
66     }\r
67     CFX_ByteString result;\r
68     CFX_Int32Array counters;\r
69     counters.SetSize(9);\r
70     FX_CHAR decodedChar;\r
71     FX_INT32 lastStart;\r
72     do {\r
73         RecordPattern(row, nextStart, &counters, e);\r
74         BC_EXCEPTION_CHECK_ReturnValue(e, "");\r
75         FX_INT32 pattern = ToNarrowWidePattern(&counters);\r
76         if (pattern < 0) {\r
77             e = BCExceptionNotFound;\r
78             return "";\r
79         }\r
80         decodedChar = PatternToChar(pattern, e);\r
81         BC_EXCEPTION_CHECK_ReturnValue(e, "");\r
82         result += decodedChar;\r
83         lastStart = nextStart;\r
84         for (FX_INT32 i = 0; i < counters.GetSize(); i++) {\r
85             nextStart += counters[i];\r
86         }\r
87         while (nextStart < end && !row->Get(nextStart)) {\r
88             nextStart++;\r
89         }\r
90     } while (decodedChar != '*');\r
91     result = result.Mid(0, result.GetLength() - 1);\r
92     FX_INT32 lastPatternSize = 0;\r
93     for (FX_INT32 j = 0; j < counters.GetSize(); j++) {\r
94         lastPatternSize += counters[j];\r
95     }\r
96     FX_INT32 whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;\r
97     if(m_usingCheckDigit) {\r
98         FX_INT32 max = result.GetLength() - 1;\r
99         FX_INT32 total = 0;\r
100         FX_INT32 len = (FX_INT32)strlen(ALPHABET_STRING);\r
101         for (FX_INT32 k = 0; k < max; k++) {\r
102             for (FX_INT32 j = 0; j < len; j++)\r
103                 if (ALPHABET_STRING[j] == result[k]) {\r
104                     total += j;\r
105                 }\r
106         }\r
107         if (result[max] != (ALPHABET_STRING)[total % 43]) {\r
108             e = BCExceptionChecksumException;\r
109             return "";\r
110         }\r
111         result = result.Mid(0, result.GetLength() - 1);\r
112     }\r
113     if (result.GetLength() == 0) {\r
114         e  = BCExceptionNotFound;\r
115         return "";\r
116     }\r
117     if(m_extendedMode) {\r
118         CFX_ByteString bytestr = DecodeExtended(result, e);\r
119         BC_EXCEPTION_CHECK_ReturnValue(e, "");\r
120         return bytestr;\r
121     } else {\r
122         return result;\r
123     }\r
124 }\r
125 CFX_Int32Array *CBC_OnedCode39Reader::FindAsteriskPattern(CBC_CommonBitArray *row, FX_INT32 &e)\r
126 {\r
127     FX_INT32 width = row->GetSize();\r
128     FX_INT32 rowOffset = 0;\r
129     while (rowOffset < width) {\r
130         if (row->Get(rowOffset)) {\r
131             break;\r
132         }\r
133         rowOffset++;\r
134     }\r
135     FX_INT32 counterPosition = 0;\r
136     CFX_Int32Array counters;\r
137     counters.SetSize(9);\r
138     FX_INT32 patternStart = rowOffset;\r
139     FX_BOOL isWhite = FALSE;\r
140     FX_INT32 patternLength = counters.GetSize();\r
141     for (FX_INT32 i = rowOffset; i < width; i++) {\r
142         FX_BOOL pixel = row->Get(i);\r
143         if (pixel ^ isWhite) {\r
144             counters[counterPosition]++;\r
145         } else {\r
146             if (counterPosition == patternLength - 1) {\r
147                 if (ToNarrowWidePattern(&counters) == ASTERISK_ENCODING) {\r
148                     FX_BOOL bT1 =  row->IsRange(FX_MAX(0, patternStart - (i - patternStart) / 2), patternStart, FALSE, e);\r
149                     BC_EXCEPTION_CHECK_ReturnValue(e, NULL);\r
150                     if (bT1) {\r
151                         CFX_Int32Array *result = FX_NEW CFX_Int32Array;\r
152                         result->SetSize(2);\r
153                         (*result)[0] = patternStart;\r
154                         (*result)[1] = i;\r
155                         return result;\r
156                     }\r
157                 }\r
158                 patternStart += counters[0] + counters[1];\r
159                 for (FX_INT32 y = 2; y < patternLength; y++) {\r
160                     counters[y - 2] = counters[y];\r
161                 }\r
162                 counters[patternLength - 2] = 0;\r
163                 counters[patternLength - 1] = 0;\r
164                 counterPosition--;\r
165             } else {\r
166                 counterPosition++;\r
167             }\r
168             counters[counterPosition] = 1;\r
169             isWhite = !isWhite;\r
170         }\r
171     }\r
172     e = BCExceptionNotFound;\r
173     return NULL;\r
174 }\r
175 FX_INT32 CBC_OnedCode39Reader::ToNarrowWidePattern(CFX_Int32Array *counters)\r
176 {\r
177     FX_INT32 numCounters = counters->GetSize();\r
178     FX_INT32 maxNarrowCounter = 0;\r
179     FX_INT32 wideCounters;\r
180     do {\r
181 #undef max\r
182         FX_INT32 minCounter = FXSYS_IntMax;\r
183         for (FX_INT32 i = 0; i < numCounters; i++) {\r
184             FX_INT32 counter = (*counters)[i];\r
185             if (counter < minCounter && counter > maxNarrowCounter) {\r
186                 minCounter = counter;\r
187             }\r
188         }\r
189         maxNarrowCounter = minCounter;\r
190         wideCounters = 0;\r
191         FX_INT32 totalWideCountersWidth = 0;\r
192         FX_INT32 pattern = 0;\r
193         for (FX_INT32 j = 0; j < numCounters; j++) {\r
194             FX_INT32 counter = (*counters)[j];\r
195             if ((*counters)[j] > maxNarrowCounter) {\r
196                 pattern |= 1 << (numCounters - 1 - j);\r
197                 wideCounters++;\r
198                 totalWideCountersWidth += counter;\r
199             }\r
200         }\r
201         if (wideCounters == 3) {\r
202             for (FX_INT32 k = 0; k < numCounters && wideCounters > 0; k++) {\r
203                 FX_INT32 counter = (*counters)[k];\r
204                 if ((*counters)[k] > maxNarrowCounter) {\r
205                     wideCounters--;\r
206                     if ((counter << 1) >= totalWideCountersWidth) {\r
207                         return -1;\r
208                     }\r
209                 }\r
210             }\r
211             return pattern;\r
212         }\r
213     } while (wideCounters > 3);\r
214     return -1;\r
215 }\r
216 FX_CHAR CBC_OnedCode39Reader::PatternToChar(FX_INT32 pattern, FX_INT32 &e)\r
217 {\r
218     for (FX_INT32 i = 0; i < 44; i++) {\r
219         if (CHARACTER_ENCODINGS[i] == pattern) {\r
220             return (ALPHABET_STRING)[i];\r
221         }\r
222     }\r
223     e = BCExceptionNotFound;\r
224     return 0;\r
225 }\r
226 CFX_ByteString CBC_OnedCode39Reader::DecodeExtended(CFX_ByteString &encoded, FX_INT32 &e)\r
227 {\r
228     FX_INT32 length = encoded.GetLength();\r
229     CFX_ByteString decoded;\r
230     FX_CHAR c, next;\r
231     for(FX_INT32 i = 0; i < length; i++) {\r
232         c = encoded[i];\r
233         if(c == '+' || c == '$' || c == '%' || c == '/') {\r
234             next = encoded[i + 1];\r
235             FX_CHAR decodedChar = '\0';\r
236             switch (c) {\r
237                 case '+':\r
238                     if (next >= 'A' && next <= 'Z') {\r
239                         decodedChar = (FX_CHAR) (next + 32);\r
240                     } else {\r
241                         e = BCExceptionFormatException;\r
242                         return "";\r
243                     }\r
244                     break;\r
245                 case '$':\r
246                     if (next >= 'A' && next <= 'Z') {\r
247                         decodedChar = (FX_CHAR) (next - 64);\r
248                     } else {\r
249                         e = BCExceptionFormatException;\r
250                         return "";\r
251                     }\r
252                     break;\r
253                 case '%':\r
254                     if (next >= 'A' && next <= 'E') {\r
255                         decodedChar = (FX_CHAR) (next - 38);\r
256                     } else if (next >= 'F' && next <= 'J') {\r
257                         decodedChar = (FX_CHAR) (next - 11);\r
258                     } else if (next >= 'K' && next <= 'O' && next != 'M' && next != 'N') {\r
259                         decodedChar = (FX_CHAR) (next + 16);\r
260                     } else if (next >= 'P' && next <= 'S') {\r
261                         decodedChar = (FX_CHAR) (next + 43);\r
262                     } else if (next == 'U') {\r
263                         decodedChar = (FX_CHAR) 0;\r
264                     } else if (next == 'V') {\r
265                         decodedChar = (FX_CHAR) 64;\r
266                     } else if (next == 'W') {\r
267                         decodedChar = (FX_CHAR) 96;\r
268                     } else if (next == 'T' || next == 'X' || next == 'Y' || next == 'Z') {\r
269                         decodedChar = (FX_CHAR) 127;\r
270                     } else {\r
271                         e = BCExceptionFormatException;\r
272                         return "";\r
273                     }\r
274                     break;\r
275                 case '/':\r
276                     if (next >= 'A' && next <= 'O') {\r
277                         decodedChar = (FX_CHAR) (next - 32);\r
278                     } else if (next == 'Z') {\r
279                         decodedChar = ':';\r
280                     } else {\r
281                         e = BCExceptionFormatException;\r
282                         return "";\r
283                     }\r
284                     break;\r
285             }\r
286             decoded += decodedChar;\r
287             i++;\r
288         } else {\r
289             decoded += c;\r
290         }\r
291     }\r
292     return decoded;\r
293 }\r