11from . import encoder
22from .errors import DataTooLongError
3+ from .format .data_capacities import DataCapacities
34from .format .rmqr_versions import rMQRVersions
45
56encoders = [
@@ -47,12 +48,13 @@ def __init__(self):
4748 self .dp = [[[self .INF for n in range (3 )] for mode in range (4 )] for length in range (self .MAX_CHARACTER + 1 )]
4849 self .parents = [[[- 1 for n in range (3 )] for mode in range (4 )] for length in range (self .MAX_CHARACTER + 1 )]
4950
50- def compute (self , data , version ):
51+ def compute (self , data , version , ecc ):
5152 """Computes the optimize segmentation for the given data.
5253
5354 Args:
5455 data (str): The data to encode.
5556 version (str): The version name.
57+ ecc (rmqrcode.ErrorCorrectionLevel): The error correction level.
5658
5759 Returns:
5860 list: The list of segments.
@@ -66,8 +68,11 @@ def compute(self, data, version):
6668
6769 self .qr_version = rMQRVersions [version ]
6870 self ._compute_costs (data )
69- best_index = self ._find_best (data )
70- path = self ._reconstruct_path (best_index )
71+ best = self ._find_best (data )
72+ if best ["cost" ] > DataCapacities [version ]["number_of_data_bits" ][ecc ]:
73+ raise DataTooLongError
74+
75+ path = self ._reconstruct_path (best ["index" ])
7176 segments = self ._compute_segments (path , data )
7277 return segments
7378
@@ -102,44 +107,76 @@ def _compute_costs(self, data):
102107 if not encoders [new_mode ].is_valid_characters (data [n ]):
103108 continue
104109
105- encoder_class = encoders [new_mode ]
106- character_count_indicator_length = self .qr_version ["character_count_indicator_length" ][
107- encoder_class
108- ]
109110 if new_mode == mode :
110- # Keep the mode
111- if encoder_class == encoder .NumericEncoder :
112- new_length = (unfilled_length + 1 ) % 3
113- cost = 4 if unfilled_length == 0 else 3
114- elif encoder_class == encoder .AlphanumericEncoder :
115- new_length = (unfilled_length + 1 ) % 2
116- cost = 6 if unfilled_length == 0 else 5
117- elif encoder_class == encoder .ByteEncoder :
118- new_length = 0
119- cost = 8
120- elif encoder_class == encoder .KanjiEncoder :
121- new_length = 0
122- cost = 13
111+ cost , new_length = self ._compute_new_state_without_mode_changing (
112+ data [n ], new_mode , unfilled_length
113+ )
123114 else :
124- # Change the mode
125- if encoder_class in [encoder .NumericEncoder , encoder .AlphanumericEncoder ]:
126- new_length = 1
127- elif encoder_class in [encoder .ByteEncoder , encoder .KanjiEncoder ]:
128- new_length = 0
129- cost = encoders [new_mode ].length (data [n ], character_count_indicator_length )
115+ cost , new_length = self ._compute_new_state_with_mode_changing (
116+ data [n ], new_mode , unfilled_length
117+ )
130118
131119 if self .dp [n ][mode ][unfilled_length ] + cost < self .dp [n + 1 ][new_mode ][new_length ]:
132120 self .dp [n + 1 ][new_mode ][new_length ] = self .dp [n ][mode ][unfilled_length ] + cost
133121 self .parents [n + 1 ][new_mode ][new_length ] = (n , mode , unfilled_length )
134122
123+ def _compute_new_state_without_mode_changing (self , character , new_mode , unfilled_length ):
124+ """Computes the new state values without mode changing.
125+
126+ Args:
127+ character (str): The current character. Assume this as one length string.
128+ new_mode (int): The state of the new mode.
129+ unfilled_length (int): The state of the current unfilled_length.
130+
131+ Returns:
132+ tuple: (cost, new_length).
133+
134+ """
135+ encoder_class = encoders [new_mode ]
136+ if encoder_class == encoder .NumericEncoder :
137+ new_length = (unfilled_length + 1 ) % 3
138+ cost = 4 if unfilled_length == 0 else 3
139+ elif encoder_class == encoder .AlphanumericEncoder :
140+ new_length = (unfilled_length + 1 ) % 2
141+ cost = 6 if unfilled_length == 0 else 5
142+ elif encoder_class == encoder .ByteEncoder :
143+ new_length = 0
144+ cost = 8 * len (character .encode ("utf-8" ))
145+ elif encoder_class == encoder .KanjiEncoder :
146+ new_length = 0
147+ cost = 13
148+ return (cost , new_length )
149+
150+ def _compute_new_state_with_mode_changing (self , character , new_mode , unfilled_length ):
151+ """Computes the new state values with mode changing.
152+
153+ Args:
154+ character (str): The current character. Assume this as one length string.
155+ new_mode (int): The state of the new mode.
156+ unfilled_length (int): The state of the current unfilled_length.
157+
158+ Returns:
159+ tuple: (cost, new_length).
160+
161+ """
162+ encoder_class = encoders [new_mode ]
163+ character_count_indicator_length = self .qr_version ["character_count_indicator_length" ][encoder_class ]
164+ if encoder_class in [encoder .NumericEncoder , encoder .AlphanumericEncoder ]:
165+ new_length = 1
166+ elif encoder_class in [encoder .ByteEncoder , encoder .KanjiEncoder ]:
167+ new_length = 0
168+ cost = encoder_class .length (character , character_count_indicator_length )
169+ return (cost , new_length )
170+
135171 def _find_best (self , data ):
136172 """Find the index which has the minimum costs.
137173
138174 Args:
139175 data (str): The data to encode.
140176
141177 Returns:
142- tuple: The best index as tuple (n, mode, unfilled_length).
178+ dict: The dict object includes "cost" and "index". The "cost" is the value of minimum cost.
179+ The "index" is the index of the dp table as a tuple (n, mode, unfilled_length).
143180
144181 """
145182 best = self .INF
@@ -149,7 +186,7 @@ def _find_best(self, data):
149186 if self .dp [len (data )][mode ][unfilled_length ] < best :
150187 best = self .dp [len (data )][mode ][unfilled_length ]
151188 best_index = (len (data ), mode , unfilled_length )
152- return best_index
189+ return { "cost" : best , "index" : best_index }
153190
154191 def _reconstruct_path (self , best_index ):
155192 """Reconstructs the path.
0 commit comments