home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / share / extensions / render_barcode_datamatrix.py < prev    next >
Text File  |  2011-07-08  |  25KB  |  655 lines

  1. #!/usr/bin/env python 
  2. # -*- coding: UTF-8 -*-
  3. '''
  4. Copyright (C) 2009 John Beard john.j.beard@gmail.com
  5.  
  6. ######DESCRIPTION######
  7.  
  8. This extension renders a DataMatrix 2D barcode, as specified in
  9. BS ISO/IEC 16022:2006. Only ECC200 codes are considered, as these are the only
  10. ones recommended for an "open" system.
  11.  
  12. The size of the DataMatrix is variable between 10x10 to 144x144
  13.  
  14. The absolute size of the DataMatrix modules (the little squares) is also
  15. variable.
  16.  
  17. If more data is given than can be contained in one DataMatrix,
  18. more than one DataMatrices will be produced.
  19.  
  20. Text is encoded as ASCII (the standard provides for other options, but these are
  21. not implemented). Consecutive digits are encoded in a compressed form, halving 
  22. the space required to store them.
  23.  
  24. The basis processing flow is;
  25.     * Convert input string to codewords (modified ASCII and compressed digits)
  26.     * Split codewords into blocks of the right size for Reed-Solomon coding
  27.     * Interleave the blocks if required
  28.     * Apply Reed-Solomon coding
  29.     * De-interleave the blocks if required
  30.     * Place the codewords into the matrix bit by bit
  31.     * Render the modules in the matrix as squares
  32.  
  33. ######LICENCE#######
  34. This program is free software; you can redistribute it and/or modify
  35. it under the terms of the GNU General Public License as published by
  36. the Free Software Foundation; either version 2 of the License, or
  37. (at your option) any later version.
  38.  
  39. This program is distributed in the hope that it will be useful,
  40. but WITHOUT ANY WARRANTY; without even the implied warranty of
  41. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  42. GNU General Public License for more details.
  43.  
  44. You should have received a copy of the GNU General Public License
  45. along with this program; if not, write to the Free Software
  46. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  47.  
  48. ######VERSION HISTORY#####
  49.     Ver.       Date                       Notes
  50.     
  51.     0.50    2009-10-25  Full functionality, up to 144x144.
  52.                         ASCII and compressed digit encoding only.
  53. '''
  54.  
  55. import inkex, simplestyle
  56.  
  57. import gettext
  58. _ = gettext.gettext
  59.     
  60. #ENCODING ROUTINES ===================================================
  61. #   Take an input string and convert it to a sequence (or sequences)
  62. #   of codewords as specified in ISO/IEC 16022:2006 (section 5.2.3)
  63. #=====================================================================
  64.     
  65. #create a 2d list corresponding to the 1's and 0s of the DataMatrix
  66. def encode(text, (nrow, ncol) ):
  67.     #retreive the parameters of this size of DataMatrix
  68.     data_nrow, data_ncol, reg_row, reg_col, nd, nc, inter = get_parameters( nrow, ncol )
  69.  
  70.     if not ((nrow == 144) and (ncol == 144)):   #we have a regular datamatrix
  71.         size144 = False
  72.     else: #special handling will be required by get_codewords()
  73.         size144 = True
  74.         
  75.     #generate the codewords including padding and ECC
  76.     codewords = get_codewords( text, nd, nc, inter, size144 )
  77.     
  78.     # break up into separate arrays if more than one DataMatrix is needed
  79.     module_arrays = []
  80.     for codeword_stream in codewords:  #for each datamatrix
  81.         bit_array = place_bits(codeword_stream, (data_nrow*reg_row, data_ncol*reg_col)) #place the codewords' bits across the array as modules
  82.         module_arrays.append(add_finder_pattern( bit_array, data_nrow, data_ncol, reg_row, reg_col )) #add finder patterns around the modules
  83.     
  84.     return module_arrays
  85.  
  86. #return parameters for the selected datamatrix size
  87. #   data_nrow   number of rows in each data region
  88. #   data_ncol   number of cols in each data region
  89. #   reg_row     number of rows of data regions
  90. #   reg_col     number of cols of data regions
  91. #   nd          number of data codewords per reed-solomon block
  92. #   nc          number of ECC codewords per reed-solomon block
  93. #   inter       number of interleaved Reed-Solomon blocks
  94. def get_parameters(nrow, ncol):
  95.  
  96.     #SQUARE SYMBOLS
  97.     if ( nrow ==  10 and ncol ==  10 ):
  98.         return  8,  8, 1, 1, 3, 5, 1
  99.     elif ( nrow ==  12 and ncol ==  12 ):
  100.         return 10, 10, 1, 1, 5, 7, 1
  101.     elif ( nrow ==  14 and ncol ==  14 ):
  102.         return 12, 12, 1, 1, 8, 10, 1
  103.     elif ( nrow ==  16 and ncol ==  16 ):
  104.         return 14, 14, 1, 1, 12, 12, 1
  105.     elif ( nrow ==  18 and ncol ==  18 ):
  106.         return 16, 16, 1, 1, 18, 14, 1
  107.     elif ( nrow ==  20 and ncol ==  20 ):
  108.         return 18, 18, 1, 1, 22, 18, 1
  109.     elif ( nrow ==  22 and ncol ==  22 ):
  110.         return 18, 18, 1, 1, 30, 20, 1
  111.     elif ( nrow ==  24 and ncol ==  24 ):
  112.         return 22, 22, 1, 1, 36, 24, 1
  113.     elif ( nrow ==  26 and ncol ==  26 ):
  114.         return 24, 24, 1, 1, 44, 28, 1
  115.     elif ( nrow ==  32 and ncol ==  32 ):
  116.         return 14, 14, 2, 2, 62, 36, 1
  117.     elif ( nrow ==  36 and ncol ==  36 ):
  118.         return 16, 16, 2, 2, 86, 42, 1
  119.     elif ( nrow ==  40 and ncol ==  40):
  120.         return 18, 18, 2, 2, 114, 48, 1
  121.     elif ( nrow ==  44 and ncol ==  44):
  122.         return 20, 20, 2, 2, 144, 56, 1
  123.     elif ( nrow ==  48 and ncol ==  48 ):
  124.         return 22, 22, 2, 2, 174, 68, 1
  125.         
  126.     elif ( nrow ==  52 and ncol ==  52 ):
  127.         return 24, 24, 2, 2, 102, 42, 2
  128.     elif ( nrow ==  64 and ncol ==  64 ):
  129.         return 16, 16, 4, 4, 140, 56, 2
  130.         
  131.     elif ( nrow ==  72 and ncol ==  72 ):
  132.         return 16, 16, 4, 4, 92, 36, 4
  133.     elif ( nrow ==  80 and ncol ==  80 ):
  134.         return 18, 18, 4, 4, 114, 48, 4
  135.     elif ( nrow ==  88 and ncol ==  88 ):
  136.         return 20, 20, 4, 4, 144, 56, 4
  137.     elif ( nrow ==  96 and ncol ==  96 ):
  138.         return 22, 22, 4, 4, 174, 68, 4
  139.         
  140.     elif ( nrow ==  104 and ncol ==  104 ):
  141.         return 24, 24, 4, 4, 136, 56, 6    
  142.     elif ( nrow ==  120 and ncol ==  120):
  143.         return 18, 18, 6, 6, 175, 68, 6
  144.         
  145.     elif ( nrow ==  132 and ncol ==  132):
  146.         return 20, 20, 6, 6, 163, 62, 8
  147.         
  148.     elif (nrow == 144 and ncol == 144):
  149.         return 22, 22, 6, 6, 0, 0, 0        #there are two separate sections of the data matrix with
  150.                                             #different interleaving and reed-solomon parameters.
  151.                                             #this will be handled separately
  152.     
  153.     #RECTANGULAR SYMBOLS
  154.     elif ( nrow ==  8 and ncol ==  18 ):
  155.         return  6, 16, 1, 1, 5, 7, 1
  156.     elif ( nrow ==  8 and ncol ==  32 ):
  157.         return  6, 14, 1, 2, 10, 11, 1
  158.     elif ( nrow ==  12 and ncol ==  26 ):
  159.         return  10, 24, 1, 1, 16, 14, 1
  160.     elif ( nrow ==  12 and ncol ==  36 ):
  161.         return  10, 16, 1, 2, 22, 18, 1
  162.     elif ( nrow ==  16 and ncol ==  36 ):
  163.         return  14, 16, 1, 2, 32, 24, 1
  164.     elif ( nrow ==  16 and ncol ==  48 ):
  165.         return  14, 22, 1, 2, 49, 28, 1
  166.     
  167.     #RETURN ERROR
  168.     else:
  169.         inkex.errormsg(_('Unrecognised DataMatrix size'))
  170.     
  171.     return None
  172.     
  173. # CODEWORD STREAM GENERATION =========================================
  174. #take the text input and return the codewords,
  175. #including the Reed-Solomon error-correcting codes.
  176. #=====================================================================
  177.  
  178. def get_codewords( text, nd, nc, inter, size144 ):
  179.     #convert the data to the codewords
  180.     data = encode_to_ascii( text )
  181.     
  182.     if not size144:    #render a "normal" datamatrix
  183.         data_blocks = partition_data(data, nd*inter)  #partition into data blocks of length nd*inter -> inter Reed-Solomon block
  184.     
  185.         data_blocks = interleave( data_blocks, inter)   # interleave consecutive inter blocks if required
  186.     
  187.         data_blocks = reed_solomon(data_blocks, nd, nc) #generate and append the Reed-Solomon codewords
  188.     
  189.         data_blocks = combine_interleaved(data_blocks, inter, nd, nc, False)  #concatenate Reed-Solomon blocks bound for the same datamatrix
  190.         
  191.     else: #we have a 144x144 datamatrix
  192.         data_blocks = partition_data(data, 1558) #partition the data into datamtrix-sized chunks (1558 =156*8 + 155*2 )
  193.         
  194.         for i in range(len(data_blocks)):  #for each datamtrix
  195.             
  196.             
  197.             inter = 8
  198.             nd = 156
  199.             nc = 62
  200.             block1 = data_blocks[i][0:156*8]
  201.             block1 = interleave( [block1], inter)   # interleave into 8 blocks
  202.             block1 = reed_solomon(block1, nd, nc) #generate and append the Reed-Solomon codewords
  203.             
  204.             inter = 2
  205.             nd = 155
  206.             nc = 62
  207.             block2 = data_blocks[i][156*8:]
  208.             block2 = interleave( [block2], inter)   # interleave into 2 blocks
  209.             block2 = reed_solomon(block2, nd, nc) #generate and append the Reed-Solomon codewords
  210.             
  211.             blocks = block1
  212.             blocks.extend(block2)
  213.                 
  214.             blocks = combine_interleaved(blocks, 10, nd, nc, True)  
  215.             
  216.             data_blocks[i] = blocks[0]
  217.  
  218.         
  219.     return data_blocks
  220.     
  221.  
  222. #Takes a codeword stream and splits up into "inter" blocks.
  223. #eg interleave( [1,2,3,4,5,6], 2 ) -> [1,3,5], [2,4,6]
  224. def interleave( blocks, inter):
  225.  
  226.     if inter == 1:       # if we don't have to interleave, just return the blocks
  227.         return blocks
  228.     else:
  229.         result = []
  230.         for block in blocks:    #for each codeword block in the stream
  231.             block_length = len(block)/inter    #length of each interleaved block
  232.             inter_blocks = [[0] * block_length for i in xrange(inter)]   #the interleaved blocks
  233.             
  234.             for i in range(block_length):   #for each element in the interleaved blocks
  235.                 for j in range(inter):       #for each interleaved block
  236.                     inter_blocks[j][i] = block[ i*inter + j ]
  237.             
  238.             result.extend(inter_blocks) #add the interleaved blocks to the output
  239.         
  240.         return result
  241.         
  242. #Combine interleaved blocks into the groups for the same datamatrix
  243. #
  244. #e.g combine_interleaved( [[d1, d3, d5, e1, e3, e5], [d2, d4, d6, e2, e4, e6]], 2, 3, 3 )
  245. #   --> [[d1, d2, d3, d4, d5, d6, e1, e2, e3, e4, e5, e6]]
  246. def combine_interleaved( blocks, inter, nd, nc, size144):
  247.     if inter == 1:  #the blocks aren't interleaved
  248.         return blocks
  249.     else:
  250.         result = []
  251.         for i in range( len(blocks) / inter ):  #for each group of "inter" blocks -> one full datamatrix
  252.             data_codewords = [] #interleaved data blocks
  253.             
  254.             if size144:
  255.                 nd_range = 1558 #1558 = 156*8 + 155*2
  256.                 nc_range = 620   #620 = 62*8 + 62*2
  257.             else:
  258.                 nd_range = nd*inter
  259.                 nc_range = nc*inter
  260.             
  261.             for j in range(nd_range):  #for each codeword in the final list
  262.                 data_codewords.append( blocks[i*inter + j%inter][j/inter] )
  263.  
  264.             for j in range(nc_range):  #for each block, add the ecc codewords
  265.                 data_codewords.append( blocks[i*inter + j%inter][nd + j/inter] )
  266.  
  267.             result.append(data_codewords)
  268.         return result
  269.     
  270. #checks if an ASCII character is a digit from 0 - 9
  271. def is_digit( char ):
  272.     
  273.     if ord(char) >= 48 and ord(char) <= 57:
  274.         return True
  275.     else:
  276.         return False
  277.     
  278. def encode_to_ascii( text):
  279.  
  280.     ascii = []
  281.     i = 0
  282.     while i < len(text):
  283.         #check for double digits
  284.         if is_digit( text[i] ) and ( i < len(text)-1) and is_digit( text[i+1] ):   #if the next char is also a digit
  285.             
  286.             codeword = int( text[i] + text[i+1] ) + 130
  287.             ascii.append( codeword )
  288.             i = i + 2   #move on 2 characters
  289.         else: #encode as a normal ascii, 
  290.             ascii.append( ord(text[i] ) + 1 ) #codeword is ASCII value + 1 (ISO 16022:2006 5.2.3)
  291.             i = i + 1   #next character
  292.             
  293.     return ascii
  294.  
  295.     
  296. #partition data into blocks of the appropriate size to suit the
  297. #Reed-Solomon block being used.
  298. #e.g. partition_data([1,2,3,4,5], 3) -> [[1,2,3],[4,5,PAD]]
  299. def partition_data( data , rs_data):
  300.  
  301.     PAD_VAL = 129        # PAD codeword (ISO 16022:2006 5.2.3)
  302.     data_blocks = []
  303.     i = 0
  304.     while i < len(data):
  305.         if len(data) >= i+rs_data:  #we have a whole block in our data
  306.             data_blocks.append( data[i:i+rs_data] )
  307.             i = i + rs_data
  308.         else:   #pad out with the pad codeword
  309.             data_block = data[i:len(data)] #add any remaining data
  310.             pad_pos = len(data)
  311.             padded = False
  312.             while len(data_block) < rs_data:#and then pad with randomised pad codewords
  313.                 if not padded:
  314.                     data_block.append( PAD_VAL ) #add a normal pad codeword
  315.                     padded = True
  316.                 else:
  317.                     data_block.append( randomise_pad_253( PAD_VAL, pad_pos) )
  318.                 pad_pos = pad_pos + 1
  319.             data_blocks.append( data_block)
  320.             break
  321.             
  322.     return data_blocks
  323.     
  324. #Pad character randomisation, to prevent regular patterns appearing
  325. #in the data matrix
  326. def randomise_pad_253(pad_value, pad_position ):
  327.     pseudo_random_number = ( ( 149 * pad_position ) % 253 )+ 1 
  328.     randomised = pad_value + pseudo_random_number
  329.     if ( randomised <= 254 ):
  330.         return randomised
  331.     else:
  332.         return randomised - 254
  333.         
  334. # REED-SOLOMON ENCODING ROUTINES =====================================
  335.     
  336. # "prod(x,y,log,alog,gf)" returns the product "x" times "y"
  337. def prod(x, y, log, alog, gf):
  338.     
  339.     if ( x==0 or y==0):
  340.         return 0
  341.     else:
  342.         result = alog[ ( log[x] + log[y] ) % (gf - 1) ]
  343.         return result
  344.  
  345. # generate the log & antilog lists:
  346. def gen_log_alog(gf, pp):
  347.     log = [0]*gf
  348.     alog = [0]*gf
  349.  
  350.     log[0] = 1-gf
  351.     alog[0] = 1
  352.     
  353.     for i in range(1,gf):
  354.         alog[i] = alog[i-1] * 2
  355.         
  356.         if (alog[i] >= gf):
  357.             alog[i] = alog[i] ^ pp
  358.             
  359.         log[alog[i]] = i 
  360.         
  361.     return log, alog
  362.     
  363. # generate the generator polynomial coefficients:
  364. def gen_poly_coeffs(nc, log, alog, gf):
  365.     c = [0] * (nc+1)
  366.     c[0] = 1
  367.  
  368.     for i in range(1,nc+1):
  369.         c[i] = c[i-1] 
  370.         
  371.         j = i-1
  372.         while j >= 1:
  373.             c[j] = c[j-1] ^ prod(c[j],alog[i],log,alog,gf)
  374.             j = j - 1
  375.         
  376.         c[0] = prod(c[0],alog[i],log,alog,gf)
  377.         
  378.     return c
  379.     
  380. # "ReedSolomon(wd,nd,nc)" takes "nd" data codeword values in wd[]
  381. # and adds on "nc" check codewords, all within GF(gf) where "gf" is a
  382. # power of 2 and "pp" is the value of its prime modulus polynomial */ 
  383. def reed_solomon(data, nd, nc):
  384.     #parameters of the polynomial arithmetic
  385.     gf = 256  #operating on 8-bit codewords -> Galois field = 2^8 = 256
  386.     pp = 301  #prime modulus polynomial for ECC-200 is 0b100101101 = 301 (ISO 16022:2006 5.7.1)
  387.     
  388.     log, alog = gen_log_alog(gf,pp)
  389.     c = gen_poly_coeffs(nc, log, alog, gf)
  390.  
  391.     for block in data: #for each block of data codewords
  392.  
  393.         block.extend( [0]*(nc+1) ) #extend to make space for the error codewords
  394.         
  395.         #generate "nc" checkwords in the list block
  396.         for i in range(0, nd):
  397.             k = block[nd] ^ block[i]
  398.             
  399.             for j in range(0,nc):
  400.                 block[nd+j] = block[nd+j+1] ^ prod(k,c[nc-j-1],log, alog,gf)
  401.                 
  402.         block.pop()
  403.  
  404.     return data
  405.     
  406. #MODULE PLACEMENT ROUTINES===========================================
  407. #   These routines take a steam of codewords, and place them into the
  408. #   DataMatrix in accordance with Annex F of BS ISO/IEC 16022:2006
  409.  
  410. # bit() returns the bit'th bit of the byte
  411. def bit(byte, bit):
  412.     #the MSB is bit 1, LSB is bit 8
  413.     return ( byte >> (8-bit) ) %2
  414.     
  415. # "module" places a given bit with appropriate wrapping within array
  416. def module(array, nrow, ncol, row, col, bit) :
  417.     if (row < 0) :
  418.         row = row + nrow
  419.         col = col + 4 - ((nrow+4)%8)
  420.         
  421.     if (col < 0):
  422.         col = col + ncol
  423.         row = row + 4 - ((ncol+4)%8) 
  424.  
  425.     array[row][col] = bit
  426.  
  427. def corner1(array, nrow, ncol, char): 
  428.     module(array, nrow, ncol, nrow-1, 0,      bit(char,1)); 
  429.     module(array, nrow, ncol, nrow-1, 1,      bit(char,2)); 
  430.     module(array, nrow, ncol, nrow-1, 2,      bit(char,3)); 
  431.     module(array, nrow, ncol, 0,      ncol-2, bit(char,4)); 
  432.     module(array, nrow, ncol, 0,      ncol-1, bit(char,5)); 
  433.     module(array, nrow, ncol, 1,      ncol-1, bit(char,6)); 
  434.     module(array, nrow, ncol, 2,      ncol-1, bit(char,7)); 
  435.     module(array, nrow, ncol, 3,      ncol-1, bit(char,8)); 
  436.  
  437. def corner2(array, nrow, ncol, char):
  438.     module(array, nrow, ncol, nrow-3, 0,      bit(char,1)); 
  439.     module(array, nrow, ncol, nrow-2, 0,      bit(char,2)); 
  440.     module(array, nrow, ncol, nrow-1, 0,      bit(char,3)); 
  441.     module(array, nrow, ncol, 0,      ncol-4, bit(char,4)); 
  442.     module(array, nrow, ncol, 0,      ncol-3, bit(char,5)); 
  443.     module(array, nrow, ncol, 0,      ncol-2, bit(char,6)); 
  444.     module(array, nrow, ncol, 0,      ncol-1, bit(char,7)); 
  445.     module(array, nrow, ncol, 1,      ncol-1, bit(char,8)); 
  446.  
  447. def corner3(array, nrow, ncol, char):
  448.     module(array, nrow, ncol, nrow-3, 0,      bit(char,1)); 
  449.     module(array, nrow, ncol, nrow-2, 0,      bit(char,2)); 
  450.     module(array, nrow, ncol, nrow-1, 0,      bit(char,3)); 
  451.     module(array, nrow, ncol, 0,      ncol-2, bit(char,4)); 
  452.     module(array, nrow, ncol, 0,      ncol-1, bit(char,5)); 
  453.     module(array, nrow, ncol, 1,      ncol-1, bit(char,6)); 
  454.     module(array, nrow, ncol, 2,      ncol-1, bit(char,7)); 
  455.     module(array, nrow, ncol, 3,      ncol-1, bit(char,8)); 
  456.  
  457. def corner4(array, nrow, ncol, char):
  458.     module(array, nrow, ncol, nrow-1, 0,      bit(char,1)); 
  459.     module(array, nrow, ncol, nrow-1, ncol-1, bit(char,2)); 
  460.     module(array, nrow, ncol, 0,      ncol-3, bit(char,3)); 
  461.     module(array, nrow, ncol, 0,      ncol-2, bit(char,4)); 
  462.     module(array, nrow, ncol, 0,      ncol-1, bit(char,5)); 
  463.     module(array, nrow, ncol, 1,      ncol-3, bit(char,6)); 
  464.     module(array, nrow, ncol, 1,      ncol-2, bit(char,7)); 
  465.     module(array, nrow, ncol, 1,      ncol-1, bit(char,8));
  466.     
  467. #"utah" places the 8 bits of a utah-shaped symbol character in ECC200
  468. def utah(array, nrow, ncol, row, col, char):
  469.     module(array, nrow, ncol,row-2, col-2, bit(char,1))
  470.     module(array, nrow, ncol,row-2, col-1, bit(char,2))
  471.     module(array, nrow, ncol,row-1, col-2, bit(char,3))
  472.     module(array, nrow, ncol,row-1, col-1, bit(char,4))
  473.     module(array, nrow, ncol,row-1, col,   bit(char,5))
  474.     module(array, nrow, ncol,row,   col-2, bit(char,6))
  475.     module(array, nrow, ncol,row,   col-1, bit(char,7))
  476.     module(array, nrow, ncol,row,   col,   bit(char,8))
  477.  
  478. #"place_bits" fills an nrow x ncol array with the bits from the 
  479. # codewords in data. 
  480. def place_bits(data, (nrow, ncol)): 
  481. # First, fill the array[] with invalid entries */ 
  482.     INVALID = 2
  483.     array = [[INVALID] * ncol for i in xrange(nrow)]   #initialise and fill with -1's (invalid value)
  484. # Starting in the correct location for character #1, bit 8,...
  485.     char = 0
  486.     row = 4
  487.     col = 0
  488.     while True:
  489.     
  490.     #first check for one of the special corner cases, then... 
  491.         if ((row == nrow) and (col == 0)):
  492.             corner1(array, nrow, ncol, data[char])
  493.             char = char + 1
  494.         if ((row == nrow-2) and (col == 0) and (ncol%4)) :
  495.             corner2(array, nrow, ncol, data[char])
  496.             char = char + 1
  497.         if ((row == nrow-2) and (col == 0) and (ncol%8 == 4)):
  498.             corner3(array, nrow, ncol, data[char])
  499.             char = char + 1
  500.         if ((row == nrow+4) and (col == 2) and ((ncol%8) == 0)):
  501.             corner4(array, nrow, ncol, data[char])
  502.             char = char + 1
  503.         
  504.         #sweep upward diagonally, inserting successive characters,...
  505.         while True:
  506.             if ((row < nrow) and (col >= 0) and (array[row][col] == INVALID)) :
  507.                 utah(array, nrow, ncol,row,col,data[char])
  508.                 char = char+1
  509.             row = row - 2
  510.             col = col + 2
  511.             
  512.             if not((row >= 0) and (col < ncol)):
  513.                 break
  514.             
  515.         row = row + 1
  516.         col = col + 3 
  517.                 
  518.         # & then sweep downward diagonally, inserting successive characters,...
  519.         while True:
  520.             if ((row >= 0) and (col < ncol) and (array[row][col] == INVALID)) :
  521.                 utah(array, nrow, ncol,row,col,data[char])
  522.                 char = char + 1
  523.             row = row + 2
  524.             col = col - 2 
  525.         
  526.             if not((row < nrow) and (col >= 0)):
  527.                 break
  528.         
  529.         row = row + 3
  530.         col = col + 1
  531.         
  532.         #... until the entire array is scanned
  533.         if not((row < nrow) or (col < ncol)):
  534.             break
  535.  
  536.     # Lastly, if the lower righthand corner is untouched, fill in fixed pattern */ 
  537.     if (array[nrow-1][ncol-1] == INVALID): 
  538.         array[nrow-1][ncol-2] = 0
  539.         array[nrow-1][ncol-1] = 1
  540.         array[nrow-2][ncol-1] = 0
  541.         array[nrow-2][ncol-2] = 1
  542.  
  543.     return array    #return the array of 1's and 0's
  544.     
  545.     
  546. def add_finder_pattern( array, data_nrow, data_ncol, reg_row, reg_col ):
  547.  
  548.     #get the total size of the datamatrix
  549.     nrow = (data_nrow+2) * reg_row
  550.     ncol = (data_ncol+2) * reg_col
  551.  
  552.     datamatrix = [[0] * ncol for i in xrange(nrow)]   #initialise and fill with 0's
  553.     
  554.     for i in range( reg_col ):    #for each column of data regions
  555.         for j in range(nrow):
  556.             datamatrix[j][i*(data_ncol+2)] = 1  #vertical black bar on left
  557.             datamatrix[j][i*(data_ncol+2)+data_ncol+1] = (j)%2 # alternating blocks
  558.             
  559.     for i in range( reg_row):   # for each row of data regions
  560.         for j in range(ncol):
  561.             datamatrix[i*(data_nrow+2)+data_nrow+1][j] = 1  #horizontal black bar at bottom
  562.             datamatrix[i*(data_nrow+2)][j] = (j+1)%2 # alternating blocks
  563.             
  564.     for i in range( data_nrow*reg_row ):
  565.         for j in range( data_ncol* reg_col ):
  566.             dest_col = j + 1 + 2*(j/(data_ncol)) #offset by 1, plus two for every addition block
  567.             dest_row = i + 1 + 2*(i/(data_nrow))
  568.             
  569.             datamatrix[dest_row][dest_col] = array[i][j]    #transfer from the plain bit array
  570.             
  571.     return datamatrix
  572.     
  573. #RENDERING ROUTINES ==================================================
  574. #   Take the array of 1's and 0's and render as a series of black
  575. #   squares. A binary 1 is a filled square
  576. #=====================================================================
  577.  
  578. #SVG element generation routine
  579. def draw_SVG_square((w,h), (x,y), parent):
  580.  
  581.     style = {   'stroke'        : 'none',
  582.                 'stroke-width'  : '1',
  583.                 'fill'          : '#000000'
  584.             }
  585.                 
  586.     attribs = {
  587.         'style'     :simplestyle.formatStyle(style),
  588.         'height'    : str(h),
  589.         'width'     : str(w),
  590.         'x'         : str(x),
  591.         'y'         : str(y)
  592.             }
  593.     circ = inkex.etree.SubElement(parent, inkex.addNS('rect','svg'), attribs )
  594.     
  595. #turn a 2D array of 1's and 0's into a set of black squares
  596. def render_data_matrix( module_arrays, size, spacing, parent):
  597.     
  598.     for i in range(len(module_arrays)): #for each data matrix
  599.     
  600.         height = len(module_arrays[i])
  601.         width  = len(module_arrays[i][0] )
  602.         
  603.         for y in range(height):     #loop over all the modules in the datamatrix
  604.             for x in range(width):
  605.                 
  606.                 if module_arrays[i][y][x] == 1: #A binary 1 is a filled square
  607.                     draw_SVG_square((size,size), (x*size + i*spacing,y*size), parent)
  608.                 elif module_arrays[i][y][x] != 0: #we have an invalid bit value
  609.                     inkex.errormsg(_('Invalid bit value, this is a bug!'))
  610.     
  611. class DataMatrix(inkex.Effect):
  612.     def __init__(self):
  613.         inkex.Effect.__init__(self)
  614.         
  615.         #PARSE OPTIONS
  616.         self.OptionParser.add_option("--text",
  617.             action="store", type="string",
  618.             dest="TEXT", default='Inkscape')
  619.         self.OptionParser.add_option("--rows",
  620.             action="store", type="int",
  621.             dest="ROWS", default=10)
  622.         self.OptionParser.add_option("--cols",
  623.             action="store", type="int",
  624.             dest="COLS", default=10)
  625.         self.OptionParser.add_option("--size",
  626.             action="store", type="int",
  627.             dest="SIZE", default=4)
  628.             
  629.     def effect(self):
  630.         
  631.         so = self.options
  632.         
  633.         if so.TEXT == '':  #abort if converting blank text
  634.             inkex.errormsg(_('Please enter an input string'))
  635.         else:
  636.         
  637.             #INKSCAPE GROUP TO CONTAIN EVERYTHING
  638.             
  639.             centre = self.view_center   #Put in in the centre of the current view
  640.             grp_transform = 'translate' + str( centre )
  641.             grp_name = 'DataMatrix'
  642.             grp_attribs = {inkex.addNS('label','inkscape'):grp_name,
  643.                            'transform':grp_transform }
  644.             grp = inkex.etree.SubElement(self.current_layer, 'g', grp_attribs)#the group to put everything in
  645.             
  646.             #GENERATE THE DATAMATRIX
  647.             encoded = encode( so.TEXT, (so.ROWS, so.COLS) ) #get the pattern of squares
  648.             render_data_matrix( encoded, so.SIZE, so.COLS*so.SIZE*1.5, grp )    # generate the SVG elements
  649.             
  650. if __name__ == '__main__':
  651.     e = DataMatrix()
  652.     e.affect()
  653.  
  654. # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
  655.