Code

e5365546edd55c2903fc6b0f8d208a498ae0e8a9
[inkscape.git] / share / extensions / render_barcode_datamatrix.py
1 #!/usr/bin/env python 
2 # -*- coding: UTF-8 -*-
3 '''
4 Copyright (C) 2009 John Beard john.j.beard@gmail.com
6 ######DESCRIPTION######
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.
12 The size of the DataMatrix is variable between 10x10 to 144x144
14 The absolute size of the DataMatrix modules (the little squares) is also
15 variable.
17 If more data is given than can be contained in one DataMatrix,
18 more than one DataMatrices will be produced.
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.
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
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.
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.
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
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 '''
55 import inkex, simplestyle
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 )
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
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):
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 #=====================================================================
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]
218         
219     return data_blocks
220     
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):
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
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] )
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] )
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):
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
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):
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
345 # generate the log & antilog lists:
346 def gen_log_alog(gf, pp):
347     log = [0]*gf
348     alog = [0]*gf
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
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)
391     for block in data: #for each block of data codewords
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()
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
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) 
425     array[row][col] = bit
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)); 
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)); 
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)); 
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))
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
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
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 ):
548     #get the total size of the datamatrix
549     nrow = (data_nrow+2) * reg_row
550     ncol = (data_ncol+2) * reg_col
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 #=====================================================================
578 #SVG element generation routine
579 def draw_SVG_square((w,h), (x,y), parent):
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()
654 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99