ropensci / baRcodeR

Labeling, tracking, and organizing samples with 2D barcodes
https://docs.ropensci.org/baRcodeR
GNU General Public License v3.0
36 stars 12 forks source link

Problems with certain linear barcodes #16

Closed thokall closed 5 years ago

thokall commented 5 years ago

Generating linear barcodes seem to have issues with certain strings, whereas most strings are working as expected.

This code block works

library(baRcodeR)
custom_create_PDF(Labels = "SEP0164581", type = "linear")

but this does not generate a valid linear barcode

library(baRcodeR)
custom_create_PDF(Labels = "SEP0164580", type = "linear")

Both examples will generate valid qr-codes if changing the type to matrix, but since I am using external tools that can only read linear barcodes I need to find a tool for generating these.

I have generated around 10000 codes of the same type and by testing a subset there seem to be between 5-10% of the generated barcodes that are not valid.

I have tested on both a linux and a mac os x machine with the same results. Below is my R session information for the linux machine.

sessionInfo()
R version 3.5.1 (2018-07-02)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.1 LTS

Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=sv_SE.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=sv_SE.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=sv_SE.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=sv_SE.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] baRcodeR_0.1.2 qrcode_0.1.1  

loaded via a namespace (and not attached):
 [1] compiler_3.5.1    magrittr_1.5      tools_3.5.1       yaml_2.2.0        stringi_1.2.4    
 [6] R.methodsS3_1.7.1 grid_3.5.1        stringr_1.3.1     R.utils_2.8.0     R.oo_1.22.0
thokall commented 5 years ago

Looking at one example the checksum part of the barcode looks to be the problem, but it is beyond me to why some work whereas other do not.

thokall commented 5 years ago

Not sure anyone is reading this, but I think the problems is related to the checksum calculation and/or translation from these to the barcode.

For the string "SEP0164580" the checksum in the below function is 1338 and the check_character hence 102. The binary_label will then try to extract value the barcode corresponding to the 102 + 32 from the barcodes128 object supplied with the package. Since there is no entry in the ASCII column with value 134 this will not add a valild checksum to the barcode

code_128_make <- function(Labels){
  ## labels is a character string
  ## read in dict 
  Barcodes <- barcodes128
  ## double check Labels
  Labels <- as.character(Labels)
  Labels <- iconv(Labels, from = "utf-8", to = "ascii", sub = "-")
  start_code <- 209
  lab_chars <- unlist(strsplit(Labels, split = ""))
  lab_values <- sapply(lab_chars, function(x) utf8ToInt(x))
  # ascii to code 128 is just a difference of 32, this line keeps clarity
  code_values <- lab_values - 32
  # 104 is the start value for start code b, hardcoded right now
  check_sum <- 104 + sum(code_values * 1:length(code_values))
  check_character <- check_sum %% 103
  Binary_code <- sapply(lab_values, function(x, Barcodes) Barcodes$Barcode[x == Barcodes$ASCII], Barcodes = Barcodes)
  ## create quiet zone
  quiet_zone <- paste(c(1:(10)*0),collapse="")
  ## paste together in order: quiet zone, start code binary, binary label, checksum character
  ## stop code, and quiet zone
  binary_label <- paste(quiet_zone, 
                        Barcodes$Barcode[Barcodes$ASCII == start_code],
                        paste(Binary_code, collapse=""),
                        Barcodes$Barcode[Barcodes$ASCII == check_character + 32],
                        "1100011101011",
                        quiet_zone,
                        collapse = "", sep ="")
  ## split binary apart for 
  bar_values <- as.numeric(unlist(strsplit(binary_label, split = "")))
  barcode_bars <- grid::rasterGrob(t(!as.matrix(bar_values)), width = 1, height = 1, interpolate = F)
  return(barcode_bars)
}
ColauttiLab commented 5 years ago

Thanks for the detective work! I can confirm that I was able to re-create the problem using your codes -- a barcode is generated but won't scan for certain codes. I agree it seems to be the checksum character.

I guess one workaround might be to use (e.g. 98 = shift A) if Checksum > 107? I think this would prevent any checksum ASCII characters that are not in the binary table.

thokall commented 5 years ago

Hi I rewrote and use the row number instead of matching in the ASCII column. A limited test suggest that it works, but I will test more extensively and do a pull request if you find it useful. The only change is in the code_make_128 function.

binary_label <- paste(quiet_zone, 
                           Barcodes$Barcode[Barcodes$ASCII == start_code], 
                            paste(Binary_code, collapse = ""), 
                            Barcodes$Barcode[check_character +1], 
                            "1100011101011", 
                            quiet_zone, 
                            collapse = "", 
                            sep = "")

I am not sure I have the necessary understanding to make full use of all three 128 versions, even though this should be the correct way implementing a solution

thokall commented 5 years ago

Since the modulo 103 is never larger than 102 it should be enough to test up to checksum 102 (eg row 1-103)

yihanwu commented 5 years ago

Hi @thokall Would you mind making this a pull request? It's great you were able to troubleshoot this!

@ColauttiLab Let's avoid switching 128 versions. I think code A cannot encode lower case letters.