12
\$\begingroup\$

Just about every store nowadays uses Universal Product Code (UPC) barcodes to simplify the checking out process. If the name doesn't mean anything to you, you will surely recognize what they look like:

Sample UPC-A barcode

Format

The most common system is UPC-A, which uses 12 digits to represent each specific product. Each digit is encoded into a series of black and white stripes to allow machines to read the code, a length of seven bits. There are a total of 11 bits worth of patterns that indicate the beginning, middle, and end of the barcode. This comes to a total barcode length of 12 × 7 + 11 = 95 bits. (From now on, when binary is used to refer to the color of each bit, 0 is white and 1 is black.)

The beginning and end both have a pattern of 101. The digits are then divided into 2 groups of 6 and encoded as shown below, with a pattern 01010 between the left and right groups. This table lists the pattern for each number. Note that the pattern is different depending on if the digit is on the right or left side (This allows the barcode to be scanned upside down). However, the pattern for the right is the opposite (swap black for white and vice versa) of that of the left.

UPC conversion table

If you can't see the image above, this is each number's binary equivalent.

#   Left    Right
0   0001101 1110010
1   0011001 1100110
2   0010011 1101100
3   0111101 1000010
4   0100011 1011100
5   0110001 1001110
6   0101111 1010000
7   0111011 1000100
8   0110111 1001000
9   0001011 1110100

Example

