Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.15% covered (success)
96.15%
25 / 26
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
BitWriter
96.15% covered (success)
96.15%
25 / 26
80.00% covered (warning)
80.00%
4 / 5
10
0.00% covered (danger)
0.00%
0 / 1
 writeBits
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
4.02
 writeUint32
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 alignToByte
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getBitPosition
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Core\File;
6
7/**
8 * Writes variable-width bit fields to a binary buffer.
9 *
10 * Inverse of the reader's BitReader — used to encode hint table
11 * entries for linearized PDF output per ISO 32000-2 Annex F.
12 */
13final class BitWriter
14{
15    /** @var list<int> accumulated bytes */
16    private array $bytes = [];
17    private int $currentByte = 0;
18    private int $bitsInCurrent = 0;
19
20    /**
21     * Write an unsigned integer of the given bit width.
22     *
23     * @param int $value Value to write
24     * @param int $count Number of bits to write (0–32), MSB first
25     */
26    public function writeBits(int $value, int $count): void
27    {
28        if ($count === 0) {
29            return;
30        }
31
32        for ($i = $count - 1; $i >= 0; $i--) {
33            $bit = ($value >> $i) & 1;
34            $this->currentByte = ($this->currentByte << 1) | $bit;
35            $this->bitsInCurrent++;
36
37            if ($this->bitsInCurrent === 8) {
38                $this->bytes[] = $this->currentByte;
39                $this->currentByte = 0;
40                $this->bitsInCurrent = 0;
41            }
42        }
43    }
44
45    /**
46     * Write a 32-bit big-endian unsigned integer (for hint table headers).
47     */
48    public function writeUint32(int $value): void
49    {
50        $this->writeBits(($value >> 24) & 0xFF, 8);
51        $this->writeBits(($value >> 16) & 0xFF, 8);
52        $this->writeBits(($value >> 8) & 0xFF, 8);
53        $this->writeBits($value & 0xFF, 8);
54    }
55
56    /** Pad remaining bits in the current byte with zeros and advance. */
57    public function alignToByte(): void
58    {
59        if ($this->bitsInCurrent > 0) {
60            $this->currentByte <<= (8 - $this->bitsInCurrent);
61            $this->bytes[] = $this->currentByte;
62            $this->currentByte = 0;
63            $this->bitsInCurrent = 0;
64        }
65    }
66
67    /** Return the accumulated binary data as a string. */
68    public function getData(): string
69    {
70        // Flush any partial byte
71        $copy = clone $this;
72        $copy->alignToByte();
73        $result = '';
74        foreach ($copy->bytes as $byte) {
75            $result .= chr($byte);
76        }
77        return $result;
78    }
79
80    /** Current position in bits. */
81    public function getBitPosition(): int
82    {
83        return (count($this->bytes) * 8) + $this->bitsInCurrent;
84    }
85}