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