Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
94.74% covered (success)
94.74%
18 / 19
CRAP
98.00% covered (success)
98.00%
49 / 50
Buffer
0.00% covered (danger)
0.00%
0 / 1
94.74% covered (success)
94.74%
18 / 19
34
98.00% covered (success)
98.00%
49 / 50
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 getPosition
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getRemaining
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 skip
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 align
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 read
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
5 / 5
 readUInt8
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 readUInt16
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 readInt16
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 readUInt32
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 readInt32
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 readFloat
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 readColor
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 toColor
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 readMatrix
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 readRect
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 readPoints
0.00% covered (danger)
0.00%
0 / 1
6.02
92.31% covered (success)
92.31%
12 / 13
 readRelativeInteger
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
5 / 5
 readString
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
<?php
declare(strict_types=1);
namespace PhpOffice\WMF\Reader\EMFPlus;
use PhpOffice\WMF\Exception\WMFException;
/**
 * Sequential reader of the binary data of EMF+ records & objects (little-endian)
 */
class Buffer
{
    /**
     * @var string
     */
    protected $data;
    /**
     * @var int
     */
    protected $position = 0;
    public function __construct(string $data)
    {
        $this->data = $data;
    }
    public function getPosition(): int
    {
        return $this->position;
    }
    public function getRemaining(): int
    {
        return strlen($this->data) - $this->position;
    }
    public function skip(int $length): self
    {
        $this->read($length);
        return $this;
    }
    /**
     * Skips the padding up to a multiple of 4 bytes (from the start of the data)
     */
    public function align(): self
    {
        $this->position = min(strlen($this->data), (int) (ceil($this->position / 4) * 4));
        return $this;
    }
    public function read(int $length): string
    {
        if ($length < 0 || $this->position + $length > strlen($this->data)) {
            throw new WMFException('Reader : Invalid file : truncated EMF+ data');
        }
        $value = (string) substr($this->data, $this->position, $length);
        $this->position += $length;
        return $value;
    }
    public function readUInt8(): int
    {
        return ord($this->read(1));
    }
    public function readUInt16(): int
    {
        return (int) unpack('v', $this->read(2))[1];
    }
    public function readInt16(): int
    {
        $value = $this->readUInt16();
        return $value >= 0x8000 ? $value - 0x10000 : $value;
    }
    public function readUInt32(): int
    {
        return (int) unpack('V', $this->read(4))[1];
    }
    public function readInt32(): int
    {
        $value = $this->readUInt32();
        return $value >= 0x80000000 ? $value - 0x100000000 : $value;
    }
    public function readFloat(): float
    {
        $value = (float) unpack('g', $this->read(4))[1];
        return is_finite($value) ? $value : 0.0;
    }
    /**
     * Reads an ARGB color : [r, g, b, a]
     *
     * @return array<int>
     */
    public function readColor(): array
    {
        return self::toColor($this->readUInt32());
    }
    /**
     * Converts an ARGB value to a color [r, g, b, a]
     *
     * @return array<int>
     */
    public static function toColor(int $argb): array
    {
        return [($argb >> 16) & 0xFF, ($argb >> 8) & 0xFF, $argb & 0xFF, ($argb >> 24) & 0xFF];
    }
    /**
     * Reads a matrix [m11, m12, m21, m22, dx, dy]
     *
     * @return array<float>
     */
    public function readMatrix(): array
    {
        $matrix = [];
        for ($i = 0; $i < 6; ++$i) {
            $matrix[] = $this->readFloat();
        }
        return $matrix;
    }
    /**
     * Reads a rectangle [x, y, width, height] : 16-bit integers if compressed, else floats
     *
     * @return array<float>
     */
    public function readRect(bool $isCompressed = false): array
    {
        if ($isCompressed) {
            return [$this->readInt16(), $this->readInt16(), $this->readInt16(), $this->readInt16()];
        }
        return [$this->readFloat(), $this->readFloat(), $this->readFloat(), $this->readFloat()];
    }
    /**
     * Reads points : floats, 16-bit integers (compressed) or relative points (EmfPlusPointR)
     *
     * @return array<array<float>>
     */
    public function readPoints(int $count, bool $isCompressed = false, bool $isRelative = false): array
    {
        if ($count < 0 || $count > $this->getRemaining()) {
            throw new WMFException('Reader : Invalid file : truncated EMF+ data');
        }
        $points = [];
        $x = $y = 0;
        for ($i = 0; $i < $count; ++$i) {
            if ($isRelative) {
                // Each coordinate is relative to the previous point
                $x += $this->readRelativeInteger();
                $y += $this->readRelativeInteger();
                $points[] = [$x, $y];
            } elseif ($isCompressed) {
                $points[] = [$this->readInt16(), $this->readInt16()];
            } else {
                $points[] = [$this->readFloat(), $this->readFloat()];
            }
        }
        return $points;
    }
    /**
     * Reads an EmfPlusInteger7 (1 byte) or an EmfPlusInteger15 (2 bytes, big-endian)
     */
    protected function readRelativeInteger(): int
    {
        $first = $this->readUInt8();
        if ($first & 0x80) {
            $value = (($first & 0x7F) << 8) | $this->readUInt8();
            return $value >= 0x4000 ? $value - 0x8000 : $value;
        }
        return $first >= 0x40 ? $first - 0x80 : $first;
    }
    /**
     * Reads a UTF-16LE string of $length characters
     */
    public function readString(int $length): string
    {
        return $this->read(2 * max(0, $length));
    }
}