/** * Partial chunks are not valid in X12. Encode using ASCII instead, using * an implied unlatch if there is exactly one ascii codeword and one symbol * codeword remaining. Otherwise use explicit unlatch. */ static void CompletePartialX12(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx; int symbolRemaining; DmtxPassFail passFail; DmtxByte outputTmpStorage[2]; DmtxByteList outputTmp; if(stream->currentScheme != DmtxSchemeX12) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); /* Roll back input progress */ for(i = 0; i < valueList->length; i++) { StreamInputAdvancePrev(stream); CHKERR; } /* Encode up to 2 codewords to a temporary stream */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); sizeIdx = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); if(outputTmp.length == 1 && symbolRemaining == 1) { /* End of symbol condition (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } else { /* Finish in ASCII (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } }
/** * 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); } }
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); } }
/** * 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); } }
/** * The remaining values can exist in 3 possible cases: * * a) 1 C40/Text/X12 remaining == 1 data * b) 2 C40/Text/X12 remaining == 1 shift + 1 data * c) 2 C40/Text/X12 remaining == 1 data + 1 data * * To distinguish between cases (b) and (c), encode the final input value to * C40/Text/X12 in a temporary location and check the resulting length. If * it expands to multiple values it represents (b); otherwise it is (c). This * accounts for both shift and upper shift conditions. * * Note that in cases (a) and (c) the final C40/Text/X12 value encoded in the * previous chunk may have been a shift value, but this will be ignored by * the decoder due to the implicit shift to ASCII. <-- what if symbol is much * larger though? * * Term Value Symbol Codeword * Cond Count Remain Sequence * ---- ------- ------ ------------------------ * (b) C40 2 2 C40+C40+0 * (d) ASCII 1 1 ASCII (implicit unlatch) * (c) ASCII 1 2 UNLATCH ASCII * - - UNLATCH (finish ASCII) */ static void CompletePartialC40Text(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx1, sizeIdx2; int symbolRemaining1, symbolRemaining2; DmtxPassFail passFail; DmtxByte inputValue; DmtxByte outputTmpStorage[4]; DmtxByteList outputTmp = dmtxByteListBuild(outputTmpStorage, sizeof(outputTmpStorage)); if(stream->currentScheme != DmtxSchemeC40 && stream->currentScheme != DmtxSchemeText) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); sizeIdx1 = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); sizeIdx2 = FindSymbolSize(stream->output->length + 2, sizeIdxRequest); symbolRemaining1 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx1); symbolRemaining2 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx2); if(valueList->length == 2 && symbolRemaining2 == 2) { /* End of symbol condition (b) -- Use Shift1 to pad final list value */ dmtxByteListPush(valueList, DmtxValueCTXShift1, &passFail); CHKPASS; AppendValuesCTX(stream, valueList); CHKERR; StreamMarkComplete(stream, sizeIdx2); } else { /* * Rollback progress of previously consumed input value(s) since ASCII * encoder will be used to finish the symbol. 2 rollbacks are needed if * valueList holds 2 data words (i.e., not shifts or upper shifts). */ StreamInputAdvancePrev(stream); CHKERR; inputValue = StreamInputPeekNext(stream); CHKERR; /* Test-encode most recently consumed input value to C40/Text/X12 */ PushCTXValues(&outputTmp, inputValue, stream->currentScheme, &passFail); if(valueList->length == 2 && outputTmp.length == 1) StreamInputAdvancePrev(stream); CHKERR; /* Re-use outputTmp to hold ASCII representation of 1-2 input values */ /* XXX Refactor how the DmtxByteList is passed back here */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); if(passFail == DmtxFail) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } if(outputTmp.length == 1 && symbolRemaining1 == 1) { /* End of symbol condition (d) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } else { /* Finish in ASCII (c) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx1 = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx1); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } } }
/** * \brief Convert message into Data Mosaic image * * 1) count how many codewords it would take to encode the whole thing * 2) take ceiling N of codeword count divided by 3 * 3) using minimum symbol size that can accomodate N codewords: * 4) create several barcodes over iterations of increasing numbers of * input codewords until you go one too far * 5) if codewords remain after filling R, G, and B barcodes then go back * to 3 and try with next larger size * 6) take the 3 different images you created and write out a new barcode * * \param enc * \param inputSize * \param inputString * \param sizeIdxRequest * \return DmtxPass | DmtxFail */ extern DmtxPassFail dmtxEncodeDataMosaic(DmtxEncode *enc, int inputSize, unsigned char *inputString) { unsigned char *inputStringR, *inputStringG, *inputStringB; int tmpInputSize; int inputSizeR, inputSizeG, inputSizeB; int sizeIdxAttempt, sizeIdxFirst, sizeIdxLast; int row, col, mappingRows, mappingCols; DmtxEncode *encR, *encG, *encB; /* Use 1/3 (ceiling) of inputSize establish input size target */ tmpInputSize = (inputSize + 2) / 3; inputSizeR = tmpInputSize; inputSizeG = tmpInputSize; inputSizeB = inputSize - (inputSizeR + inputSizeG); inputStringR = inputString; inputStringG = inputStringR + inputSizeR; inputStringB = inputStringG + inputSizeG; /* Use 1/3 (floor) of dataWordCount establish first symbol size attempt */ sizeIdxFirst = FindSymbolSize(tmpInputSize, enc->sizeIdxRequest); if(sizeIdxFirst == DmtxUndefined) return DmtxFail; /* Set the last possible symbol size for this symbol shape or specific size request */ if(enc->sizeIdxRequest == DmtxSymbolSquareAuto) sizeIdxLast = DmtxSymbolSquareCount - 1; else if(enc->sizeIdxRequest == DmtxSymbolRectAuto) sizeIdxLast = DmtxSymbolSquareCount + DmtxSymbolRectCount - 1; else sizeIdxLast = sizeIdxFirst; encR = encG = encB = NULL; /* Try increasing symbol sizes until 3 of them can hold all input values */ for(sizeIdxAttempt = sizeIdxFirst; sizeIdxAttempt <= sizeIdxLast; sizeIdxAttempt++) { dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); encR = dmtxEncodeCreate(); encG = dmtxEncodeCreate(); encB = dmtxEncodeCreate(); /* Copy all settings from master DmtxEncode, including pointer to image and message, which is initially null */ *encR = *encG = *encB = *enc; dmtxEncodeSetProp(encR, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeSetProp(encG, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeSetProp(encB, DmtxPropSizeRequest, sizeIdxAttempt); /* RED LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encR, inputSizeR, inputStringR); if(encR->region.sizeIdx != sizeIdxAttempt) continue; /* GREEN LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encG, inputSizeG, inputStringG); if(encG->region.sizeIdx != sizeIdxAttempt) continue; /* BLUE LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encB, inputSizeB, inputStringB); if(encB->region.sizeIdx != sizeIdxAttempt) continue; /* If we get this far we found a fit */ break; } if(encR == NULL || encG == NULL || encB == NULL) { dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); return DmtxFail; } /* Now we have the correct sizeIdxAttempt, and they all fit into the desired size */ /* Perform the red portion of the final encode to set internals correctly */ dmtxEncodeSetProp(enc, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeDataMatrix(enc, inputSizeR, inputStringR); /* Zero out the array and overwrite the bits in 3 passes */ mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdxAttempt); mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdxAttempt); memset(enc->message->array, 0x00, sizeof(unsigned char) * enc->region.mappingRows * enc->region.mappingCols); ModulePlacementEcc200(enc->message->array, encR->message->code, sizeIdxAttempt, DmtxModuleOnRed); /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */ for(row = 0; row < mappingRows; row++) { for(col = 0; col < mappingCols; col++) { enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited)); } } ModulePlacementEcc200(enc->message->array, encG->message->code, sizeIdxAttempt, DmtxModuleOnGreen); /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */ for(row = 0; row < mappingRows; row++) { for(col = 0; col < mappingCols; col++) { enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited)); } } ModulePlacementEcc200(enc->message->array, encB->message->code, sizeIdxAttempt, DmtxModuleOnBlue); /* Destroy encR, encG, and encB */ dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); PrintPattern(enc); return DmtxPass; }