adding files
commit
0cbc9c06a7
|
@ -0,0 +1,2 @@
|
|||
from . import datamatrix
|
||||
from datamatrix import *
|
|
@ -0,0 +1,653 @@
|
|||
#!/usr/bin/env python2.7
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
Copyright (C) 2009 John Beard john.j.beard@gmail.com
|
||||
|
||||
modified by: Jan Wiśniewski vuko@hackerspace.pl
|
||||
|
||||
######DESCRIPTION######
|
||||
|
||||
This extension renders a DataMatrix 2D barcode, as specified in
|
||||
BS ISO/IEC 16022:2006. Only ECC200 codes are considered, as these are the only
|
||||
ones recommended for an "open" system.
|
||||
|
||||
The size of the DataMatrix is variable between 10x10 to 144x144
|
||||
|
||||
The absolute size of the DataMatrix modules (the little squares) is also
|
||||
variable.
|
||||
|
||||
If more data is given than can be contained in one DataMatrix,
|
||||
more than one DataMatrices will be produced.
|
||||
|
||||
Text is encoded as ASCII (the standard provides for other options, but these are
|
||||
not implemented). Consecutive digits are encoded in a compressed form, halving
|
||||
the space required to store them.
|
||||
|
||||
The basis processing flow is;
|
||||
* Convert input string to codewords (modified ASCII and compressed digits)
|
||||
* Split codewords into blocks of the right size for Reed-Solomon coding
|
||||
* Interleave the blocks if required
|
||||
* Apply Reed-Solomon coding
|
||||
* De-interleave the blocks if required
|
||||
* Place the codewords into the matrix bit by bit
|
||||
* Render the modules in the matrix as squares
|
||||
|
||||
######LICENCE#######
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
######VERSION HISTORY#####
|
||||
Ver. Date Notes
|
||||
|
||||
0.50 2009-10-25 Full functionality, up to 144x144.
|
||||
ASCII and compressed digit encoding only.
|
||||
'''
|
||||
# local library
|
||||
from . import simplestyle
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
|
||||
symbols = {
|
||||
'sq10': (10, 10),
|
||||
'sq12': (12, 12),
|
||||
'sq14': (14, 14),
|
||||
'sq16': (16, 16),
|
||||
'sq18': (18, 18),
|
||||
'sq20': (20, 20),
|
||||
'sq22': (22, 22),
|
||||
'sq24': (24, 24),
|
||||
'sq26': (26, 26),
|
||||
'sq32': (32, 32),
|
||||
'sq36': (36, 36),
|
||||
'sq40': (40, 40),
|
||||
'sq44': (44, 44),
|
||||
'sq48': (48, 48),
|
||||
'sq52': (52, 52),
|
||||
'sq64': (64, 64),
|
||||
'sq72': (72, 72),
|
||||
'sq80': (80, 80),
|
||||
'sq88': (88, 88),
|
||||
'sq96': (96, 96),
|
||||
'sq104': (104, 104),
|
||||
'sq120': (120, 120),
|
||||
'sq132': (132, 132),
|
||||
'sq144': (144, 144),
|
||||
'rect8x18': (8, 18),
|
||||
'rect8x32': (8, 32),
|
||||
'rect12x26': (12, 26),
|
||||
'rect12x36': (12, 36),
|
||||
'rect16x36': (16, 36),
|
||||
'rect16x48': (16, 48),
|
||||
}
|
||||
|
||||
#ENCODING ROUTINES ===================================================
|
||||
# Take an input string and convert it to a sequence (or sequences)
|
||||
# of codewords as specified in ISO/IEC 16022:2006 (section 5.2.3)
|
||||
#=====================================================================
|
||||
|
||||
#create a 2d list corresponding to the 1's and 0s of the DataMatrix
|
||||
def encode(text, size, ascii=True):
|
||||
nrow, ncol = size
|
||||
#retreive the parameters of this size of DataMatrix
|
||||
data_nrow, data_ncol, reg_row, reg_col, nd, nc, inter = get_parameters( nrow, ncol )
|
||||
|
||||
if not ((nrow == 144) and (ncol == 144)): #we have a regular datamatrix
|
||||
size144 = False
|
||||
else: #special handling will be required by get_codewords()
|
||||
size144 = True
|
||||
|
||||
#generate the codewords including padding and ECC
|
||||
codewords = get_codewords( text, nd, nc, inter, size144 , ascii=ascii)
|
||||
|
||||
# break up into separate arrays if more than one DataMatrix is needed
|
||||
module_arrays = []
|
||||
for codeword_stream in codewords: #for each datamatrix
|
||||
bit_array = place_bits(codeword_stream, (data_nrow*reg_row, data_ncol*reg_col)) #place the codewords' bits across the array as modules
|
||||
module_arrays.append(add_finder_pattern( bit_array, data_nrow, data_ncol, reg_row, reg_col )) #add finder patterns around the modules
|
||||
|
||||
return module_arrays
|
||||
|
||||
#return parameters for the selected datamatrix size
|
||||
# data_nrow number of rows in each data region
|
||||
# data_ncol number of cols in each data region
|
||||
# reg_row number of rows of data regions
|
||||
# reg_col number of cols of data regions
|
||||
# nd number of data codewords per reed-solomon block
|
||||
# nc number of ECC codewords per reed-solomon block
|
||||
# inter number of interleaved Reed-Solomon blocks
|
||||
def get_parameters(nrow, ncol):
|
||||
|
||||
#SQUARE SYMBOLS
|
||||
if ( nrow == 10 and ncol == 10 ):
|
||||
return 8, 8, 1, 1, 3, 5, 1
|
||||
elif ( nrow == 12 and ncol == 12 ):
|
||||
return 10, 10, 1, 1, 5, 7, 1
|
||||
elif ( nrow == 14 and ncol == 14 ):
|
||||
return 12, 12, 1, 1, 8, 10, 1
|
||||
elif ( nrow == 16 and ncol == 16 ):
|
||||
return 14, 14, 1, 1, 12, 12, 1
|
||||
elif ( nrow == 18 and ncol == 18 ):
|
||||
return 16, 16, 1, 1, 18, 14, 1
|
||||
elif ( nrow == 20 and ncol == 20 ):
|
||||
return 18, 18, 1, 1, 22, 18, 1
|
||||
elif ( nrow == 22 and ncol == 22 ):
|
||||
return 20, 20, 1, 1, 30, 20, 1
|
||||
elif ( nrow == 24 and ncol == 24 ):
|
||||
return 22, 22, 1, 1, 36, 24, 1
|
||||
elif ( nrow == 26 and ncol == 26 ):
|
||||
return 24, 24, 1, 1, 44, 28, 1
|
||||
elif ( nrow == 32 and ncol == 32 ):
|
||||
return 14, 14, 2, 2, 62, 36, 1
|
||||
elif ( nrow == 36 and ncol == 36 ):
|
||||
return 16, 16, 2, 2, 86, 42, 1
|
||||
elif ( nrow == 40 and ncol == 40):
|
||||
return 18, 18, 2, 2, 114, 48, 1
|
||||
elif ( nrow == 44 and ncol == 44):
|
||||
return 20, 20, 2, 2, 144, 56, 1
|
||||
elif ( nrow == 48 and ncol == 48 ):
|
||||
return 22, 22, 2, 2, 174, 68, 1
|
||||
|
||||
elif ( nrow == 52 and ncol == 52 ):
|
||||
return 24, 24, 2, 2, 102, 42, 2
|
||||
elif ( nrow == 64 and ncol == 64 ):
|
||||
return 14, 14, 4, 4, 140, 56, 2
|
||||
|
||||
elif ( nrow == 72 and ncol == 72 ):
|
||||
return 16, 16, 4, 4, 92, 36, 4
|
||||
elif ( nrow == 80 and ncol == 80 ):
|
||||
return 18, 18, 4, 4, 114, 48, 4
|
||||
elif ( nrow == 88 and ncol == 88 ):
|
||||
return 20, 20, 4, 4, 144, 56, 4
|
||||
elif ( nrow == 96 and ncol == 96 ):
|
||||
return 22, 22, 4, 4, 174, 68, 4
|
||||
|
||||
elif ( nrow == 104 and ncol == 104 ):
|
||||
return 24, 24, 4, 4, 136, 56, 6
|
||||
elif ( nrow == 120 and ncol == 120):
|
||||
return 18, 18, 6, 6, 175, 68, 6
|
||||
|
||||
elif ( nrow == 132 and ncol == 132):
|
||||
return 20, 20, 6, 6, 163, 62, 8
|
||||
|
||||
elif (nrow == 144 and ncol == 144):
|
||||
return 22, 22, 6, 6, 0, 0, 0 #there are two separate sections of the data matrix with
|
||||
#different interleaving and reed-solomon parameters.
|
||||
#this will be handled separately
|
||||
|
||||
#RECTANGULAR SYMBOLS
|
||||
elif ( nrow == 8 and ncol == 18 ):
|
||||
return 6, 16, 1, 1, 5, 7, 1
|
||||
elif ( nrow == 8 and ncol == 32 ):
|
||||
return 6, 14, 1, 2, 10, 11, 1
|
||||
elif ( nrow == 12 and ncol == 26 ):
|
||||
return 10, 24, 1, 1, 16, 14, 1
|
||||
elif ( nrow == 12 and ncol == 36 ):
|
||||
return 10, 16, 1, 2, 22, 18, 1
|
||||
elif ( nrow == 16 and ncol == 36 ):
|
||||
return 14, 16, 1, 2, 32, 24, 1
|
||||
elif ( nrow == 16 and ncol == 48 ):
|
||||
return 14, 22, 1, 2, 49, 28, 1
|
||||
|
||||
#RETURN ERROR
|
||||
else:
|
||||
raise Exception("Invalid DataMatrix dimensions")
|
||||
|
||||
return None
|
||||
|
||||
# CODEWORD STREAM GENERATION =========================================
|
||||
#take the text input and return the codewords,
|
||||
#including the Reed-Solomon error-correcting codes.
|
||||
#=====================================================================
|
||||
|
||||
def get_codewords( text, nd, nc, inter, size144, ascii=True):
|
||||
#convert the data to the codewords
|
||||
if ascii:
|
||||
data = encode_to_ascii( text )
|
||||
else:
|
||||
data = text
|
||||
|
||||
if not size144: #render a "normal" datamatrix
|
||||
data_blocks = partition_data(data, nd*inter) #partition into data blocks of length nd*inter -> inter Reed-Solomon block
|
||||
|
||||
data_blocks = interleave( data_blocks, inter) # interleave consecutive inter blocks if required
|
||||
|
||||
data_blocks = reed_solomon(data_blocks, nd, nc) #generate and append the Reed-Solomon codewords
|
||||
|
||||
data_blocks = combine_interleaved(data_blocks, inter, nd, nc, False) #concatenate Reed-Solomon blocks bound for the same datamatrix
|
||||
|
||||
else: #we have a 144x144 datamatrix
|
||||
data_blocks = partition_data(data, 1558) #partition the data into datamtrix-sized chunks (1558 =156*8 + 155*2 )
|
||||
|
||||
for i in range(len(data_blocks)): #for each datamtrix
|
||||
|
||||
|
||||
inter = 8
|
||||
nd = 156
|
||||
nc = 62
|
||||
block1 = data_blocks[i][0:156*8]
|
||||
block1 = interleave( [block1], inter) # interleave into 8 blocks
|
||||
block1 = reed_solomon(block1, nd, nc) #generate and append the Reed-Solomon codewords
|
||||
|
||||
inter = 2
|
||||
nd = 155
|
||||
nc = 62
|
||||
block2 = data_blocks[i][156*8:]
|
||||
block2 = interleave( [block2], inter) # interleave into 2 blocks
|
||||
block2 = reed_solomon(block2, nd, nc) #generate and append the Reed-Solomon codewords
|
||||
|
||||
blocks = block1
|
||||
blocks.extend(block2)
|
||||
|
||||
blocks = combine_interleaved(blocks, 10, nd, nc, True)
|
||||
|
||||
data_blocks[i] = blocks[0]
|
||||
|
||||
|
||||
return data_blocks
|
||||
|
||||
|
||||
#Takes a codeword stream and splits up into "inter" blocks.
|
||||
#eg interleave( [1,2,3,4,5,6], 2 ) -> [1,3,5], [2,4,6]
|
||||
def interleave( blocks, inter):
|
||||
|
||||
if inter == 1: # if we don't have to interleave, just return the blocks
|
||||
return blocks
|
||||
else:
|
||||
result = []
|
||||
for block in blocks: #for each codeword block in the stream
|
||||
block_length = len(block)//inter #length of each interleaved block
|
||||
inter_blocks = [[0] * block_length for i in range(inter)] #the interleaved blocks
|
||||
|
||||
for i in range(block_length): #for each element in the interleaved blocks
|
||||
for j in range(inter): #for each interleaved block
|
||||
inter_blocks[j][i] = block[ i*inter + j ]
|
||||
|
||||
result.extend(inter_blocks) #add the interleaved blocks to the output
|
||||
|
||||
return result
|
||||
|
||||
#Combine interleaved blocks into the groups for the same datamatrix
|
||||
#
|
||||
#e.g combine_interleaved( [[d1, d3, d5, e1, e3, e5], [d2, d4, d6, e2, e4, e6]], 2, 3, 3 )
|
||||
# --> [[d1, d2, d3, d4, d5, d6, e1, e2, e3, e4, e5, e6]]
|
||||
def combine_interleaved( blocks, inter, nd, nc, size144):
|
||||
if inter == 1: #the blocks aren't interleaved
|
||||
return blocks
|
||||
else:
|
||||
result = []
|
||||
for i in range( len(blocks) // inter ): #for each group of "inter" blocks -> one full datamatrix
|
||||
data_codewords = [] #interleaved data blocks
|
||||
|
||||
if size144:
|
||||
nd_range = 1558 #1558 = 156*8 + 155*2
|
||||
nc_range = 620 #620 = 62*8 + 62*2
|
||||
else:
|
||||
nd_range = nd*inter
|
||||
nc_range = nc*inter
|
||||
|
||||
for j in range(nd_range): #for each codeword in the final list
|
||||
data_codewords.append( blocks[i*inter + j%inter][j//inter] )
|
||||
|
||||
for j in range(nc_range): #for each block, add the ecc codewords
|
||||
data_codewords.append( blocks[i*inter + j%inter][nd + j//inter] )
|
||||
|
||||
result.append(data_codewords)
|
||||
return result
|
||||
|
||||
#checks if an ASCII character is a digit from 0 - 9
|
||||
def is_digit( char ):
|
||||
|
||||
if ord(char) >= 48 and ord(char) <= 57:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def encode_to_ascii( text):
|
||||
|
||||
ascii = []
|
||||
i = 0
|
||||
while i < len(text):
|
||||
#check for double digits
|
||||
if is_digit( text[i] ) and ( i < len(text)-1) and is_digit( text[i+1] ): #if the next char is also a digit
|
||||
|
||||
codeword = int( text[i] + text[i+1] ) + 130
|
||||
ascii.append( codeword )
|
||||
i = i + 2 #move on 2 characters
|
||||
else: #encode as a normal ascii,
|
||||
ascii.append( ord(text[i] ) + 1 ) #codeword is ASCII value + 1 (ISO 16022:2006 5.2.3)
|
||||
i = i + 1 #next character
|
||||
|
||||
return ascii
|
||||
|
||||
|
||||
#partition data into blocks of the appropriate size to suit the
|
||||
#Reed-Solomon block being used.
|
||||
#e.g. partition_data([1,2,3,4,5], 3) -> [[1,2,3],[4,5,PAD]]
|
||||
def partition_data( data , rs_data):
|
||||
|
||||
PAD_VAL = 129 # PAD codeword (ISO 16022:2006 5.2.3)
|
||||
data_blocks = []
|
||||
i = 0
|
||||
while i < len(data):
|
||||
if len(data) >= i+rs_data: #we have a whole block in our data
|
||||
data_blocks.append( data[i:i+rs_data] )
|
||||
i = i + rs_data
|
||||
else: #pad out with the pad codeword
|
||||
data_block = data[i:len(data)] #add any remaining data
|
||||
pad_pos = len(data)
|
||||
padded = False
|
||||
while len(data_block) < rs_data:#and then pad with randomised pad codewords
|
||||
if not padded:
|
||||
data_block.append( PAD_VAL ) #add a normal pad codeword
|
||||
padded = True
|
||||
else:
|
||||
data_block.append( randomise_pad_253( PAD_VAL, pad_pos) )
|
||||
pad_pos = pad_pos + 1
|
||||
data_blocks.append( data_block)
|
||||
break
|
||||
|
||||
return data_blocks
|
||||
|
||||
#Pad character randomisation, to prevent regular patterns appearing
|
||||
#in the data matrix
|
||||
def randomise_pad_253(pad_value, pad_position ):
|
||||
pseudo_random_number = ( ( 149 * pad_position ) % 253 )+ 1
|
||||
randomised = pad_value + pseudo_random_number
|
||||
if ( randomised <= 254 ):
|
||||
return randomised
|
||||
else:
|
||||
return randomised - 254
|
||||
|
||||
# REED-SOLOMON ENCODING ROUTINES =====================================
|
||||
|
||||
# "prod(x,y,log,alog,gf)" returns the product "x" times "y"
|
||||
def prod(x, y, log, alog, gf):
|
||||
|
||||
if ( x==0 or y==0):
|
||||
return 0
|
||||
else:
|
||||
result = alog[ ( log[x] + log[y] ) % (gf - 1) ]
|
||||
return result
|
||||
|
||||
# generate the log & antilog lists:
|
||||
def gen_log_alog(gf, pp):
|
||||
log = [0]*gf
|
||||
alog = [0]*gf
|
||||
|
||||
log[0] = 1-gf
|
||||
alog[0] = 1
|
||||
|
||||
for i in range(1,gf):
|
||||
alog[i] = alog[i-1] * 2
|
||||
|
||||
if (alog[i] >= gf):
|
||||
alog[i] = alog[i] ^ pp
|
||||
|
||||
log[alog[i]] = i
|
||||
|
||||
return log, alog
|
||||
|
||||
# generate the generator polynomial coefficients:
|
||||
def gen_poly_coeffs(nc, log, alog, gf):
|
||||
c = [0] * (nc+1)
|
||||
c[0] = 1
|
||||
|
||||
for i in range(1,nc+1):
|
||||
c[i] = c[i-1]
|
||||
|
||||
j = i-1
|
||||
while j >= 1:
|
||||
c[j] = c[j-1] ^ prod(c[j],alog[i],log,alog,gf)
|
||||
j = j - 1
|
||||
|
||||
c[0] = prod(c[0],alog[i],log,alog,gf)
|
||||
|
||||
return c
|
||||
|
||||
# "ReedSolomon(wd,nd,nc)" takes "nd" data codeword values in wd[]
|
||||
# and adds on "nc" check codewords, all within GF(gf) where "gf" is a
|
||||
# power of 2 and "pp" is the value of its prime modulus polynomial */
|
||||
def reed_solomon(data, nd, nc):
|
||||
#parameters of the polynomial arithmetic
|
||||
gf = 256 #operating on 8-bit codewords -> Galois field = 2^8 = 256
|
||||
pp = 301 #prime modulus polynomial for ECC-200 is 0b100101101 = 301 (ISO 16022:2006 5.7.1)
|
||||
|
||||
log, alog = gen_log_alog(gf,pp)
|
||||
c = gen_poly_coeffs(nc, log, alog, gf)
|
||||
|
||||
for block in data: #for each block of data codewords
|
||||
|
||||
block.extend( [0]*(nc+1) ) #extend to make space for the error codewords
|
||||
|
||||
#generate "nc" checkwords in the list block
|
||||
for i in range(0, nd):
|
||||
k = block[nd] ^ block[i]
|
||||
|
||||
for j in range(0,nc):
|
||||
block[nd+j] = block[nd+j+1] ^ prod(k,c[nc-j-1],log, alog,gf)
|
||||
|
||||
block.pop()
|
||||
|
||||
return data
|
||||
|
||||
#MODULE PLACEMENT ROUTINES===========================================
|
||||
# These routines take a steam of codewords, and place them into the
|
||||
# DataMatrix in accordance with Annex F of BS ISO/IEC 16022:2006
|
||||
|
||||
# bit() returns the bit'th bit of the byte
|
||||
def bit(byte, bit):
|
||||
#the MSB is bit 1, LSB is bit 8
|
||||
return ( byte >> (8-bit) ) %2
|
||||
|
||||
# "module" places a given bit with appropriate wrapping within array
|
||||
def module(array, nrow, ncol, row, col, bit) :
|
||||
if (row < 0) :
|
||||
row = row + nrow
|
||||
col = col + 4 - ((nrow+4)%8)
|
||||
|
||||
if (col < 0):
|
||||
col = col + ncol
|
||||
row = row + 4 - ((ncol+4)%8)
|
||||
|
||||
array[row][col] = bit
|
||||
|
||||
def corner1(array, nrow, ncol, char):
|
||||
module(array, nrow, ncol, nrow-1, 0, bit(char,1));
|
||||
module(array, nrow, ncol, nrow-1, 1, bit(char,2));
|
||||
module(array, nrow, ncol, nrow-1, 2, bit(char,3));
|
||||
module(array, nrow, ncol, 0, ncol-2, bit(char,4));
|
||||
module(array, nrow, ncol, 0, ncol-1, bit(char,5));
|
||||
module(array, nrow, ncol, 1, ncol-1, bit(char,6));
|
||||
module(array, nrow, ncol, 2, ncol-1, bit(char,7));
|
||||
module(array, nrow, ncol, 3, ncol-1, bit(char,8));
|
||||
|
||||
def corner2(array, nrow, ncol, char):
|
||||
module(array, nrow, ncol, nrow-3, 0, bit(char,1));
|
||||
module(array, nrow, ncol, nrow-2, 0, bit(char,2));
|
||||
module(array, nrow, ncol, nrow-1, 0, bit(char,3));
|
||||
module(array, nrow, ncol, 0, ncol-4, bit(char,4));
|
||||
module(array, nrow, ncol, 0, ncol-3, bit(char,5));
|
||||
module(array, nrow, ncol, 0, ncol-2, bit(char,6));
|
||||
module(array, nrow, ncol, 0, ncol-1, bit(char,7));
|
||||
module(array, nrow, ncol, 1, ncol-1, bit(char,8));
|
||||
|
||||
def corner3(array, nrow, ncol, char):
|
||||
module(array, nrow, ncol, nrow-3, 0, bit(char,1));
|
||||
module(array, nrow, ncol, nrow-2, 0, bit(char,2));
|
||||
module(array, nrow, ncol, nrow-1, 0, bit(char,3));
|
||||
module(array, nrow, ncol, 0, ncol-2, bit(char,4));
|
||||
module(array, nrow, ncol, 0, ncol-1, bit(char,5));
|
||||
module(array, nrow, ncol, 1, ncol-1, bit(char,6));
|
||||
module(array, nrow, ncol, 2, ncol-1, bit(char,7));
|
||||
module(array, nrow, ncol, 3, ncol-1, bit(char,8));
|
||||
|
||||
def corner4(array, nrow, ncol, char):
|
||||
module(array, nrow, ncol, nrow-1, 0, bit(char,1));
|
||||
module(array, nrow, ncol, nrow-1, ncol-1, bit(char,2));
|
||||
module(array, nrow, ncol, 0, ncol-3, bit(char,3));
|
||||
module(array, nrow, ncol, 0, ncol-2, bit(char,4));
|
||||
module(array, nrow, ncol, 0, ncol-1, bit(char,5));
|
||||
module(array, nrow, ncol, 1, ncol-3, bit(char,6));
|
||||
module(array, nrow, ncol, 1, ncol-2, bit(char,7));
|
||||
module(array, nrow, ncol, 1, ncol-1, bit(char,8));
|
||||
|
||||
#"utah" places the 8 bits of a utah-shaped symbol character in ECC200
|
||||
def utah(array, nrow, ncol, row, col, char):
|
||||
module(array, nrow, ncol,row-2, col-2, bit(char,1))
|
||||
module(array, nrow, ncol,row-2, col-1, bit(char,2))
|
||||
module(array, nrow, ncol,row-1, col-2, bit(char,3))
|
||||
module(array, nrow, ncol,row-1, col-1, bit(char,4))
|
||||
module(array, nrow, ncol,row-1, col, bit(char,5))
|
||||
module(array, nrow, ncol,row, col-2, bit(char,6))
|
||||
module(array, nrow, ncol,row, col-1, bit(char,7))
|
||||
module(array, nrow, ncol,row, col, bit(char,8))
|
||||
|
||||
#"place_bits" fills an nrow x ncol array with the bits from the
|
||||
# codewords in data.
|
||||
def place_bits(data, size):
|
||||
nrow, ncol = size
|
||||
# First, fill the array[] with invalid entries */
|
||||
INVALID = 2
|
||||
array = [[INVALID] * ncol for i in range(nrow)] #initialise and fill with -1's (invalid value)
|
||||
# Starting in the correct location for character #1, bit 8,...
|
||||
char = 0
|
||||
row = 4
|
||||
col = 0
|
||||
while True:
|
||||
|
||||
#first check for one of the special corner cases, then...
|
||||
if ((row == nrow) and (col == 0)):
|
||||
corner1(array, nrow, ncol, data[char])
|
||||
char = char + 1
|
||||
if ((row == nrow-2) and (col == 0) and (ncol%4)) :
|
||||
corner2(array, nrow, ncol, data[char])
|
||||
char = char + 1
|
||||
if ((row == nrow-2) and (col == 0) and (ncol%8 == 4)):
|
||||
corner3(array, nrow, ncol, data[char])
|
||||
char = char + 1
|
||||
if ((row == nrow+4) and (col == 2) and ((ncol%8) == 0)):
|
||||
corner4(array, nrow, ncol, data[char])
|
||||
char = char + 1
|
||||
|
||||
#sweep upward diagonally, inserting successive characters,...
|
||||
while True:
|
||||
if ((row < nrow) and (col >= 0) and (array[row][col] == INVALID)) :
|
||||
utah(array, nrow, ncol,row,col,data[char])
|
||||
char = char+1
|
||||
row = row - 2
|
||||
col = col + 2
|
||||
|
||||
if not((row >= 0) and (col < ncol)):
|
||||
break
|
||||
|
||||
row = row + 1
|
||||
col = col + 3
|
||||
|
||||
# & then sweep downward diagonally, inserting successive characters,...
|
||||
while True:
|
||||
if ((row >= 0) and (col < ncol) and (array[row][col] == INVALID)) :
|
||||
utah(array, nrow, ncol,row,col,data[char])
|
||||
char = char + 1
|
||||
row = row + 2
|
||||
col = col - 2
|
||||
|
||||
if not((row < nrow) and (col >= 0)):
|
||||
break
|
||||
|
||||
row = row + 3
|
||||
col = col + 1
|
||||
|
||||
#... until the entire array is scanned
|
||||
if not((row < nrow) or (col < ncol)):
|
||||
break
|
||||
|
||||
# Lastly, if the lower righthand corner is untouched, fill in fixed pattern */
|
||||
if (array[nrow-1][ncol-1] == INVALID):
|
||||
array[nrow-1][ncol-2] = 0
|
||||
array[nrow-1][ncol-1] = 1
|
||||
array[nrow-2][ncol-1] = 0
|
||||
array[nrow-2][ncol-2] = 1
|
||||
|
||||
return array #return the array of 1's and 0's
|
||||
|
||||
|
||||
def add_finder_pattern( array, data_nrow, data_ncol, reg_row, reg_col ):
|
||||
|
||||
#get the total size of the datamatrix
|
||||
nrow = (data_nrow+2) * reg_row
|
||||
ncol = (data_ncol+2) * reg_col
|
||||
|
||||
datamatrix = [[0] * ncol for i in range(nrow)] #initialise and fill with 0's
|
||||
|
||||
for i in range( reg_col ): #for each column of data regions
|
||||
for j in range(nrow):
|
||||
datamatrix[j][i*(data_ncol+2)] = 1 #vertical black bar on left
|
||||
datamatrix[j][i*(data_ncol+2)+data_ncol+1] = (j)%2 # alternating blocks
|
||||
|
||||
for i in range( reg_row): # for each row of data regions
|
||||
for j in range(ncol):
|
||||
datamatrix[i*(data_nrow+2)+data_nrow+1][j] = 1 #horizontal black bar at bottom
|
||||
datamatrix[i*(data_nrow+2)][j] = (j+1)%2 # alternating blocks
|
||||
|
||||
for i in range( data_nrow*reg_row ):
|
||||
for j in range( data_ncol* reg_col ):
|
||||
dest_col = j + 1 + 2*(j//(data_ncol)) #offset by 1, plus two for every addition block
|
||||
dest_row = i + 1 + 2*(i//(data_nrow))
|
||||
|
||||
datamatrix[dest_row][dest_col] = array[i][j] #transfer from the plain bit array
|
||||
|
||||
return datamatrix
|
||||
|
||||
#RENDERING ROUTINES ==================================================
|
||||
# Take the array of 1's and 0's and render as a series of black
|
||||
# squares. A binary 1 is a filled square
|
||||
#=====================================================================
|
||||
|
||||
#SVG element generation routine
|
||||
def draw_SVG_square(w_h, x_y, parent):
|
||||
w, h = w_h
|
||||
x, y = x_y
|
||||
|
||||
style = { 'stroke' : 'none',
|
||||
'stroke-width' : '1',
|
||||
'fill' : '#000000'
|
||||
}
|
||||
|
||||
attribs = {
|
||||
'style' :simplestyle.formatStyle(style),
|
||||
'height' : str(h),
|
||||
'width' : str(w),
|
||||
'x' : str(x),
|
||||
'y' : str(y)
|
||||
}
|
||||
#print('rect, svg: ' + str(attribs))
|
||||
circ = ElementTree.SubElement(parent, 'rect', attribs )
|
||||
|
||||
#turn a 2D array of 1's and 0's into a set of black squares
|
||||
def render_data_matrix( module_arrays, size, spacing, parent):
|
||||
|
||||
for i in range(len(module_arrays)): #for each data matrix
|
||||
|
||||
height = len(module_arrays[i])
|
||||
width = len(module_arrays[i][0] )
|
||||
|
||||
for y in range(height): #loop over all the modules in the datamatrix
|
||||
for x in range(width):
|
||||
|
||||
if module_arrays[i][y][x] == 1: #A binary 1 is a filled square
|
||||
draw_SVG_square((size,size), (x*size + i*spacing,y*size), parent)
|
||||
elif module_arrays[i][y][x] != 0: #we have an invalid bit value
|
||||
print('Invalid bit value, this is a bug!')
|
||||
#inkex.errormsg(_('Invalid bit value, this is a bug!'))
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
|
|
@ -0,0 +1,247 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
simplestyle.py
|
||||
Two simple functions for working with inline css
|
||||
and some color handling on top.
|
||||
|
||||
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
||||
|
||||
modified by: Jan Wiśniewski <vuko@hackerspace.pl>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
svgcolors={
|
||||
'aliceblue':'#f0f8ff',
|
||||
'antiquewhite':'#faebd7',
|
||||
'aqua':'#00ffff',
|
||||
'aquamarine':'#7fffd4',
|
||||
'azure':'#f0ffff',
|
||||
'beige':'#f5f5dc',
|
||||
'bisque':'#ffe4c4',
|
||||
'black':'#000000',
|
||||
'blanchedalmond':'#ffebcd',
|
||||
'blue':'#0000ff',
|
||||
'blueviolet':'#8a2be2',
|
||||
'brown':'#a52a2a',
|
||||
'burlywood':'#deb887',
|
||||
'cadetblue':'#5f9ea0',
|
||||
'chartreuse':'#7fff00',
|
||||
'chocolate':'#d2691e',
|
||||
'coral':'#ff7f50',
|
||||
'cornflowerblue':'#6495ed',
|
||||
'cornsilk':'#fff8dc',
|
||||
'crimson':'#dc143c',
|
||||
'cyan':'#00ffff',
|
||||
'darkblue':'#00008b',
|
||||
'darkcyan':'#008b8b',
|
||||
'darkgoldenrod':'#b8860b',
|
||||
'darkgray':'#a9a9a9',
|
||||
'darkgreen':'#006400',
|
||||
'darkgrey':'#a9a9a9',
|
||||
'darkkhaki':'#bdb76b',
|
||||
'darkmagenta':'#8b008b',
|
||||
'darkolivegreen':'#556b2f',
|
||||
'darkorange':'#ff8c00',
|
||||
'darkorchid':'#9932cc',
|
||||
'darkred':'#8b0000',
|
||||
'darksalmon':'#e9967a',
|
||||
'darkseagreen':'#8fbc8f',
|
||||
'darkslateblue':'#483d8b',
|
||||
'darkslategray':'#2f4f4f',
|
||||
'darkslategrey':'#2f4f4f',
|
||||
'darkturquoise':'#00ced1',
|
||||
'darkviolet':'#9400d3',
|
||||
'deeppink':'#ff1493',
|
||||
'deepskyblue':'#00bfff',
|
||||
'dimgray':'#696969',
|
||||
'dimgrey':'#696969',
|
||||
'dodgerblue':'#1e90ff',
|
||||
'firebrick':'#b22222',
|
||||
'floralwhite':'#fffaf0',
|
||||
'forestgreen':'#228b22',
|
||||
'fuchsia':'#ff00ff',
|
||||
'gainsboro':'#dcdcdc',
|
||||
'ghostwhite':'#f8f8ff',
|
||||
'gold':'#ffd700',
|
||||
'goldenrod':'#daa520',
|
||||
'gray':'#808080',
|
||||
'grey':'#808080',
|
||||
'green':'#008000',
|
||||
'greenyellow':'#adff2f',
|
||||
'honeydew':'#f0fff0',
|
||||
'hotpink':'#ff69b4',
|
||||
'indianred':'#cd5c5c',
|
||||
'indigo':'#4b0082',
|
||||
'ivory':'#fffff0',
|
||||
'khaki':'#f0e68c',
|
||||
'lavender':'#e6e6fa',
|
||||
'lavenderblush':'#fff0f5',
|
||||
'lawngreen':'#7cfc00',
|
||||
'lemonchiffon':'#fffacd',
|
||||
'lightblue':'#add8e6',
|
||||
'lightcoral':'#f08080',
|
||||
'lightcyan':'#e0ffff',
|
||||
'lightgoldenrodyellow':'#fafad2',
|
||||
'lightgray':'#d3d3d3',
|
||||
'lightgreen':'#90ee90',
|
||||
'lightgrey':'#d3d3d3',
|
||||
'lightpink':'#ffb6c1',
|
||||
'lightsalmon':'#ffa07a',
|
||||
'lightseagreen':'#20b2aa',
|
||||
'lightskyblue':'#87cefa',
|
||||
'lightslategray':'#778899',
|
||||
'lightslategrey':'#778899',
|
||||
'lightsteelblue':'#b0c4de',
|
||||
'lightyellow':'#ffffe0',
|
||||
'lime':'#00ff00',
|
||||
'limegreen':'#32cd32',
|
||||
'linen':'#faf0e6',
|
||||
'magenta':'#ff00ff',
|
||||
'maroon':'#800000',
|
||||
'mediumaquamarine':'#66cdaa',
|
||||
'mediumblue':'#0000cd',
|
||||
'mediumorchid':'#ba55d3',
|
||||
'mediumpurple':'#9370db',
|
||||
'mediumseagreen':'#3cb371',
|
||||
'mediumslateblue':'#7b68ee',
|
||||
'mediumspringgreen':'#00fa9a',
|
||||
'mediumturquoise':'#48d1cc',
|
||||
'mediumvioletred':'#c71585',
|
||||
'midnightblue':'#191970',
|
||||
'mintcream':'#f5fffa',
|
||||
'mistyrose':'#ffe4e1',
|
||||
'moccasin':'#ffe4b5',
|
||||
'navajowhite':'#ffdead',
|
||||
'navy':'#000080',
|
||||
'oldlace':'#fdf5e6',
|
||||
'olive':'#808000',
|
||||
'olivedrab':'#6b8e23',
|
||||
'orange':'#ffa500',
|
||||
'orangered':'#ff4500',
|
||||
'orchid':'#da70d6',
|
||||
'palegoldenrod':'#eee8aa',
|
||||
'palegreen':'#98fb98',
|
||||
'paleturquoise':'#afeeee',
|
||||
'palevioletred':'#db7093',
|
||||
'papayawhip':'#ffefd5',
|
||||
'peachpuff':'#ffdab9',
|
||||
'peru':'#cd853f',
|
||||
'pink':'#ffc0cb',
|
||||
'plum':'#dda0dd',
|
||||
'powderblue':'#b0e0e6',
|
||||
'purple':'#800080',
|
||||
'rebeccapurple':'#663399',
|
||||
'red':'#ff0000',
|
||||
'rosybrown':'#bc8f8f',
|
||||
'royalblue':'#4169e1',
|
||||
'saddlebrown':'#8b4513',
|
||||
'salmon':'#fa8072',
|
||||
'sandybrown':'#f4a460',
|
||||
'seagreen':'#2e8b57',
|
||||
'seashell':'#fff5ee',
|
||||
'sienna':'#a0522d',
|
||||
'silver':'#c0c0c0',
|
||||
'skyblue':'#87ceeb',
|
||||
'slateblue':'#6a5acd',
|
||||
'slategray':'#708090',
|
||||
'slategrey':'#708090',
|
||||
'snow':'#fffafa',
|
||||
'springgreen':'#00ff7f',
|
||||
'steelblue':'#4682b4',
|
||||
'tan':'#d2b48c',
|
||||
'teal':'#008080',
|
||||
'thistle':'#d8bfd8',
|
||||
'tomato':'#ff6347',
|
||||
'turquoise':'#40e0d0',
|
||||
'violet':'#ee82ee',
|
||||
'wheat':'#f5deb3',
|
||||
'white':'#ffffff',
|
||||
'whitesmoke':'#f5f5f5',
|
||||
'yellow':'#ffff00',
|
||||
'yellowgreen':'#9acd32'
|
||||
}
|
||||
|
||||
def parseStyle(s):
|
||||
"""Create a dictionary from the value of an inline style attribute"""
|
||||
if s is None:
|
||||
return {}
|
||||
else:
|
||||
return dict([[x.strip() for x in i.split(":")] for i in s.split(";") if len(i.strip())])
|
||||
|
||||
def formatStyle(a):
|
||||
"""Format an inline style attribute from a dictionary"""
|
||||
return ";".join([att+":"+str(val) for att,val in iter(a.items())])
|
||||
|
||||
def isColor(c):
|
||||
"""Determine if its a color we can use. If not, leave it unchanged."""
|
||||
if c.startswith('#') and (len(c)==4 or len(c)==7):
|
||||
return True
|
||||
if c.lower() in svgcolors.keys():
|
||||
return True
|
||||
#might be "none" or some undefined color constant or rgb()
|
||||
#however, rgb() shouldnt occur at this point
|
||||
return False
|
||||
|
||||
def parseColor(c):
|
||||
"""Creates a rgb int array"""
|
||||
tmp = svgcolors.get(c.lower())
|
||||
if tmp is not None:
|
||||
c = tmp
|
||||
elif c.startswith('#') and len(c)==4:
|
||||
c='#'+c[1:2]+c[1:2]+c[2:3]+c[2:3]+c[3:]+c[3:]
|
||||
elif c.startswith('rgb('):
|
||||
# remove the rgb(...) stuff
|
||||
tmp = c.strip()[4:-1]
|
||||
numbers = [number.strip() for number in tmp.split(',')]
|
||||
converted_numbers = []
|
||||
if len(numbers) == 3:
|
||||
for num in numbers:
|
||||
if num.endswith(r'%'):
|
||||
converted_numbers.append(int(float(num[0:-1])*255/100))
|
||||
else:
|
||||
converted_numbers.append(int(num))
|
||||
return tuple(converted_numbers)
|
||||
else:
|
||||
return (0,0,0)
|
||||
try:
|
||||
r=int(c[1:3],16)
|
||||
g=int(c[3:5],16)
|
||||
b=int(c[5:],16)
|
||||
except:
|
||||
# unknown color ...
|
||||
# Return a default color. Maybe not the best thing to do but probably
|
||||
# better than raising an exception.
|
||||
return(0,0,0)
|
||||
return (r,g,b)
|
||||
|
||||
def formatColoria(a):
|
||||
"""int array to #rrggbb"""
|
||||
return '#%02x%02x%02x' % (a[0],a[1],a[2])
|
||||
|
||||
def formatColorfa(a):
|
||||
"""float array to #rrggbb"""
|
||||
return '#%02x%02x%02x' % (int(round(a[0]*255)),int(round(a[1]*255)),int(round(a[2]*255)))
|
||||
|
||||
def formatColor3i(r,g,b):
|
||||
"""3 ints to #rrggbb"""
|
||||
return '#%02x%02x%02x' % (r,g,b)
|
||||
|
||||
def formatColor3f(r,g,b):
|
||||
"""3 floats to #rrggbb"""
|
||||
return '#%02x%02x%02x' % (int(round(r*255)),int(round(g*255)),int(round(b*255)))
|
||||
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
|
|
@ -0,0 +1,49 @@
|
|||
import labelgen
|
||||
import argparse
|
||||
import yaml
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"labels", nargs="?", default=None, help="file with label definitions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--outline", action="store_true", help="add outline rectangle to each label"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip", type=int, default=0, help="number of label positions to skip"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--uuid", action="store_true", help="fill empty space with DataMatrix UUID's"
|
||||
)
|
||||
parser.add_argument("--empty", action="store_true", help="add empty labels")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.labels is not None:
|
||||
with open(args.labels) as f:
|
||||
parts = yaml.safe_load(f)
|
||||
else:
|
||||
parts = []
|
||||
|
||||
l = labelgen.Labelgen(
|
||||
y_offset=0.3, x_offset=-1.0, outline=args.outline, add_uuid=args.uuid
|
||||
)
|
||||
|
||||
for field in range(1 + args.skip, 25):
|
||||
try:
|
||||
part = parts[field]
|
||||
l.set_label(field, labelgen.Label.from_dict(part))
|
||||
except IndexError:
|
||||
if args.empty:
|
||||
l.set_label(field, labelgen.Label.empty())
|
||||
else:
|
||||
pass
|
||||
except TypeError:
|
||||
logging.exception(f"label generation failed for {field} {part}")
|
||||
if args.empty:
|
||||
l.set_label(field, labelgen.Label.empty())
|
||||
|
||||
l.gen_svg("out/montage.svg")
|
||||
subprocess.call("inkscape -f out/montage.svg -A out/montage.pdf".split(" "))
|
|
@ -0,0 +1,182 @@
|
|||
import xml.etree.ElementTree as ET
|
||||
import xml.dom.minidom as minidom
|
||||
from datamatrix import datamatrix
|
||||
import uuid_gen
|
||||
import random
|
||||
import math
|
||||
|
||||
|
||||
class Label:
|
||||
def __init__(self, name, category, others, url):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.category = category
|
||||
self.others = others
|
||||
|
||||
def get_svg_element(self):
|
||||
e = ET.Element("g")
|
||||
|
||||
fr = ET.SubElement(e, "flowRoot")
|
||||
fr.set("style", "font-family:Sans;text-anchor:middle;font-size:1")
|
||||
rect = ET.SubElement(ET.SubElement(fr, "flowRegion"), "rect")
|
||||
rect.set("height", "23")
|
||||
rect.set("width", "30")
|
||||
rect.set("x", "2.5")
|
||||
rect.set("y", "2.5")
|
||||
|
||||
fp = ET.SubElement(fr, "flowPara")
|
||||
fp.set("style", "text-align:left;font-size:2.5")
|
||||
fp.text = self.category
|
||||
fp = ET.SubElement(fr, "flowPara")
|
||||
fp.set("style", "line-height:110%;text-align:center;font-size:4")
|
||||
fp.text = self.name
|
||||
fp = ET.SubElement(fr, "flowPara")
|
||||
fp.set("style", "fill:none;line-height:50%;text-align:center;font-size:1.8")
|
||||
fp.text = "a"
|
||||
for par in self.others:
|
||||
fp = ET.SubElement(fr, "flowPara")
|
||||
fp.set("style", "text-align:left;font-size:1.8")
|
||||
fp.text = par
|
||||
|
||||
dmc = datamatrix.encode(self.url, datamatrix.symbols["rect12x36"])
|
||||
if len(dmc) > 1:
|
||||
raise Error("identifier too long")
|
||||
dmg = ET.SubElement(e, "g")
|
||||
dmg.set(
|
||||
"transform",
|
||||
"translate({:f} {:f})".format(
|
||||
35.0 / 2 - (1.0 * 32) / 2, 37.125 - (32 / 36 * 12) - 1.5
|
||||
),
|
||||
)
|
||||
datamatrix.render_data_matrix(dmc, 32 / 36, 0, dmg)
|
||||
e.append(dmg)
|
||||
|
||||
return e
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d):
|
||||
labels = {
|
||||
"description": "desc",
|
||||
"package": "pkg",
|
||||
"value": "val",
|
||||
"manufacturer": "mfr",
|
||||
}
|
||||
others = []
|
||||
for l in labels:
|
||||
if l in d:
|
||||
others.append("{:s}: {:s}".format(labels[l], d[l]))
|
||||
return Label(d["name"], d["type"], others, d["url"])
|
||||
|
||||
@staticmethod
|
||||
def empty():
|
||||
_id = "HTTP://I/{:014d}".format(math.floor((random.random()) * 10 ** 14))
|
||||
return Label("", "", "", _id)
|
||||
|
||||
|
||||
class Labelgen:
|
||||
def __init__(
|
||||
self,
|
||||
width=210,
|
||||
height=297,
|
||||
x_no=6,
|
||||
y_no=8,
|
||||
x_marg=1,
|
||||
y_marg=1,
|
||||
x_offset=0,
|
||||
y_offset=0,
|
||||
outline=False,
|
||||
add_uuid=False,
|
||||
):
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._x_no = x_no
|
||||
self._y_no = y_no
|
||||
self._x_marg = x_marg
|
||||
self._y_marg = y_marg
|
||||
self._x_offset = x_offset
|
||||
self._y_offset = y_offset
|
||||
self._outline = outline
|
||||
self._fields = [None] * ((x_no - 2 * x_marg) * (y_no - 2 * y_marg))
|
||||
self._add_uuid = add_uuid
|
||||
|
||||
def gen_svg(self, filename):
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
montage = ET.ElementTree(element=ET.Element("svg"))
|
||||
|
||||
mr = montage.getroot()
|
||||
mr.set("width", "{:d}mm".format(self._width))
|
||||
mr.set("height", "{:d}mm".format(self._height))
|
||||
xoffset = self._x_offset
|
||||
yoffset = self._y_offset
|
||||
mr.set(
|
||||
"viewBox",
|
||||
"{:f} {:f} {:f} {:f}".format(
|
||||
0 + xoffset, 0 + yoffset, self._width + xoffset, self._height + yoffset
|
||||
),
|
||||
)
|
||||
|
||||
el_x = self._width / self._x_no
|
||||
el_y = self._height / self._y_no
|
||||
|
||||
field_no = 0
|
||||
for yn in range(self._y_no):
|
||||
for xn in range(self._x_no):
|
||||
if (
|
||||
yn >= self._y_marg
|
||||
and yn < self._y_no - self._y_marg
|
||||
and xn >= self._x_marg
|
||||
and xn < self._x_no - self._x_marg
|
||||
):
|
||||
field_no += 1
|
||||
field = self._fields[field_no - 1]
|
||||
if field is not None:
|
||||
el = ET.Element("g")
|
||||
el.set(
|
||||
"transform",
|
||||
"translate({:f} {:f})".format(xn * el_x, yn * el_y),
|
||||
)
|
||||
el.append(field.get_svg_element())
|
||||
mr.append(el)
|
||||
if self._outline:
|
||||
fr = ET.Element("rect")
|
||||
mg = 0.9
|
||||
fr.set("style", "fill:none;stroke:#000000;stroke-width:0.2")
|
||||
fr.set("x", "{:f}".format(mg))
|
||||
fr.set("y", "{:f}".format(mg))
|
||||
fr.set("width", "{:f}".format(35 - 2 * mg))
|
||||
fr.set("height", "{:f}".format(37.125 - 2 * mg))
|
||||
el.append(fr)
|
||||
elif self._add_uuid:
|
||||
for ux in range(3):
|
||||
for uy in range(3):
|
||||
if yn + 1 == self._y_no and uy + 1 == 3:
|
||||
continue
|
||||
if xn + 1 == self._x_no and ux + 1 == 3:
|
||||
continue
|
||||
if yn == 0 and uy == 0:
|
||||
continue
|
||||
if xn == 0 and ux == 0:
|
||||
continue
|
||||
el = ET.Element("g")
|
||||
el.set(
|
||||
"transform",
|
||||
"translate({:f} {:f}) scale(8 8)".format(
|
||||
xn * 35 + 10 * ux + 2.5,
|
||||
yn * 37.125 + 10 * uy + (37.125 - 30) / 2,
|
||||
),
|
||||
)
|
||||
el.append(uuid_gen.get_uuid())
|
||||
mr.append(el)
|
||||
|
||||
xmlstr = minidom.parseString(ET.tostring(mr)).toprettyxml(indent=" ")
|
||||
with open(filename, "w") as f:
|
||||
f.write(xmlstr)
|
||||
|
||||
def set_label(self, field_no, label):
|
||||
self._fields[field_no - 1] = label
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
l = Labelgen()
|
||||
l.set_label(2, Label("name", "category", {"pkg": "carton box"}, "HTTP://I/100"))
|
||||
l.gen_svg("test.svg")
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import uuid
|
||||
from datamatrix import datamatrix
|
||||
import math
|
||||
import xml.etree.ElementTree as ET
|
||||
import xml.dom.minidom as minidom
|
||||
|
||||
|
||||
def encode_base256(b):
|
||||
be = bytes()
|
||||
if len(b) > 249:
|
||||
raise Exception("data longer than 249 not yet supported")
|
||||
f = bytes([len(b),])
|
||||
be += f
|
||||
be += b
|
||||
out = bytes([231])
|
||||
n = 2
|
||||
for byte in be:
|
||||
rn = (149 * n) % 255 + 1
|
||||
out += bytes([(byte + rn) % 256])
|
||||
n += 1
|
||||
return out
|
||||
|
||||
|
||||
# u = uuid.UUID('076c635f-3477-404c-bf05-1d98a947b6f8')
|
||||
|
||||
# creates svg etree object with 1x1 uuid datamatrix (18 x 18 squere)
|
||||
def get_uuid(u=None, anchor_middle=False):
|
||||
if u is not None:
|
||||
u = uuid.UUID(uuid)
|
||||
else:
|
||||
u = uuid.uuid4()
|
||||
|
||||
dmc = datamatrix.encode(
|
||||
list(encode_base256(u.bytes)), datamatrix.symbols["sq18"], ascii=False
|
||||
)
|
||||
dmg = ET.Element("g")
|
||||
if anchor_middle:
|
||||
dmg.set("transform", "translate({:f} {:f})".format(-0.5, -0.5))
|
||||
datamatrix.render_data_matrix(dmc, 1 / 18, 0, dmg)
|
||||
return dmg
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"format", choice={"a4", "stickers", "single"}, help="output format"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
montage = ET.ElementTree(element=ET.Element("svg"))
|
||||
|
||||
if args.format in {"a4", "stickers"}:
|
||||
width = 210
|
||||
height = 297
|
||||
xoffset = 0
|
||||
yoffset = 0
|
||||
|
||||
mr = montage.getroot()
|
||||
mr.set("width", "{:d}mm".format(width))
|
||||
mr.set("height", "{:d}mm".format(height))
|
||||
mr.set(
|
||||
"viewBox",
|
||||
"{:f} {:f} {:f} {:f}".format(
|
||||
0 + xoffset, 0 + yoffset, width + xoffset, height + yoffset
|
||||
),
|
||||
)
|
||||
|
||||
if args.format == "a4":
|
||||
margin = 20
|
||||
yn = 0
|
||||
dist = 8 + 1.7
|
||||
while (yn + 1) * dist + 40 < height:
|
||||
xn = 0
|
||||
while (xn + 1) * dist + 40 < width:
|
||||
g = ET.Element("g")
|
||||
tr = []
|
||||
tr.append("translate({:f} {:f})".format(20 + dist * xn, 20 + dist * yn))
|
||||
tr.append("scale({:f} {:f})".format(8, 8))
|
||||
g.set("transform", " ".join(tr))
|
||||
g.append(get_uuid(anchor_middle=False))
|
||||
mr.append(g)
|
||||
xn += 1
|
||||
yn += 1
|
||||
elif args.format == "stickers":
|
||||
x_no = 3
|
||||
y_no = 8
|
||||
el_x = width / x_no
|
||||
el_y = height / y_no
|
||||
|
||||
field_no = 0
|
||||
for yn in range(y_no):
|
||||
for xn in range(x_no):
|
||||
for ux in range(6):
|
||||
for uy in range(3):
|
||||
if yn + 1 == y_no and uy + 1 == 3:
|
||||
continue
|
||||
if xn + 1 == x_no and ux + 1 == 6:
|
||||
continue
|
||||
if yn == 0 and uy == 0:
|
||||
continue
|
||||
if xn == 0 and ux == 0:
|
||||
continue
|
||||
el = ET.Element("g")
|
||||
el.set(
|
||||
"transform",
|
||||
"translate({:f} {:f}) scale(8 8)".format(
|
||||
xn * el_x + 10 * ux + 5 + 1,
|
||||
yn * el_y + 10 * uy + (el_y - 30) / 2,
|
||||
),
|
||||
)
|
||||
el.append(get_uuid())
|
||||
mr.append(el)
|
||||
elif args.format == "single":
|
||||
width = 10
|
||||
height = 10
|
||||
xoffset = 0
|
||||
yoffset = 0
|
||||
|
||||
mr = montage.getroot()
|
||||
mr.set("width", "{:d}mm".format(width))
|
||||
mr.set("height", "{:d}mm".format(height))
|
||||
mr.set(
|
||||
"viewBox",
|
||||
"{:f} {:f} {:f} {:f}".format(
|
||||
0 + xoffset, 0 + yoffset, width + xoffset, height + yoffset
|
||||
),
|
||||
)
|
||||
|
||||
el = ET.Element("g")
|
||||
el.set("transform", "translate(1 1) scale(8 8)")
|
||||
el.append(get_uuid())
|
||||
mr.append(el)
|
||||
|
||||
xmlstr = minidom.parseString(ET.tostring(mr)).toprettyxml(indent=" ")
|
||||
s = open("{:s}.svg".format(args.format), "w")
|
||||
s.write(xmlstr)
|
||||
s.close()
|
||||
|
||||
import subprocess
|
||||
|
||||
subprocess.call(
|
||||
"inkscape -f {format:s}.svg -A {format:s}.pdf".format(format=args.format).split(
|
||||
" "
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue