13
13
use chillerlan \QRCode \Common \{BitBuffer , EccLevel , MaskPattern , Mode , Version };
14
14
use chillerlan \Settings \SettingsContainerInterface ;
15
15
16
+ use function count ;
16
17
use function sprintf ;
17
18
18
19
/**
@@ -135,35 +136,48 @@ public function writeMatrix(MaskPattern $maskPattern):QRMatrix{
135
136
* @throws \chillerlan\QRCode\Data\QRCodeDataException
136
137
*/
137
138
public function estimateTotalBitLength ():int {
138
- $ length = 4 ; // 4 bits for the terminator
139
- $ margin = 0 ;
139
+ $ length = 0 ;
140
140
141
141
foreach ($ this ->dataSegments as $ segment ){
142
- // data length in bits of the current segment +4 bits for each mode descriptor
143
- $ length += ($ segment ->getLengthInBits () + Mode::getLengthBitsForVersion ($ segment ::DATAMODE , 1 ) + 4 );
144
-
145
- if (!$ segment instanceof ECI ){
146
- // mode length bits margin to the next breakpoint
147
- $ margin += ($ segment instanceof Byte) ? 8 : 2 ;
148
- }
149
-
142
+ // data length of the current segment
143
+ $ length += $ segment ->getLengthInBits ();
144
+ // +4 bits for the mode descriptor
145
+ $ length += 4 ;
146
+ // Hanzi mode sets an additional 4 bit long subset identifier
150
147
if ($ segment instanceof Hanzi){
151
- // Hanzi mode sets an additional 4 bit long subset identifier
152
148
$ length += 4 ;
153
149
}
150
+ }
151
+
152
+ $ provisionalVersion = null ;
153
+
154
+ foreach ($ this ->maxBitsForEcc as $ version => $ maxBits ){
155
+
156
+ if ($ length <= $ maxBits ){
157
+ $ provisionalVersion = $ version ;
158
+ }
154
159
155
160
}
156
161
157
- foreach ([9 , 26 , 40 ] as $ breakpoint ){
162
+ if ($ provisionalVersion !== null ){
163
+
164
+ // add character count indicator bits for the provisional version
165
+ foreach ($ this ->dataSegments as $ segment ){
166
+ $ length += Mode::getLengthBitsForVersion ($ segment ::DATAMODE , $ provisionalVersion );
167
+ }
158
168
159
- // length bits for the first breakpoint have already been added
160
- if ($ breakpoint > 9 ){
161
- $ length += $ margin ;
169
+ // it seems that in some cases the estimated total length is not 100% accurate,
170
+ // so we substract 4 bits from the total when not in mixed mode
171
+ if (count ($ this ->dataSegments ) <= 1 ){
172
+ $ length -= 4 ;
162
173
}
163
174
164
- if ($ length < $ this ->maxBitsForEcc [$ breakpoint ]){
175
+ // we've got a match!
176
+ // or let's see if there's a higher version number available
177
+ if ($ length <= $ this ->maxBitsForEcc [$ provisionalVersion ] || isset ($ this ->maxBitsForEcc [($ provisionalVersion + 1 )])){
165
178
return $ length ;
166
179
}
180
+
167
181
}
168
182
169
183
throw new QRCodeDataException (sprintf ('estimated data exceeds %d bits ' , $ length ));
@@ -199,11 +213,10 @@ public function getMinimumVersion():Version{
199
213
* @throws \chillerlan\QRCode\QRCodeException on data overflow
200
214
*/
201
215
private function writeBitBuffer ():void {
202
- $ version = $ this ->version ->getVersionNumber ();
203
- $ MAX_BITS = $ this ->maxBitsForEcc [$ version ];
216
+ $ MAX_BITS = $ this ->eccLevel ->getMaxBitsForVersion ($ this ->version );
204
217
205
218
foreach ($ this ->dataSegments as $ segment ){
206
- $ segment ->write ($ this ->bitBuffer , $ version );
219
+ $ segment ->write ($ this ->bitBuffer , $ this -> version -> getVersionNumber () );
207
220
}
208
221
209
222
// overflow, likely caused due to invalid version setting
@@ -215,14 +228,19 @@ private function writeBitBuffer():void{
215
228
216
229
// add terminator (ISO/IEC 18004:2000 Table 2)
217
230
if (($ this ->bitBuffer ->getLength () + 4 ) <= $ MAX_BITS ){
218
- $ this ->bitBuffer ->put (0 , 4 );
231
+ $ this ->bitBuffer ->put (Mode:: TERMINATOR , 4 );
219
232
}
220
233
221
234
// Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
222
235
223
236
// if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
224
237
// by the addition of padding bits with binary value 0
225
238
while (($ this ->bitBuffer ->getLength () % 8 ) !== 0 ){
239
+
240
+ if ($ this ->bitBuffer ->getLength () === $ MAX_BITS ){
241
+ break ;
242
+ }
243
+
226
244
$ this ->bitBuffer ->putBit (false );
227
245
}
228
246
@@ -231,12 +249,18 @@ private function writeBitBuffer():void{
231
249
// Codewords 11101100 and 00010001 alternately.
232
250
$ alternate = false ;
233
251
234
- while ($ this ->bitBuffer ->getLength () <= $ MAX_BITS ){
252
+ while (( $ this ->bitBuffer ->getLength () + 8 ) <= $ MAX_BITS ){
235
253
$ this ->bitBuffer ->put (($ alternate ) ? 0b00010001 : 0b11101100 , 8 );
236
254
237
255
$ alternate = !$ alternate ;
238
256
}
239
257
258
+ // In certain versions of symbol, it may be necessary to add 3, 4 or 7 Remainder Bits (all zeros)
259
+ // to the end of the message in order exactly to fill the symbol capacity
260
+ while ($ this ->bitBuffer ->getLength () <= $ MAX_BITS ){
261
+ $ this ->bitBuffer ->putBit (false );
262
+ }
263
+
240
264
}
241
265
242
266
}
0 commit comments