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 dmtxencodeascii.c
14 * \brief ASCII encoding rules
15 */
16
17 /**
18 * Simple single scheme encoding uses "Normal"
19 * The optimizer needs to track "Expanded" and "Compact" streams separately, so they
20 * are called explicitly.
21 *
22 * Normal: Automatically collapses 2 consecutive digits into one codeword
23 * Expanded: Uses a whole codeword to represent a digit (never collapses)
24 * Compact: Collapses 2 digits into a single codeword or marks the stream
25 * invalid if either values are not digits
26 *
27 * \param stream
28 * \param option [Expanded|Compact|Normal]
29 */
30 static void
EncodeNextChunkAscii(DmtxEncodeStream * stream,int option)31 EncodeNextChunkAscii(DmtxEncodeStream *stream, int option)
32 {
33 DmtxByte v0, v1;
34 DmtxBoolean compactDigits;
35
36 if(StreamInputHasNext(stream))
37 {
38 v0 = StreamInputAdvanceNext(stream); CHKERR;
39
40 if((option == DmtxEncodeCompact || option == DmtxEncodeNormal) &&
41 StreamInputHasNext(stream))
42 {
43 v1 = StreamInputPeekNext(stream); CHKERR;
44
45 /* Check for FNC1 character */
46 if(stream->fnc1 != DmtxUndefined && (int)v1 == stream->fnc1)
47 {
48 v1 = 0;
49 compactDigits = DmtxFalse;
50 }
51 else
52 compactDigits = (ISDIGIT(v0) && ISDIGIT(v1)) ? DmtxTrue : DmtxFalse;
53 }
54 else /* option == DmtxEncodeFull */
55 {
56 v1 = 0;
57 compactDigits = DmtxFalse;
58 }
59
60 if(compactDigits == DmtxTrue)
61 {
62 /* Two adjacent digit chars: Make peek progress official and encode */
63 StreamInputAdvanceNext(stream); CHKERR;
64 AppendValueAscii(stream, 10 * (v0-'0') + (v1-'0') + 130); CHKERR;
65 }
66 else if(option == DmtxEncodeCompact)
67 {
68 /* Can't compact non-digits */
69 StreamMarkInvalid(stream, DmtxErrorCantCompactNonDigits);
70 }
71 else
72 {
73 /* Encode single ASCII value */
74 if(stream->fnc1 != DmtxUndefined && (int)v0 == stream->fnc1)
75 {
76 /* FNC1 */
77 AppendValueAscii(stream, DmtxValueFNC1); CHKERR;
78 }
79 else if(v0 < 128)
80 {
81 /* Regular ASCII */
82 AppendValueAscii(stream, v0 + 1); CHKERR;
83 }
84 else
85 {
86 /* Extended ASCII */
87 AppendValueAscii(stream, DmtxValueAsciiUpperShift); CHKERR;
88 AppendValueAscii(stream, v0 - 127); CHKERR;
89 }
90 }
91 }
92 }
93
94 /**
95 * this code is separated from EncodeNextChunkAscii() because it needs to be
96 * called directly elsewhere
97 */
98 static void
AppendValueAscii(DmtxEncodeStream * stream,DmtxByte value)99 AppendValueAscii(DmtxEncodeStream *stream, DmtxByte value)
100 {
101 CHKSCHEME(DmtxSchemeAscii);
102
103 StreamOutputChainAppend(stream, value); CHKERR;
104 stream->outputChainValueCount++;
105 }
106
107 /**
108 *
109 *
110 */
111 static void
CompleteIfDoneAscii(DmtxEncodeStream * stream,int sizeIdxRequest)112 CompleteIfDoneAscii(DmtxEncodeStream *stream, int sizeIdxRequest)
113 {
114 int sizeIdx;
115
116 if(stream->status == DmtxStatusComplete)
117 return;
118
119 if(!StreamInputHasNext(stream))
120 {
121 sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE;
122 PadRemainingInAscii(stream, sizeIdx); CHKERR;
123 StreamMarkComplete(stream, sizeIdx);
124 }
125 }
126
127 /**
128 * Can we just receive a length to pad here? I don't like receiving
129 * sizeIdxRequest (or sizeIdx) this late in the game
130 */
131 static void
PadRemainingInAscii(DmtxEncodeStream * stream,int sizeIdx)132 PadRemainingInAscii(DmtxEncodeStream *stream, int sizeIdx)
133 {
134 int symbolRemaining;
135 DmtxByte padValue;
136
137 CHKSCHEME(DmtxSchemeAscii);
138 CHKSIZE;
139
140 symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx);
141
142 /* First pad character is not randomized */
143 if(symbolRemaining > 0)
144 {
145 padValue = DmtxValueAsciiPad;
146 StreamOutputChainAppend(stream, padValue); CHKERR;
147 symbolRemaining--;
148 }
149
150 /* All remaining pad characters are randomized based on character position */
151 while(symbolRemaining > 0)
152 {
153 padValue = Randomize253State(DmtxValueAsciiPad, stream->output->length + 1);
154 StreamOutputChainAppend(stream, padValue); CHKERR;
155 symbolRemaining--;
156 }
157 }
158
159 /**
160 * consider receiving instantiated DmtxByteList instead of the output components
161 */
162 static DmtxByteList
EncodeTmpRemainingInAscii(DmtxEncodeStream * stream,DmtxByte * storage,int capacity,DmtxPassFail * passFail)163 EncodeTmpRemainingInAscii(DmtxEncodeStream *stream, DmtxByte *storage,
164 int capacity, DmtxPassFail *passFail)
165 {
166 DmtxEncodeStream streamAscii;
167 DmtxByteList output = dmtxByteListBuild(storage, capacity);
168
169 /* Create temporary copy of stream that writes to storage */
170 streamAscii = *stream;
171 streamAscii.currentScheme = DmtxSchemeAscii;
172 streamAscii.outputChainValueCount = 0;
173 streamAscii.outputChainWordCount = 0;
174 streamAscii.reason = NULL;
175 streamAscii.sizeIdx = DmtxUndefined;
176 streamAscii.status = DmtxStatusEncoding;
177 streamAscii.output = &output;
178
179 while(dmtxByteListHasCapacity(streamAscii.output))
180 {
181 if(StreamInputHasNext(&streamAscii))
182 EncodeNextChunkAscii(&streamAscii, DmtxEncodeNormal); /* No CHKERR */
183 else
184 break;
185 }
186
187 /*
188 * We stopped encoding before attempting to write beyond output boundary so
189 * any stream errors are truly unexpected. The passFail status indicates
190 * whether output.length can be trusted by the calling function.
191 */
192
193 if(streamAscii.status == DmtxStatusInvalid || streamAscii.status == DmtxStatusFatal)
194 *passFail = DmtxFail;
195 else
196 *passFail = DmtxPass;
197
198 return output;
199 }
200
201 /**
202 * \brief Randomize 253 state
203 * \param codewordValue
204 * \param codewordPosition
205 * \return Randomized value
206 */
207 static DmtxByte
Randomize253State(DmtxByte cwValue,int cwPosition)208 Randomize253State(DmtxByte cwValue, int cwPosition)
209 {
210 int pseudoRandom, tmp;
211
212 pseudoRandom = ((149 * cwPosition) % 253) + 1;
213 tmp = cwValue + pseudoRandom;
214 if(tmp > 254)
215 tmp -= 254;
216
217 assert(tmp >= 0 && tmp < 256);
218
219 return (DmtxByte)tmp;
220 }
221