1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 // Original code is licensed as follows:
7 /*
8  * Copyright 2006-2007 Jeremias Maerki.
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22 
23 #include "xfa/src/fxbarcode/barcode.h"
24 #include "xfa/src/fxbarcode/BC_Dimension.h"
25 #include "xfa/src/fxbarcode/common/BC_CommonBitMatrix.h"
26 #include "BC_Encoder.h"
27 #include "BC_SymbolShapeHint.h"
28 #include "BC_SymbolInfo.h"
29 #include "BC_EncoderContext.h"
30 #include "BC_HighLevelEncoder.h"
31 #include "BC_C40Encoder.h"
CBC_C40Encoder()32 CBC_C40Encoder::CBC_C40Encoder() {}
~CBC_C40Encoder()33 CBC_C40Encoder::~CBC_C40Encoder() {}
getEncodingMode()34 int32_t CBC_C40Encoder::getEncodingMode() {
35   return C40_ENCODATION;
36 }
Encode(CBC_EncoderContext & context,int32_t & e)37 void CBC_C40Encoder::Encode(CBC_EncoderContext& context, int32_t& e) {
38   CFX_WideString buffer;
39   while (context.hasMoreCharacters()) {
40     FX_WCHAR c = context.getCurrentChar();
41     context.m_pos++;
42     int32_t lastCharSize = encodeChar(c, buffer, e);
43     if (e != BCExceptionNO) {
44       return;
45     }
46     int32_t unwritten = (buffer.GetLength() / 3) * 2;
47     int32_t curCodewordCount = context.getCodewordCount() + unwritten;
48     context.updateSymbolInfo(curCodewordCount, e);
49     if (e != BCExceptionNO) {
50       return;
51     }
52     int32_t available = context.m_symbolInfo->m_dataCapacity - curCodewordCount;
53     if (!context.hasMoreCharacters()) {
54       CFX_WideString removed;
55       if ((buffer.GetLength() % 3) == 2) {
56         if (available < 2 || available > 2) {
57           lastCharSize =
58               backtrackOneCharacter(context, buffer, removed, lastCharSize, e);
59           if (e != BCExceptionNO) {
60             return;
61           }
62         }
63       }
64       while ((buffer.GetLength() % 3) == 1 &&
65              ((lastCharSize <= 3 && available != 1) || lastCharSize > 3)) {
66         lastCharSize =
67             backtrackOneCharacter(context, buffer, removed, lastCharSize, e);
68         if (e != BCExceptionNO) {
69           return;
70         }
71       }
72       break;
73     }
74     int32_t count = buffer.GetLength();
75     if ((count % 3) == 0) {
76       int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
77           context.m_msg, context.m_pos, getEncodingMode());
78       if (newMode != getEncodingMode()) {
79         context.signalEncoderChange(newMode);
80         break;
81       }
82     }
83   }
84   handleEOD(context, buffer, e);
85 }
writeNextTriplet(CBC_EncoderContext & context,CFX_WideString & buffer)86 void CBC_C40Encoder::writeNextTriplet(CBC_EncoderContext& context,
87                                       CFX_WideString& buffer) {
88   context.writeCodewords(encodeToCodewords(buffer, 0));
89   buffer.Delete(0, 3);
90 }
handleEOD(CBC_EncoderContext & context,CFX_WideString & buffer,int32_t & e)91 void CBC_C40Encoder::handleEOD(CBC_EncoderContext& context,
92                                CFX_WideString& buffer,
93                                int32_t& e) {
94   int32_t unwritten = (buffer.GetLength() / 3) * 2;
95   int32_t rest = buffer.GetLength() % 3;
96   int32_t curCodewordCount = context.getCodewordCount() + unwritten;
97   context.updateSymbolInfo(curCodewordCount, e);
98   if (e != BCExceptionNO) {
99     return;
100   }
101   int32_t available = context.m_symbolInfo->m_dataCapacity - curCodewordCount;
102   if (rest == 2) {
103     buffer += (FX_WCHAR)'\0';
104     while (buffer.GetLength() >= 3) {
105       writeNextTriplet(context, buffer);
106     }
107     if (context.hasMoreCharacters()) {
108       context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
109     }
110   } else if (available == 1 && rest == 1) {
111     while (buffer.GetLength() >= 3) {
112       writeNextTriplet(context, buffer);
113     }
114     if (context.hasMoreCharacters()) {
115       context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
116     }
117     context.m_pos--;
118   } else if (rest == 0) {
119     while (buffer.GetLength() >= 3) {
120       writeNextTriplet(context, buffer);
121     }
122     if (available > 0 || context.hasMoreCharacters()) {
123       context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
124     }
125   } else {
126     e = BCExceptionIllegalStateUnexpectedCase;
127     return;
128   }
129   context.signalEncoderChange(ASCII_ENCODATION);
130 }
encodeChar(FX_WCHAR c,CFX_WideString & sb,int32_t & e)131 int32_t CBC_C40Encoder::encodeChar(FX_WCHAR c, CFX_WideString& sb, int32_t& e) {
132   if (c == ' ') {
133     sb += (FX_WCHAR)'\3';
134     return 1;
135   } else if ((c >= '0') && (c <= '9')) {
136     sb += (FX_WCHAR)(c - 48 + 4);
137     return 1;
138   } else if ((c >= 'A') && (c <= 'Z')) {
139     sb += (FX_WCHAR)(c - 65 + 14);
140     return 1;
141   } else if ((c >= '\0') && (c <= 0x1f)) {
142     sb += (FX_WCHAR)'\0';
143     sb += c;
144     return 2;
145   } else if ((c >= '!') && (c <= '/')) {
146     sb += (FX_WCHAR)'\1';
147     sb += (FX_WCHAR)(c - 33);
148     return 2;
149   } else if ((c >= ':') && (c <= '@')) {
150     sb += (FX_WCHAR)'\1';
151     sb += (FX_WCHAR)(c - 58 + 15);
152     return 2;
153   } else if ((c >= '[') && (c <= '_')) {
154     sb += (FX_WCHAR)'\1';
155     sb += (FX_WCHAR)(c - 91 + 22);
156     return 2;
157   } else if ((c >= 60) && (c <= 0x7f)) {
158     sb += (FX_WCHAR)'\2';
159     sb += (FX_WCHAR)(c - 96);
160     return 2;
161   } else if (c >= 80) {
162     sb += (FX_WCHAR)'\1';
163     sb += (FX_WCHAR)0x001e;
164     int32_t len = 2;
165     len += encodeChar((c - 128), sb, e);
166     BC_EXCEPTION_CHECK_ReturnValue(e, 0);
167     return len;
168   } else {
169     e = BCExceptionIllegalArgument;
170     return 0;
171   }
172 }
backtrackOneCharacter(CBC_EncoderContext & context,CFX_WideString & buffer,CFX_WideString & removed,int32_t lastCharSize,int32_t & e)173 int32_t CBC_C40Encoder::backtrackOneCharacter(CBC_EncoderContext& context,
174                                               CFX_WideString& buffer,
175                                               CFX_WideString& removed,
176                                               int32_t lastCharSize,
177                                               int32_t& e) {
178   int32_t count = buffer.GetLength();
179   buffer.Delete(count - lastCharSize, count);
180   context.m_pos--;
181   FX_WCHAR c = context.getCurrentChar();
182   lastCharSize = encodeChar(c, removed, e);
183   BC_EXCEPTION_CHECK_ReturnValue(e, -1);
184   context.resetSymbolInfo();
185   return lastCharSize;
186 }
encodeToCodewords(CFX_WideString sb,int32_t startPos)187 CFX_WideString CBC_C40Encoder::encodeToCodewords(CFX_WideString sb,
188                                                  int32_t startPos) {
189   FX_WCHAR c1 = sb.GetAt(startPos);
190   FX_WCHAR c2 = sb.GetAt(startPos + 1);
191   FX_WCHAR c3 = sb.GetAt(startPos + 2);
192   int32_t v = (1600 * c1) + (40 * c2) + c3 + 1;
193   FX_WCHAR cw1 = (FX_WCHAR)(v / 256);
194   FX_WCHAR cw2 = (FX_WCHAR)(v % 256);
195   CFX_WideString b1(cw1);
196   CFX_WideString b2(cw2);
197   return b1 + b2;
198 }
199