1 /**
2 * libdmtx - Data Matrix Encoding/Decoding Library
3 * Copyright 2011 Mike Laughton. All rights reserved.
4 * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved.
5 *
6 * See LICENSE file in the main project directory for full
7 * terms of use and distribution.
8 *
9 * Contact:
10 * Vadim A. Misbakh-Soloviov <dmtx@mva.name>
11 * Mike Laughton <mike@dragonflylogic.com>
12 *
13 * \file dmtxencodescheme.c
14 * \brief Logic for encoding in single scheme
15 */
16
17 /**
18 * In this file:
19 *
20 * A "word" refers to a full codeword byte to be appended to the encoded output.
21 *
22 * A "value" refers to any scheme value being appended to the output stream,
23 * regardless of how many bytes are used to represent it. Examples:
24 *
25 * ASCII: 1 value in 1 codeword
26 * ASCII (digits): 2 values in 1 codeword
27 * C40/Text/X12: 3 values in 2 codewords
28 * C40/Text/X12 (unlatch): 1 values in 1 codeword
29 * EDIFACT: 4 values in 3 codewords
30 * Base 256: 1 value in 1 codeword
31 *
32 * * Shifts count as values, so outputChainValueCount will reflect these.
33 *
34 * * Latches and unlatches are also counted as values, but always in the
35 * scheme being exited.
36 *
37 * * Base256 header bytes are not included as values.
38 *
39 * A "chunk" refers to the minimum grouping of values in a schema that must be
40 * encoded together.
41 *
42 * ASCII: 1 value (1 codeword) in 1 chunk
43 * ASCII (digits): 2 values (1 codeword) in 1 chunk (optional)
44 * C40/Text/X12: 3 values (2 codewords) in 1 chunk
45 * C40/Text/X12 (unlatch): 1 value (1 codeword) in 1 chunk
46 * EDIFACT: 1 value (1 codeword*) in 1 chunk
47 * Base 256: 1 value (1 codeword) in 1 chunk
48 *
49 * * EDIFACT writes 6 bits at a time, but progress is tracked to the next byte
50 * boundary. If unlatch value finishes mid-byte, the remaining bits before
51 * the next boundary are set to zero.
52 *
53 * Each scheme implements 3 equivalent functions:
54 * * EncodeNextChunk[Scheme]
55 * * AppendValue[Scheme]
56 * * CompleteIfDone[Scheme]
57 *
58 * The function EncodeNextChunk() (no Scheme in the name) knows which scheme-
59 * specific implementations to call based on the stream's current encodation
60 * scheme.
61 *
62 * It's important that EncodeNextChunk[Scheme] not call CompleteIfDone[Scheme]
63 * directly because some parts of the logic might want to encode a stream
64 * without allowing the padding and other extra logic that can occur when an
65 * end-of-symbol condition is triggered.
66 */
67
68 /* Verify stream is using expected scheme */
69 #define CHKSCHEME(s) { \
70 if(stream->currentScheme != (s)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } \
71 }
72
73 /* CHKERR should follow any call that might alter stream status */
74 #define CHKERR { \
75 if(stream->status != DmtxStatusEncoding) { return; } \
76 }
77
78 /* CHKSIZE should follows typical calls to FindSymbolSize() */
79 #define CHKSIZE { \
80 if(sizeIdx == DmtxUndefined) { StreamMarkInvalid(stream, DmtxErrorUnknown); return; } \
81 }
82
83 /**
84 *
85 *
86 */
87 static int
EncodeSingleScheme(DmtxByteList * input,DmtxByteList * output,int sizeIdxRequest,DmtxScheme scheme,int fnc1)88 EncodeSingleScheme(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1)
89 {
90 DmtxEncodeStream stream;
91
92 stream = StreamInit(input, output);
93 stream.fnc1 = fnc1;
94
95 /* 1st FNC1 special case, encode before scheme switch */
96 if (fnc1 != DmtxUndefined && (int)(input->b[0]) == fnc1)
97 {
98 StreamInputAdvanceNext(&stream);
99 AppendValueAscii(&stream, DmtxValueFNC1);
100 }
101
102 /* Continue encoding until complete */
103 while(stream.status == DmtxStatusEncoding)
104 EncodeNextChunk(&stream, scheme, DmtxEncodeNormal, sizeIdxRequest);
105
106 /* Verify encoding completed and all inputs were consumed */
107 if(stream.status != DmtxStatusComplete || StreamInputHasNext(&stream))
108 return DmtxUndefined;
109
110 return stream.sizeIdx;
111 }
112
113 /**
114 * This function distributes work to the equivalent scheme-specific
115 * implementation.
116 *
117 * Each of these functions will encode the next symbol input word, and in some
118 * cases this requires additional input words to be encoded as well.
119 */
120 static void
EncodeNextChunk(DmtxEncodeStream * stream,int scheme,int option,int sizeIdxRequest)121 EncodeNextChunk(DmtxEncodeStream *stream, int scheme, int option, int sizeIdxRequest)
122 {
123 /* Special case: Prevent X12 from entering state with no way to unlatch */
124 if(stream->currentScheme != DmtxSchemeX12 && scheme == DmtxSchemeX12)
125 {
126 if(PartialX12ChunkRemains(stream))
127 scheme = DmtxSchemeAscii;
128 }
129
130 /* Change to target scheme if necessary */
131 if(stream->currentScheme != scheme)
132 {
133 EncodeChangeScheme(stream, scheme, DmtxUnlatchExplicit); CHKERR;
134 CHKSCHEME(scheme);
135 }
136
137 /* Special case: Edifact may be done before writing first word */
138 if(scheme == DmtxSchemeEdifact)
139 CompleteIfDoneEdifact(stream, sizeIdxRequest); CHKERR;
140
141 switch(stream->currentScheme)
142 {
143 case DmtxSchemeAscii:
144 EncodeNextChunkAscii(stream, option); CHKERR;
145 CompleteIfDoneAscii(stream, sizeIdxRequest); CHKERR;
146 break;
147 case DmtxSchemeC40:
148 case DmtxSchemeText:
149 case DmtxSchemeX12:
150 EncodeNextChunkCTX(stream, sizeIdxRequest); CHKERR;
151 CompleteIfDoneCTX(stream, sizeIdxRequest); CHKERR;
152 break;
153 case DmtxSchemeEdifact:
154 EncodeNextChunkEdifact(stream); CHKERR;
155 CompleteIfDoneEdifact(stream, sizeIdxRequest); CHKERR;
156 break;
157 case DmtxSchemeBase256:
158 EncodeNextChunkBase256(stream); CHKERR;
159 CompleteIfDoneBase256(stream, sizeIdxRequest); CHKERR;
160 break;
161 default:
162 StreamMarkFatal(stream, DmtxErrorUnknown);
163 break;
164 }
165 }
166
167 /**
168 *
169 *
170 */
171 static void
EncodeChangeScheme(DmtxEncodeStream * stream,DmtxScheme targetScheme,int unlatchType)172 EncodeChangeScheme(DmtxEncodeStream *stream, DmtxScheme targetScheme, int unlatchType)
173 {
174 /* Nothing to do */
175 if(stream->currentScheme == targetScheme)
176 return;
177
178 /* Every latch must go through ASCII */
179 switch(stream->currentScheme)
180 {
181 case DmtxSchemeC40:
182 case DmtxSchemeText:
183 case DmtxSchemeX12:
184 if(unlatchType == DmtxUnlatchExplicit)
185 {
186 AppendUnlatchCTX(stream); CHKERR;
187 }
188 break;
189 case DmtxSchemeEdifact:
190 if(unlatchType == DmtxUnlatchExplicit)
191 {
192 AppendValueEdifact(stream, DmtxValueEdifactUnlatch); CHKERR;
193 }
194 break;
195 default:
196 /* Nothing to do for ASCII or Base 256 */
197 assert(stream->currentScheme == DmtxSchemeAscii ||
198 stream->currentScheme == DmtxSchemeBase256);
199 break;
200 }
201 stream->currentScheme = DmtxSchemeAscii;
202
203 /* Anything other than ASCII (the default) requires a latch */
204 switch(targetScheme)
205 {
206 case DmtxSchemeC40:
207 AppendValueAscii(stream, DmtxValueC40Latch); CHKERR;
208 break;
209 case DmtxSchemeText:
210 AppendValueAscii(stream, DmtxValueTextLatch); CHKERR;
211 break;
212 case DmtxSchemeX12:
213 AppendValueAscii(stream, DmtxValueX12Latch); CHKERR;
214 break;
215 case DmtxSchemeEdifact:
216 AppendValueAscii(stream, DmtxValueEdifactLatch); CHKERR;
217 break;
218 case DmtxSchemeBase256:
219 AppendValueAscii(stream, DmtxValueBase256Latch); CHKERR;
220 break;
221 default:
222 /* Nothing to do for ASCII */
223 CHKSCHEME(DmtxSchemeAscii);
224 break;
225 }
226 stream->currentScheme = targetScheme;
227
228 /* Reset new chain length to zero */
229 stream->outputChainWordCount = 0;
230 stream->outputChainValueCount = 0;
231
232 /* Insert header byte if just latched to Base256 */
233 if(targetScheme == DmtxSchemeBase256)
234 {
235 UpdateBase256ChainHeader(stream, DmtxUndefined); CHKERR;
236 }
237 }
238
239 /**
240 *
241 *
242 */
243 static int
GetRemainingSymbolCapacity(int outputLength,int sizeIdx)244 GetRemainingSymbolCapacity(int outputLength, int sizeIdx)
245 {
246 int capacity;
247 int remaining;
248
249 if(sizeIdx == DmtxUndefined)
250 {
251 remaining = DmtxUndefined;
252 }
253 else
254 {
255 capacity = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx);
256 remaining = capacity - outputLength;
257 }
258
259 return remaining;
260 }
261