1313use chillerlan \QRCode \Common \{BitBuffer , EccLevel , MaskPattern , Mode , Version };
1414use chillerlan \Settings \SettingsContainerInterface ;
1515
16+ use function count ;
1617use function sprintf ;
1718
1819/**
@@ -135,35 +136,48 @@ public function writeMatrix(MaskPattern $maskPattern):QRMatrix{
135136 * @throws \chillerlan\QRCode\Data\QRCodeDataException
136137 */
137138 public function estimateTotalBitLength ():int {
138- $ length = 4 ; // 4 bits for the terminator
139- $ margin = 0 ;
139+ $ length = 0 ;
140140
141141 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
150147 if ($ segment instanceof Hanzi){
151- // Hanzi mode sets an additional 4 bit long subset identifier
152148 $ length += 4 ;
153149 }
150+ }
151+
152+ $ provisionalVersion = null ;
153+
154+ foreach ($ this ->maxBitsForEcc as $ version => $ maxBits ){
155+
156+ if ($ length <= $ maxBits ){
157+ $ provisionalVersion = $ version ;
158+ }
154159
155160 }
156161
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+ }
158168
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 ;
162173 }
163174
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 )])){
165178 return $ length ;
166179 }
180+
167181 }
168182
169183 throw new QRCodeDataException (sprintf ('estimated data exceeds %d bits ' , $ length ));
@@ -199,11 +213,10 @@ public function getMinimumVersion():Version{
199213 * @throws \chillerlan\QRCode\QRCodeException on data overflow
200214 */
201215 private function writeBitBuffer ():void {
202- $ version = $ this ->version ->getVersionNumber ();
203- $ MAX_BITS = $ this ->maxBitsForEcc [$ version ];
216+ $ MAX_BITS = $ this ->eccLevel ->getMaxBitsForVersion ($ this ->version );
204217
205218 foreach ($ this ->dataSegments as $ segment ){
206- $ segment ->write ($ this ->bitBuffer , $ version );
219+ $ segment ->write ($ this ->bitBuffer , $ this -> version -> getVersionNumber () );
207220 }
208221
209222 // overflow, likely caused due to invalid version setting
@@ -215,14 +228,19 @@ private function writeBitBuffer():void{
215228
216229 // add terminator (ISO/IEC 18004:2000 Table 2)
217230 if (($ this ->bitBuffer ->getLength () + 4 ) <= $ MAX_BITS ){
218- $ this ->bitBuffer ->put (0 , 4 );
231+ $ this ->bitBuffer ->put (Mode:: TERMINATOR , 4 );
219232 }
220233
221234 // Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
222235
223236 // if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
224237 // by the addition of padding bits with binary value 0
225238 while (($ this ->bitBuffer ->getLength () % 8 ) !== 0 ){
239+
240+ if ($ this ->bitBuffer ->getLength () === $ MAX_BITS ){
241+ break ;
242+ }
243+
226244 $ this ->bitBuffer ->putBit (false );
227245 }
228246
@@ -231,12 +249,18 @@ private function writeBitBuffer():void{
231249 // Codewords 11101100 and 00010001 alternately.
232250 $ alternate = false ;
233251
234- while ($ this ->bitBuffer ->getLength () <= $ MAX_BITS ){
252+ while (( $ this ->bitBuffer ->getLength () + 8 ) <= $ MAX_BITS ){
235253 $ this ->bitBuffer ->put (($ alternate ) ? 0b00010001 : 0b11101100 , 8 );
236254
237255 $ alternate = !$ alternate ;
238256 }
239257
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+
240264 }
241265
242266}
0 commit comments