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 dmtxencodeedifact.c
14  * \brief Edifact encoding rules
15  */
16 
17 /**
18  *
19  *
20  */
21 static void
EncodeNextChunkEdifact(DmtxEncodeStream * stream)22 EncodeNextChunkEdifact(DmtxEncodeStream *stream)
23 {
24    DmtxByte value;
25 
26    if(StreamInputHasNext(stream))
27    {
28       /* Check for FNC1 character, which needs to be sent in ASCII */
29       value = StreamInputPeekNext(stream); CHKERR;
30       if(stream->fnc1 != DmtxUndefined && (int)value == stream->fnc1) {
31          EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR;
32 
33          StreamInputAdvanceNext(stream); CHKERR;
34          AppendValueAscii(stream, DmtxValueFNC1); CHKERR;
35          return;
36       }
37 
38       value = StreamInputAdvanceNext(stream); CHKERR;
39       AppendValueEdifact(stream, value); CHKERR;
40    }
41 }
42 
43 /**
44  *
45  *
46  */
47 static void
AppendValueEdifact(DmtxEncodeStream * stream,DmtxByte value)48 AppendValueEdifact(DmtxEncodeStream *stream, DmtxByte value)
49 {
50    DmtxByte edifactValue, previousOutput;
51 
52    CHKSCHEME(DmtxSchemeEdifact);
53 
54    /*
55     *  TODO: KECA -> korean, circles
56     *  TODO: UNOX -> ISO-2022-JP
57     *  TODO: and so on
58     */
59    if(value < 32 || value > 94)
60    {
61       StreamMarkInvalid(stream, DmtxChannelUnsupportedChar);
62       return;
63    }
64 
65    edifactValue = (value & 0x3f) << 2;
66 
67    switch(stream->outputChainValueCount % 4)
68    {
69       case 0:
70          StreamOutputChainAppend(stream, edifactValue); CHKERR;
71          break;
72       case 1:
73          previousOutput = StreamOutputChainRemoveLast(stream); CHKERR;
74          StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 6)); CHKERR;
75          StreamOutputChainAppend(stream, edifactValue << 2); CHKERR;
76          break;
77       case 2:
78          previousOutput = StreamOutputChainRemoveLast(stream); CHKERR;
79          StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 4)); CHKERR;
80          StreamOutputChainAppend(stream, edifactValue << 4); CHKERR;
81          break;
82       case 3:
83          previousOutput = StreamOutputChainRemoveLast(stream); CHKERR;
84          StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 2)); CHKERR;
85          break;
86    }
87 
88    stream->outputChainValueCount++;
89 }
90 
91 /**
92  * Complete EDIFACT encoding if it matches a known end-of-symbol condition.
93  *
94  *   Term  Clean  Symbol  ASCII   Codeword
95  *   Cond  Bound  Remain  Remain  Sequence
96  *   ----  -----  ------  ------  -----------
97  *    (a)      Y       0       0  [none]
98  *    (b)      Y       1       0  PAD
99  *    (c)      Y       1       1  ASCII
100  *    (d)      Y       2       0  PAD PAD
101  *    (e)      Y       2       1  ASCII PAD
102  *    (f)      Y       2       2  ASCII ASCII
103  *             -       -       0  UNLATCH
104  *
105  * If not matching any of the above, continue without doing anything.
106  */
107 static void
CompleteIfDoneEdifact(DmtxEncodeStream * stream,int sizeIdxRequest)108 CompleteIfDoneEdifact(DmtxEncodeStream *stream, int sizeIdxRequest)
109 {
110    int i;
111    int sizeIdx;
112    int symbolRemaining;
113    DmtxBoolean cleanBoundary;
114    DmtxPassFail passFail;
115    DmtxByte outputTmpStorage[3];
116    DmtxByteList outputTmp;
117 
118    if(stream->status == DmtxStatusComplete)
119       return;
120 
121    /*
122     * If we just completed a triplet (cleanBoundary), 1 or 2 symbol codewords
123     * remain, and our remaining inputs (if any) represented in ASCII would fit
124     * in the remaining space, encode them in ASCII with an implicit unlatch.
125     */
126 
127    cleanBoundary = (stream->outputChainValueCount % 4 == 0) ? DmtxTrue : DmtxFalse;
128 
129    if(cleanBoundary == DmtxTrue)
130    {
131       /* Encode up to 3 codewords to a temporary stream */
132       outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage,
133             sizeof(outputTmpStorage), &passFail);
134 
135       if(passFail == DmtxFail)
136       {
137          StreamMarkFatal(stream, DmtxErrorUnknown);
138          return;
139       }
140 
141       if(outputTmp.length < 3)
142       {
143          /* Find minimum symbol size for projected length */
144          sizeIdx = FindSymbolSize(stream->output->length + outputTmp.length, sizeIdxRequest); CHKSIZE;
145 
146          /* Find remaining capacity over current length */
147          symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); CHKERR;
148 
149          if(symbolRemaining < 3 && outputTmp.length <= symbolRemaining)
150          {
151             EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR;
152 
153             for(i = 0; i < outputTmp.length; i++)
154             {
155                AppendValueAscii(stream, outputTmp.b[i]); CHKERR;
156             }
157 
158             /* Register progress since encoding happened outside normal path */
159             stream->inputNext = stream->input->length;
160 
161             /* Pad remaining if necessary */
162             PadRemainingInAscii(stream, sizeIdx); CHKERR;
163             StreamMarkComplete(stream, sizeIdx);
164             return;
165          }
166       }
167    }
168 
169    if(!StreamInputHasNext(stream))
170    {
171       sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE;
172       symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); CHKERR;
173 
174       /* Explicit unlatch required unless on clean boundary and full symbol */
175       if(cleanBoundary == DmtxFalse || symbolRemaining > 0)
176       {
177          EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR;
178          sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE;
179          PadRemainingInAscii(stream, sizeIdx); CHKERR;
180       }
181 
182       StreamMarkComplete(stream, sizeIdx);
183    }
184 }
185