Say you have the UPC 022000 125033. (Those aren't random numbers. Leave a comment if you figure out their significance.) You start with this boilerplate that is the same in every barcode:

101xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx01010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx101

For the digits, you replace each one with the corresponding encoding for the side (left or right) it's on. If you're still confused, see the image below.

Breakdown of UPC encoding

Here is the output in binary with | pipes separating the parts.

101|0001101|0010011|0010011|0001101|0001101|0001101|01010|1100110|1101100|1001110|1110010|1000010|1000010|101

Challenge

Write a program that outputs the UPC-A barcode for the user input. The image's dimensions should be 95 × 30 pixels, with each "bit" being one pixel wide and 30 pixels tall. Black stripes are in rgb(0, 0, 0) and white stripes are consistently transparent or rgb(255, 255, 255).

Notes

  • Take input from stdin or the command line, or write a function that takes a string or integer (note that input can have leading zeroes, and most languages remove them or convert the number to octal).
  • Output the image in one of the following ways:
    • Save it to a file with a name and format (PNG, PBM, etc.) of your choice.
    • Display it on the screen.
    • Output its file data to stdout.
  • You may not use libraries or builtins that generate barcodes (I'm looking at you, Mathematica), although you may use image or graphics libraries.
  • The last digit of a UPC is usually a check digit, but for these purposes you don't have to worry about it.

Examples

Here are some more examples to test your code with. The binary output is also given for convenience.

Input: 012345678910

Output:

10100011010011001001001101111010100011011000101010101000010001001001000111010011001101110010101

Input: 777777222222

Output:

10101110110111011011101101110110111011011101101010110110011011001101100110110011011001101100101

Scoring

This is code golf, so the shortest submission (in bytes wins). Tiebreaker goes to the earliest post.

\$\endgroup\$
7
  • \$\begingroup\$ Mmm... juicy fruit. \$\endgroup\$
    – Dennis
    Aug 15, 2015 at 3:25
  • \$\begingroup\$ Can the input be taken in as an array? e.g. ["777777","222222"] \$\endgroup\$
    – Downgoat
    Aug 15, 2015 at 5:09
  • \$\begingroup\$ @vihan Hmm, I think that's a bit of a stretch. I'm going to say no. \$\endgroup\$ Aug 15, 2015 at 14:29
  • 2
    \$\begingroup\$ First scanned UPC barcode ever! \$\endgroup\$
    – Dennis
    Aug 15, 2015 at 14:30
  • 1
    \$\begingroup\$ This is brilliant. Barcodes have always fascinated me \$\endgroup\$
    – Beta Decay
    Aug 15, 2015 at 23:59

8 Answers 8

4
\$\begingroup\$

Rev 1 BBC BASIC, 155 ascii chars, tokenised filesize 132 bytes

INPUTn$
FORi=91TO185p=i MOD2j=i MOD47IFj<42j+=i DIV141*42p=(j>41EORASC(MID$("XLd^bFznvh",VAL(MID$(n$,j/7+1,1))+1)))>>(j MOD7)AND1
IFp LINEi*2,60,i*2,0
NEXT

saved a few bytes by incorporating the offset of 43 into the i loop. In order to avoid breaking the MOD2 an additional 47 had to be added for a total of 90.

This moves the barcode further from the origin, as shown, if that is acceptable:

enter image description here

Rev 0 BBC BASIC, 157 ascii chars, tokenised filesize 137 bytes

INPUTn$
FORi=1TO95p=i MOD2j=(i+43)MOD47IFj<42j+=i DIV51*42p=(i>50EORASC(MID$("XLd^bFznvh",VAL(MID$(n$,j/7+1,1))+1)))>>(j MOD7)AND1
IFp LINEi*2,0,i*2,60
NEXT

Download interpreter at http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

The default screen mode is black text on a white background. This differs from original BBC BASC.

Ungolfed version with test printing

Calculation of a data bar depends on IF j<42 and must all be done on one line. In the ungolfed version it is done in three steps. In the golfed version the last two steps are combined into a single huge expression p=...

I had to reverse the order of the bitmaps, because I use >>(j MOD 7) to access the bits, which means I access the least significant bit first. Once this is done, all the left bitmaps are conveniently in the ASCII range.

  INPUTn$
  FOR i=1TO95                            :REM iterate through 95 bars
    p=i MOD2                             :REM calculate colour of format bar 1=black
    j=(i+43)MOD47                        :REM repetition is 42 data bars + 5 format bars. offset and modulo. if j<42 it is a data bar and we must change p.
    
    REM if i DIV 51=1 we are in the second half, so add 42 to j. Find the bitmap for left hand value, from character j/7 of the input.
    REM i>50 evaluates to false=0 true=-1. XOR this with p to invert bitmap for right hand side. Shift and AND with 1.  
    IF j<42 j+=i DIV51*42:p=ASC(MID$("XLd^bFznvh",  VAL(MID$(n$,j/7+1,1))+1  )) :p=(i>50EORp)>>(j MOD7) AND 1

    IF j MOD 7 = 0 PRINT                  :REM format test output
    PRINT ;p;                             :REM print test output
    IF p LINEi*2-2,0,i*2-2,60             :REM if p=1 plot bar. there are 2 logical units for each pixel.
  NEXT

Typical output, ungolfed version, with test output

enter image description here

\$\endgroup\$
3
\$\begingroup\$

CJam, 58 57 bytes

'P1N95S30N[A1r:~"rflB\NPDHt":i2fbf=:R6<::!0AAR6>A1]s30*S*

Prints a Portable BitMap (ASCII) to STDOUT. Try it online.

How it works

'P1N95S30N     e# Push 'P', 1, '\n', 95, ' ', 30 and '\n'.

[              e#
  A1           e#   Push 10 and 1.
  r            e#   Read a token from STDIN.
  :~           e#   Caluate each character ('0' -> 0).
  "rflB\NPDHt" e#   Push that string.
  :i           e#   Cast each character to integer.
               e#   This pushes [114 102 108 66 92 78 80 68 72 116].
  2fb          e#   Convert each integer to base 2.
               e#   This pushes the representations for the right side.
  f=           e#   Select the proper representation of each digit in the input.
  :R           e#   Save the result in R.
  6<           e#   Keep the representations of the first six digits.
  ::!          e#   Negate each binary digit to obtain the "left" representation.
  0AA          e#   Push 0, 10, 10.
  R6>          e#   Push the representations of the last six digits.
  A1           e#   Push 10, 1.
]s             e# Collect in an array and cast to string.

30*            e# Repeat the resulting string 30 times.
S*             e# Join it, using spaces as separators.
\$\endgroup\$
0
2
\$\begingroup\$

JavaScript ES6, 225 bytes

s=>`P1
30 90
`+([...`101${(f=(z,j)=>[...j].map(i=>`000${z[+i].toString(2)}`.slice(-7)).join``)([13,25,19,61,35,49,47,59,55,11],s[0])}01010${f([114,102,108,66,92,78,80,68,72,116],s[1])}101`].join` `+`
`).repeat(30).slice(0,-1)

Could of been shorter with ES7 features but I'm not sure about their support so I'm sticking with ES6. I'm also assuming an input as an array. The output is a PBN file. There is also lots of golfing to do.

If I did anything wrong leave a comment and I'll be sure to fix it

\$\endgroup\$
1
  • \$\begingroup\$ I think you mean PBM file... \$\endgroup\$
    – sergiol
    Dec 1, 2017 at 20:15
2
\$\begingroup\$

Perl, 153 bytes

substr($_=<>,6,0)=A;y/0-9A/=ICmSa_kg;0/;$s.=sprintf("%07b",-48+ord$1^($k++>6?127:0))while/(.)/g;$s=~s/0{7}/01010/;print"P1
95 30
".('101'.$s.'101'.$/)x30

Copy to a file barcode.perl and then run like this:

perl barcode.perl > output.pbm

then input the barcode number.

Explanation:

The bit patterns for the barcode digits are stored in a string and substituted for the input digits using the Perl y/// transliteration operator. Each value in the substitution string has 48 (ASCII '0') added from it, to avoid unprintable characters. Digits in the second half of the barcode are inverses of those in the first half.

The central pattern is set to 0000000 (a pattern that can otherwise never appear, encoded as 'A' and then '0') and then substituted with 01010 rather than handling its different length as a special case when sprinting.

\$\endgroup\$
1
\$\begingroup\$

Octave, 115 bytes

function b(s)
n='rflB\MPDHt'-0;r=dec2bin(n(s-47)',7)'(:)-48;v=[a=[1 0 1] ~r(1:42)' 0 a r(43:84)' a];v(ones(30,1),:)

Multi-line version:

function b(s)
   n='rflB\MPDHt'-0;
   r=dec2bin(n(s-47)',7)'(:)-48;
   v=[a=[1 0 1] ~r(1:42)' 0 a r(43:84)' a];
   v(ones(30,1),:)

n is the ASCII equivalent of the right-side digit codes (they were easier to enter than the left side as they were all displayable characters). After that, a straight decimal-to-binary conversion with some annoying type changes from char to numeric. v builds the final binary string and then we repeat it 30 times and output to console.

Sample output with only 2 of the 30 rows shown for brevity:

s = '777777222222';
ans =

 Columns 1 through 30:

   1   0   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   1   1   0   1
   1   0   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   1   1   0   1
...

 Columns 31 through 60:

   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   0   1   1   1   0   1   1   0   0   1   1   0   1
   1   0   1   1   1   0   1   1   0   1   1   1   0   1   1   0   1   0   1   1   1   0   1   1   0   0   1   1   0   1
...

 Columns 61 through 90:

   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0
   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0   0   1   1   0   1   1   0
...

 Columns 91 through 94:

   0   1   0   1
   0   1   0   1
...

Compressed output:

1010111011011101101110110111011011101101110110101110110011011001101100110110011011001101100101

I had originally intended to display the image, but sending output to the console saved me 9 bytes. You can display the results using imshow, but it displays 1 as white and 0 as black, so you have to invert the data first.

imshow(~v(ones(30,1),:));
\$\endgroup\$
1
\$\begingroup\$

Cobra - 218

do(s='')
    print'P1\n95 30'+('\n'+('101'+(for n in 12get Convert.toString(if((t=139+[2,14,8,50,24,38,36,48,44,0][s[n]to int-48])and n<6,t,~t),2)[-7:]+if(n-5,'','01010')).join('')+'101').toCharArray.join(' ')).repeat(30)
\$\endgroup\$
1
\$\begingroup\$

Javascript ES6, 199 bytes

n=>`P1 95 30 `+(101+(g=(a,...s)=>(``+1e12+n).slice(...s,-6).split``.map(m=>(1e3+a[m].toString(2)).slice(-7)).join``)(a=[13,25,19,61,35,49,47,59,55,11],-12)+`01010`+g(a.map(i=>~i&127))+101).repeat(30)
\$\endgroup\$
2
  • \$\begingroup\$ "the shortest submission (in bytes wins)". You need to count your code in bytes, so if using Unicode, that's 2 bytes per character, I think. \$\endgroup\$
    – mbomb007
    Aug 20, 2015 at 21:39
  • \$\begingroup\$ Bah, yea, I guess my un-unicoded answer is shorter then \$\endgroup\$
    – Dendrobium
    Aug 20, 2015 at 23:15
0
\$\begingroup\$

Python 2, 174 bytes

I know it can be golfed.

The string s is the binary table in the question with the left half of the table as the left half of the string. The values are ANDed by 63 first if in the right half (remove first 1), then shifted by 63 to be printable ASCII.

BUG: Currently trying to fix a bug. The output of the first example is off by one digit of the barcode. If you figure it out, let me know please.

I=raw_input()
s="LXR|bpnzvJcekA[MOCGs"
x="".join(format(ord(s[int(I[i])+10*(i>5)])-63|1+63*(i>5),'07b')for i in range(len(I)))
L=len(x)/2
print"101%s01010%s101"%(x[:L],x[L:])
\$\endgroup\$
1
  • \$\begingroup\$ Or, I made have done the challenge completely wrong. Let me know in that case too. \$\endgroup\$
    – mbomb007
    Aug 20, 2015 at 21:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.