/** * 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 EncodeNextChunkBase256(DmtxEncodeStream *stream) { DmtxByte value; if(StreamInputHasNext(stream)) { value = StreamInputAdvanceNext(stream); CHKERR; AppendValueBase256(stream, value); CHKERR; } }
/** * peek at first/oldest * used for ascii double digit */ static DmtxByte StreamInputPeekNext(DmtxEncodeStream *stream) { DmtxByte value = 0; if(StreamInputHasNext(stream)) value = stream->input->b[stream->inputNext]; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); return value; }
static void CompleteIfDoneAscii(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; PadRemainingInAscii(stream, sizeIdx); CHKERR; StreamMarkComplete(stream, sizeIdx); } }
/** * 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; }
/** * check remaining symbol capacity and remaining codewords * if the chain can finish perfectly at the end of symbol data words there is a * special one-byte length header value that can be used (i think ... read the * spec again before commiting to anything) */ static void CompleteIfDoneBase256(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; int headerByteCount, outputLength, symbolRemaining; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount; assert(headerByteCount == 1 || headerByteCount == 2); /* Check for special case where every last symbol word is used */ if(headerByteCount == 2) { /* Find symbol size as if headerByteCount was only 1 */ outputLength = stream->output->length - 1; sizeIdx = FindSymbolSize(outputLength, sizeIdxRequest); /* No CHKSIZE */ if(sizeIdx != DmtxUndefined) { symbolRemaining = GetRemainingSymbolCapacity(outputLength, sizeIdx); if(symbolRemaining == 0) { /* Perfect fit -- complete encoding */ UpdateBase256ChainHeader(stream, sizeIdx); CHKERR; StreamMarkComplete(stream, sizeIdx); return; } } } /* Normal case */ sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); PadRemainingInAscii(stream, sizeIdx); StreamMarkComplete(stream, sizeIdx); } }
/** * consider receiving instantiated DmtxByteList instead of the output components */ static DmtxByteList EncodeTmpRemainingInAscii(DmtxEncodeStream *stream, DmtxByte *storage, int capacity, DmtxPassFail *passFail) { DmtxEncodeStream streamAscii; DmtxByteList output = dmtxByteListBuild(storage, capacity); /* Create temporary copy of stream that writes to storage */ streamAscii = *stream; streamAscii.currentScheme = DmtxSchemeAscii; streamAscii.outputChainValueCount = 0; streamAscii.outputChainWordCount = 0; streamAscii.reason = NULL; streamAscii.sizeIdx = DmtxUndefined; streamAscii.status = DmtxStatusEncoding; streamAscii.output = &output; while(dmtxByteListHasCapacity(streamAscii.output)) { if(StreamInputHasNext(&streamAscii)) EncodeNextChunkAscii(&streamAscii, DmtxEncodeNormal); /* No CHKERR */ else break; } /* * We stopped encoding before attempting to write beyond output boundary so * any stream errors are truly unexpected. The passFail status indicates * whether output.length can be trusted by the calling function. */ if(streamAscii.status == DmtxStatusInvalid || streamAscii.status == DmtxStatusFatal) *passFail = DmtxFail; else *passFail = DmtxPass; return output; }
/** * Complete C40/Text/X12 encoding if it matches a known end-of-symbol condition. * * Term Trip Symbol Codeword * Cond Size Remain Sequence * ---- ---- ------ ----------------------- * (a) 3 2 Special case * - - UNLATCH [PAD] */ static void CompleteIfDoneCTX(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; int symbolRemaining; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); if(symbolRemaining > 0) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; PadRemainingInAscii(stream, sizeIdx); } StreamMarkComplete(stream, sizeIdx); } }