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