/** * Return DmtxTrue 1 or 2 X12 values remain, otherwise DmtxFalse */ static DmtxBoolean PartialX12ChunkRemains(DmtxEncodeStream *stream) { DmtxEncodeStream streamTmp; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); DmtxPassFail passFail; /* Create temporary copy of stream to track test input progress */ streamTmp = *stream; streamTmp.currentScheme = DmtxSchemeX12; streamTmp.outputChainValueCount = 0; streamTmp.outputChainWordCount = 0; streamTmp.reason = NULL; streamTmp.sizeIdx = DmtxUndefined; streamTmp.status = DmtxStatusEncoding; streamTmp.output = NULL; while(StreamInputHasNext(&streamTmp)) { inputValue = StreamInputAdvanceNext(&streamTmp); if(stream->status != DmtxStatusEncoding) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, streamTmp.currentScheme, &passFail); if(passFail == DmtxFail) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Not a final partial chunk */ if(valueList.length >= 3) return DmtxFalse; } return (valueList.length == 0) ? DmtxFalse : DmtxTrue; }
/** * Simple single scheme encoding uses "Normal" * The optimizer needs to track "Expanded" and "Compact" streams separately, so they * are called explicitly. * * Normal: Automatically collapses 2 consecutive digits into one codeword * Expanded: Uses a whole codeword to represent a digit (never collapses) * Compact: Collapses 2 digits into a single codeword or marks the stream * invalid if either values are not digits * * \param stream * \param option [Expanded|Compact|Normal] */ static void EncodeNextChunkAscii(DmtxEncodeStream *stream, int option) { DmtxByte v0, v1; DmtxBoolean compactDigits; if(StreamInputHasNext(stream)) { v0 = StreamInputAdvanceNext(stream); CHKERR; if((option == DmtxEncodeCompact || option == DmtxEncodeNormal) && StreamInputHasNext(stream)) { v1 = StreamInputPeekNext(stream); CHKERR; compactDigits = (ISDIGIT(v0) && ISDIGIT(v1)) ? DmtxTrue : DmtxFalse; } else /* option == DmtxEncodeFull */ { v1 = 0; compactDigits = DmtxFalse; } if(compactDigits == DmtxTrue) { /* Two adjacent digit chars: Make peek progress official and encode */ StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, 10 * (v0-'0') + (v1-'0') + 130); CHKERR; } else if(option == DmtxEncodeCompact) { /* Can't compact non-digits */ StreamMarkInvalid(stream, DmtxErrorCantCompactNonDigits); } else { /* Encode single ASCII value */ if(v0 < 128) { /* Regular ASCII */ AppendValueAscii(stream, v0 + 1); CHKERR; } else { /* Extended ASCII */ AppendValueAscii(stream, DmtxValueAsciiUpperShift); CHKERR; AppendValueAscii(stream, v0 - 127); CHKERR; } } } }
static void EncodeNextChunkCTX(DmtxEncodeStream *stream, int sizeIdxRequest) { DmtxPassFail passFail; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); while(StreamInputHasNext(stream)) { inputValue = StreamInputAdvanceNext(stream); CHKERR; /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, stream->currentScheme, &passFail); if(passFail == DmtxFail) { /* XXX Perhaps PushCTXValues should return this error code */ StreamMarkInvalid(stream, DmtxErrorUnsupportedCharacter); return; } /* If there at least 3 CTX values available encode them to output */ while(valueList.length >= 3) { AppendValuesCTX(stream, &valueList); CHKERR; ShiftValueListBy3(&valueList, &passFail); CHKPASS; } /* Finished on byte boundary -- done with current chunk */ if(valueList.length == 0) break; } /* * Special case: If all input values have been consumed and 1 or 2 unwritten * C40/Text/X12 values remain, finish encoding the symbol according to the * established end-of-symbol conditions. */ if(!StreamInputHasNext(stream) && valueList.length > 0) { if(stream->currentScheme == DmtxSchemeX12) { CompletePartialX12(stream, &valueList, sizeIdxRequest); CHKERR; } else { CompletePartialC40Text(stream, &valueList, sizeIdxRequest); CHKERR; } } }
static void AdvanceCTX(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int ctxValueCount, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; /* we won't actually use inputNext here */ switch(targetState) { case C40Offset0: case TextOffset0: case X12Offset0: isStartState = (ctxValueCount % 3 == 0) ? DmtxTrue : DmtxFalse; break; case C40Offset1: case TextOffset1: case X12Offset1: isStartState = (ctxValueCount % 3 == 1) ? DmtxTrue : DmtxFalse; break; case C40Offset2: case TextOffset2: case X12Offset2: isStartState = (ctxValueCount % 3 == 2) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }
static void AdvanceEdifact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case EdifactOffset0: isStartState = (inputNext % 4 == 0) ? DmtxTrue : DmtxFalse; break; case EdifactOffset1: isStartState = (inputNext % 4 == 1) ? DmtxTrue : DmtxFalse; break; case EdifactOffset2: isStartState = (inputNext % 4 == 2) ? DmtxTrue : DmtxFalse; break; case EdifactOffset3: isStartState = (inputNext % 4 == 3) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); if(currentStream->status == DmtxStatusEncoding && currentStream->currentScheme == DmtxSchemeEdifact) EncodeNextChunk(targetStream, DmtxSchemeEdifact, DmtxEncodeNormal, sizeIdxRequest); else StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }
static void AppendUnlatchCTX(DmtxEncodeStream *stream) { if(!IsCTX(stream->currentScheme)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Verify we are on byte boundary */ if(stream->outputChainValueCount % 3 != 0) { StreamMarkInvalid(stream, DmtxErrorNotOnByteBoundary); return; } StreamOutputChainAppend(stream, DmtxValueCTXUnlatch); CHKERR; stream->outputChainValueCount++; }
static void AdvanceAsciiCompact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case AsciiCompactOffset0: isStartState = (inputNext % 2 == 0) ? DmtxTrue : DmtxFalse; break; case AsciiCompactOffset1: isStartState = (inputNext % 2 == 1) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }